From 9e1256440af7fcba4b025792b65f1d38f2824631 Mon Sep 17 00:00:00 2001 From: Stephan Raue Date: Tue, 14 Apr 2015 13:39:53 +0200 Subject: [PATCH] projects/RPi*: update RPi support patches, swicth to kernel 4.0 Signed-off-by: Stephan Raue --- projects/RPi/linux/linux.arm.conf | 65 +- projects/RPi/options | 2 +- .../patches/linux/linux-01-RPi_support.patch | 115561 ++++++--------- .../patches/linux/linux-02-RPi_support.patch | 31 - projects/RPi2/linux/linux.arm.conf | 66 +- projects/RPi2/options | 2 +- .../patches/linux/linux-01-RPi_support.patch | 115561 ++++++--------- .../patches/linux/linux-02-RPi_support.patch | 31 - 8 files changed, 93013 insertions(+), 138306 deletions(-) delete mode 100644 projects/RPi/patches/linux/linux-02-RPi_support.patch delete mode 100644 projects/RPi2/patches/linux/linux-02-RPi_support.patch diff --git a/projects/RPi/linux/linux.arm.conf b/projects/RPi/linux/linux.arm.conf index ca76a656a7..d850f4ec9d 100644 --- a/projects/RPi/linux/linux.arm.conf +++ b/projects/RPi/linux/linux.arm.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 3.19.1 Kernel Configuration +# Linux/arm 4.0.0 Kernel Configuration # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -92,9 +92,11 @@ CONFIG_TICK_CPU_ACCOUNTING=y # RCU Subsystem # CONFIG_TINY_RCU=y +CONFIG_SRCU=y # CONFIG_TASKS_RCU is not set # CONFIG_RCU_STALL_COMMON is not set # CONFIG_TREE_RCU_TRACE is not set +CONFIG_RCU_KTHREAD_PRIO=0 CONFIG_BUILD_BIN2C=y CONFIG_IKCONFIG=m CONFIG_IKCONFIG_PROC=y @@ -124,7 +126,6 @@ CONFIG_RD_GZIP=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set -# CONFIG_INIT_FALLBACK is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y @@ -175,6 +176,7 @@ 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 @@ -350,6 +352,7 @@ CONFIG_ARM_ERRATA_364296=y # # Bus support # +# CONFIG_PCI_DOMAINS_GENERIC is not set # CONFIG_PCI_SYSCALL is not set # CONFIG_PCCARD is not set @@ -793,6 +796,7 @@ CONFIG_BT_RFCOMM_TTY=y # CONFIG_BT_BNEP is not set CONFIG_BT_HIDP=m CONFIG_BT_LE=y +# CONFIG_BT_SELFTEST is not set # # Bluetooth device drivers @@ -905,6 +909,7 @@ 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 @@ -918,7 +923,6 @@ CONFIG_BLK_DEV_NBD=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 -# 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 @@ -1290,6 +1294,7 @@ CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set # CONFIG_INPUT_ARIZONA_HAPTICS 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 @@ -1331,6 +1336,7 @@ CONFIG_UNIX98_PTYS=y # 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 # @@ -1359,6 +1365,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y # 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_TTY_PRINTK is not set # CONFIG_HVC_DCC is not set @@ -1397,13 +1404,13 @@ CONFIG_I2C_ALGOBIT=y # # I2C Hardware Bus support # +CONFIG_I2C_BCM2708=y +CONFIG_I2C_BCM2708_BAUDRATE=100000 # # I2C system bus drivers (mostly embedded / system-on-chip) # # CONFIG_I2C_BCM2835 is not set -CONFIG_I2C_BCM2708=y -CONFIG_I2C_BCM2708_BAUDRATE=100000 # CONFIG_I2C_CBUS_GPIO is not set # CONFIG_I2C_DESIGNWARE_PLATFORM is not set CONFIG_I2C_GPIO=y @@ -1568,6 +1575,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_CHARGER_BQ24190 is not set # CONFIG_CHARGER_BQ24735 is not set # CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set # CONFIG_POWER_RESET is not set # CONFIG_POWER_AVS is not set CONFIG_HWMON=y @@ -1768,6 +1776,7 @@ CONFIG_MFD_CORE=y # 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 @@ -1794,6 +1803,7 @@ CONFIG_MFD_CORE=y # CONFIG_MFD_PCF50633 is not set # CONFIG_UCB1400_CORE is not set # CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_RT5033 is not set CONFIG_MFD_RTSX_USB=y # CONFIG_MFD_RC5T583 is not set # CONFIG_MFD_RK808 is not set @@ -2327,6 +2337,11 @@ CONFIG_SND_USB_CAIAQ_INPUT=y CONFIG_SND_USB_6FIRE=m CONFIG_SND_USB_HIFACE=m CONFIG_SND_BCD2000=m +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=m CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y # CONFIG_SND_ATMEL_SOC is not set @@ -2336,6 +2351,7 @@ CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m CONFIG_SND_BCM2708_SOC_RPI_DAC=m +CONFIG_SND_BCM2708_SOC_RPI_PROTO=m CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m # CONFIG_SND_DESIGNWARE_I2S is not set @@ -2352,6 +2368,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m # CONFIG_SND_SOC_FSL_SPDIF is not set # CONFIG_SND_SOC_FSL_ESAI is not set # CONFIG_SND_SOC_IMX_AUDMUX is not set +# CONFIG_SND_SOC_XTFPGA_I2S is not set CONFIG_SND_SOC_I2C_AND_SPI=m # @@ -2390,6 +2407,7 @@ CONFIG_SND_SOC_PCM5102A=m # CONFIG_SND_SOC_SSM2602_SPI is not set # CONFIG_SND_SOC_SSM2602_I2C is not set # CONFIG_SND_SOC_SSM4567 is not set +CONFIG_SND_SOC_STA32X=m # CONFIG_SND_SOC_STA350 is not set # CONFIG_SND_SOC_TAS2552 is not set # CONFIG_SND_SOC_TAS5086 is not set @@ -2405,7 +2423,7 @@ CONFIG_SND_SOC_TAS5713=m # CONFIG_SND_SOC_WM8580 is not set # CONFIG_SND_SOC_WM8711 is not set # CONFIG_SND_SOC_WM8728 is not set -# CONFIG_SND_SOC_WM8731 is not set +CONFIG_SND_SOC_WM8731=m # CONFIG_SND_SOC_WM8737 is not set # CONFIG_SND_SOC_WM8741 is not set # CONFIG_SND_SOC_WM8750 is not set @@ -2439,6 +2457,7 @@ CONFIG_HID_APPLE=y # CONFIG_HID_APPLEIR is not set CONFIG_HID_AUREAL=y CONFIG_HID_BELKIN=y +# CONFIG_HID_BETOP_FF is not set CONFIG_HID_CHERRY=y CONFIG_HID_CHICONY=y # CONFIG_HID_PRODIKEYS is not set @@ -2544,7 +2563,6 @@ CONFIG_USB_MON=m # CONFIG_USB_EHCI_HCD 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_FUSBH200_HCD is not set # CONFIG_USB_FOTG210_HCD is not set @@ -2598,6 +2616,7 @@ CONFIG_USB_UAS=y # CONFIG_USB_MUSB_HDRC is not set # CONFIG_USB_DWC3 is not set # CONFIG_USB_DWC2 is not set +# CONFIG_USB_ISP1760 is not set # # USB port drivers @@ -2709,15 +2728,14 @@ CONFIG_MMC_BLOCK_BOUNCE=y # # MMC/SD/SDIO Host Controller Drivers # +CONFIG_MMC_BCM2835=y +CONFIG_MMC_BCM2835_DMA=y +CONFIG_MMC_BCM2835_PIO_DMA_BARRIER=2 # CONFIG_MMC_ARMMMCI is not set CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y # CONFIG_MMC_SDHCI_OF_ARASAN is not set -# CONFIG_MMC_SDHCI_PXAV3 is not set -# CONFIG_MMC_SDHCI_PXAV2 is not set -CONFIG_MMC_BCM2835=y -CONFIG_MMC_BCM2835_DMA=y -CONFIG_MMC_BCM2835_PIO_DMA_BARRIER=2 +# CONFIG_MMC_SDHCI_F_SDH30 is not set # CONFIG_MMC_SPI is not set # CONFIG_MMC_DW is not set # CONFIG_MMC_VUB300 is not set @@ -2727,6 +2745,7 @@ CONFIG_MMC_REALTEK_USB=m # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=y # # LED drivers @@ -2793,6 +2812,7 @@ CONFIG_RTC_INTF_DEV=y # # I2C RTC drivers # +# CONFIG_RTC_DRV_ABB5ZES3 is not set CONFIG_RTC_DRV_DS1307=m # CONFIG_RTC_DRV_DS1374 is not set # CONFIG_RTC_DRV_DS1672 is not set @@ -2842,6 +2862,7 @@ CONFIG_RTC_DRV_DS1307=m # 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 @@ -2905,7 +2926,6 @@ CONFIG_STAGING=y # CONFIG_RTLLIB is not set CONFIG_R8712U=m # CONFIG_R8188EU is not set -# CONFIG_LINE6_USB is not set CONFIG_VT6656=m # CONFIG_FT1000 is not set @@ -2913,13 +2933,10 @@ CONFIG_VT6656=m # 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=y -# CONFIG_VIDEO_TLG2300 is not set CONFIG_DVB_MN88472=m CONFIG_DVB_MN88473=m -# CONFIG_VIDEO_SAA7191 is not set CONFIG_LIRC_STAGING=y # CONFIG_LIRC_IMON is not set CONFIG_LIRC_RPI=m @@ -2949,7 +2966,9 @@ CONFIG_COMMON_CLK=y # # CONFIG_COMMON_CLK_SI5351 is not set # CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_CLK_QORIQ is not set # CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_CDCE706 is not set # CONFIG_COMMON_CLK_QCOM is not set # @@ -3150,6 +3169,7 @@ CONFIG_NFS_SWAP=y CONFIG_NFS_V4_1=y CONFIG_NFS_V4_2=y CONFIG_PNFS_FILE_LAYOUT=y +CONFIG_PNFS_FLEXFILE_LAYOUT=m CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" CONFIG_NFS_V4_1_MIGRATION=y CONFIG_ROOT_NFS=y @@ -3345,6 +3365,7 @@ CONFIG_TRACING_SUPPORT=y # 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 @@ -3363,7 +3384,6 @@ CONFIG_HAVE_ARCH_KGDB=y # CONFIG_DEBUG_USER is not set # CONFIG_DEBUG_LL is not set CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -# CONFIG_DEBUG_UART_PL01X is not set # CONFIG_DEBUG_UART_8250 is not set # CONFIG_DEBUG_UART_BCM63XX is not set CONFIG_UNCOMPRESS_INCLUDE="mach/uncompress.h" @@ -3378,7 +3398,6 @@ CONFIG_KEYS=y # CONFIG_PERSISTENT_KEYRINGS is not set # CONFIG_BIG_KEYS is not set # 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 @@ -3405,8 +3424,8 @@ 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_GF128MUL=m +CONFIG_CRYPTO_NULL=m CONFIG_CRYPTO_WORKQUEUE=y # CONFIG_CRYPTO_CRYPTD is not set # CONFIG_CRYPTO_MCRYPTD is not set @@ -3417,7 +3436,7 @@ CONFIG_CRYPTO_WORKQUEUE=y # Authenticated Encryption with Associated Data # CONFIG_CRYPTO_CCM=m -# CONFIG_CRYPTO_GCM is not set +CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_SEQIV=m # @@ -3445,7 +3464,7 @@ CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_CRC32 is not set # CONFIG_CRYPTO_CRCT10DIF is not set -# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_GHASH=m CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set @@ -3496,6 +3515,7 @@ CONFIG_CRYPTO_LZO=m # CONFIG_CRYPTO_DRBG_MENU is not set # 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_HW is not set # CONFIG_ASYMMETRIC_KEY_TYPE is not set # CONFIG_BINARY_PRINTF is not set @@ -3505,6 +3525,7 @@ CONFIG_CRYPTO_LZO=m # CONFIG_RAID6_PQ=m CONFIG_BITREVERSE=y +# CONFIG_HAVE_ARCH_BITREVERSE is not set CONFIG_GENERIC_STRNCPY_FROM_USER=y CONFIG_GENERIC_STRNLEN_USER=y CONFIG_GENERIC_NET_UTILS=y diff --git a/projects/RPi/options b/projects/RPi/options index 1704541c7e..1c45910994 100644 --- a/projects/RPi/options +++ b/projects/RPi/options @@ -57,7 +57,7 @@ # Kernel to use. values can be: # default: default mainline kernel - LINUX="3.19" + LINUX="default" # NOOBS supported hex versions NOOBS_HEX="2,3,4,5,6,7,8,9,d,e,f,10,11,12,14,19" diff --git a/projects/RPi/patches/linux/linux-01-RPi_support.patch b/projects/RPi/patches/linux/linux-01-RPi_support.patch index fbe6e260a5..2259be5a25 100644 --- a/projects/RPi/patches/linux/linux-01-RPi_support.patch +++ b/projects/RPi/patches/linux/linux-01-RPi_support.patch @@ -1,14 +1,13 @@ -From 8fa199a9eafe1ffbb0b1dd9c67989ea322f72306 Mon Sep 17 00:00:00 2001 +From 6a451debad7237887314b96e5728277b816eb3bb Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 12 May 2013 12:24:19 +0100 -Subject: [PATCH 001/172] Main bcm2708 linux port +Subject: [PATCH 001/104] Main bcm2708 linux port Signed-off-by: popcornmix --- arch/arm/Kconfig | 17 + arch/arm/Kconfig.debug | 8 + arch/arm/Makefile | 1 + - arch/arm/configs/bcmrpi_defconfig | 464 ++++++++++++++++ arch/arm/kernel/process.c | 10 + arch/arm/mach-bcm2708/Kconfig | 26 + arch/arm/mach-bcm2708/Makefile | 6 + @@ -47,8 +46,7 @@ Signed-off-by: popcornmix arch/arm/tools/mach-types | 1 + drivers/tty/serial/amba-pl011.c | 2 +- include/linux/mmc/host.h | 1 + - 42 files changed, 4715 insertions(+), 5 deletions(-) - create mode 100644 arch/arm/configs/bcmrpi_defconfig + 41 files changed, 4251 insertions(+), 5 deletions(-) create mode 100644 arch/arm/mach-bcm2708/Kconfig create mode 100644 arch/arm/mach-bcm2708/Makefile create mode 100644 arch/arm/mach-bcm2708/Makefile.boot @@ -83,10 +81,10 @@ Signed-off-by: popcornmix create mode 100644 arch/arm/mach-bcm2708/vcio.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 97d07ed..1dc698c 100644 +index cf4c0c9..6876ae8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -367,6 +367,22 @@ config ARCH_AT91 +@@ -369,6 +369,22 @@ config ARCH_AT91 This enables support for systems based on Atmel AT91RM9200, AT91SAM9 and SAMA5 processors. @@ -109,7 +107,7 @@ index 97d07ed..1dc698c 100644 config ARCH_CLPS711X bool "Cirrus Logic CLPS711x/EP721x/EP731x-based" select ARCH_REQUIRE_GPIOLIB -@@ -962,6 +978,7 @@ source "arch/arm/plat-versatile/Kconfig" +@@ -967,6 +983,7 @@ source "arch/arm/plat-versatile/Kconfig" source "arch/arm/mach-vt8500/Kconfig" source "arch/arm/mach-w90x900/Kconfig" @@ -118,10 +116,10 @@ index 97d07ed..1dc698c 100644 source "arch/arm/mach-zynq/Kconfig" diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug -index 5ddd490..e5b2370 100644 +index 970de75..eaadbe8 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug -@@ -1107,6 +1107,14 @@ choice +@@ -1209,6 +1209,14 @@ choice options; the platform specific options are deprecated and will be soon removed. @@ -135,9 +133,9 @@ index 5ddd490..e5b2370 100644 + endchoice - config DEBUG_EXYNOS_UART + config DEBUG_AT91_UART diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index c1785ee..ceb8a4e 100644 +index eb7bb51..27c0f92 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -146,6 +146,7 @@ textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 @@ -148,476 +146,6 @@ index c1785ee..ceb8a4e 100644 machine-$(CONFIG_ARCH_BERLIN) += berlin machine-$(CONFIG_ARCH_CLPS711X) += clps711x machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -new file mode 100644 -index 0000000..8950e53 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -0,0 +1,464 @@ -+# CONFIG_LOCALVERSION_AUTO is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_EMBEDDED=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+CONFIG_PROFILING=y -+CONFIG_OPROFILE=m -+CONFIG_KPROBES=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_PARTITION_ADVANCED=y -+CONFIG_MAC_PARTITION=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2708=y -+CONFIG_AEABI=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" -+CONFIG_KEXEC=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=m -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_XFRM_USER=y -+CONFIG_NET_KEY=m -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+CONFIG_NET_PKTGEN=m -+CONFIG_IRDA=m -+CONFIG_IRLAN=m -+CONFIG_IRCOMM=m -+CONFIG_IRDA_ULTRA=y -+CONFIG_IRDA_CACHE_LAST_LSAP=y -+CONFIG_IRDA_FAST_RR=y -+CONFIG_IRTTY_SIR=m -+CONFIG_KINGSUN_DONGLE=m -+CONFIG_KSDAZZLE_DONGLE=m -+CONFIG_KS959_DONGLE=m -+CONFIG_USB_IRDA=m -+CONFIG_SIGMATEL_FIR=m -+CONFIG_MCS_FIR=m -+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 -+CONFIG_BT_HCIBTUSB=m -+CONFIG_BT_HCIBCM203X=m -+CONFIG_BT_HCIBPA10X=m -+CONFIG_BT_HCIBFUSB=m -+CONFIG_BT_HCIVHCI=m -+CONFIG_BT_MRVL=m -+CONFIG_BT_MRVL_SDIO=m -+CONFIG_BT_ATH3K=m -+CONFIG_CFG80211=m -+CONFIG_MAC80211=m -+CONFIG_MAC80211_MESH=y -+CONFIG_WIMAX=m -+CONFIG_NET_9P=m -+CONFIG_NFC=m -+CONFIG_NFC_PN533=m -+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_NBD=m -+CONFIG_BLK_DEV_RAM=y -+CONFIG_CDROM_PKTCDVD=m -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+CONFIG_BLK_DEV_SD=m -+CONFIG_BLK_DEV_SR=m -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_MD=y -+CONFIG_NETDEVICES=y -+CONFIG_NETCONSOLE=m -+CONFIG_TUN=m -+CONFIG_MDIO_BITBANG=m -+CONFIG_PPP=m -+CONFIG_PPP_BSDCOMP=m -+CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_ASYNC=m -+CONFIG_PPP_SYNC_TTY=m -+CONFIG_SLIP=m -+CONFIG_SLIP_COMPRESSED=y -+CONFIG_USB_CATC=m -+CONFIG_USB_KAWETH=m -+CONFIG_USB_PEGASUS=m -+CONFIG_USB_RTL8150=m -+CONFIG_USB_USBNET=y -+CONFIG_USB_NET_AX8817X=m -+CONFIG_USB_NET_CDCETHER=m -+CONFIG_USB_NET_CDC_EEM=m -+CONFIG_USB_NET_DM9601=m -+CONFIG_USB_NET_SMSC75XX=m -+CONFIG_USB_NET_SMSC95XX=y -+CONFIG_USB_NET_GL620A=m -+CONFIG_USB_NET_NET1080=m -+CONFIG_USB_NET_PLUSB=m -+CONFIG_USB_NET_MCS7830=m -+CONFIG_USB_NET_CDC_SUBSET=m -+CONFIG_USB_ALI_M5632=y -+CONFIG_USB_AN2720=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_INT51X1=m -+CONFIG_USB_IPHETH=m -+CONFIG_USB_SIERRA_NET=m -+CONFIG_USB_VL600=m -+CONFIG_LIBERTAS_THINFIRM=m -+CONFIG_LIBERTAS_THINFIRM_USB=m -+CONFIG_AT76C50X_USB=m -+CONFIG_USB_ZD1201=m -+CONFIG_USB_NET_RNDIS_WLAN=m -+CONFIG_RTL8187=m -+CONFIG_MAC80211_HWSIM=m -+CONFIG_B43=m -+CONFIG_B43LEGACY=m -+CONFIG_HOSTAP=m -+CONFIG_LIBERTAS=m -+CONFIG_LIBERTAS_USB=m -+CONFIG_LIBERTAS_SDIO=m -+CONFIG_P54_COMMON=m -+CONFIG_P54_USB=m -+CONFIG_RT2X00=m -+CONFIG_RT2500USB=m -+CONFIG_RT73USB=m -+CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT53XX=y -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_MISC=y -+CONFIG_INPUT_AD714X=m -+CONFIG_INPUT_ATI_REMOTE2=m -+CONFIG_INPUT_KEYSPAN_REMOTE=m -+CONFIG_INPUT_POWERMATE=m -+CONFIG_INPUT_YEALINK=m -+CONFIG_INPUT_CM109=m -+CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_ADXL34X=m -+CONFIG_INPUT_CMA3000=m -+CONFIG_SERIO=m -+CONFIG_SERIO_RAW=m -+CONFIG_GAMEPORT=m -+CONFIG_GAMEPORT_NS558=m -+CONFIG_GAMEPORT_L4=m -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+# CONFIG_HW_RANDOM is not set -+CONFIG_RAW_DRIVER=y -+# CONFIG_HWMON is not set -+CONFIG_WATCHDOG=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_HID_A4TECH=m -+CONFIG_HID_ACRUX=m -+CONFIG_HID_APPLE=m -+CONFIG_HID_BELKIN=m -+CONFIG_HID_CHERRY=m -+CONFIG_HID_CHICONY=m -+CONFIG_HID_CYPRESS=m -+CONFIG_HID_DRAGONRISE=m -+CONFIG_HID_EMS_FF=m -+CONFIG_HID_ELECOM=m -+CONFIG_HID_EZKEY=m -+CONFIG_HID_HOLTEK=m -+CONFIG_HID_KEYTOUCH=m -+CONFIG_HID_KYE=m -+CONFIG_HID_UCLOGIC=m -+CONFIG_HID_WALTOP=m -+CONFIG_HID_GYRATION=m -+CONFIG_HID_TWINHAN=m -+CONFIG_HID_KENSINGTON=m -+CONFIG_HID_LCPOWER=m -+CONFIG_HID_LOGITECH=m -+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_HID_PETALYNX=m -+CONFIG_HID_PICOLCD=m -+CONFIG_HID_ROCCAT=m -+CONFIG_HID_SAMSUNG=m -+CONFIG_HID_SONY=m -+CONFIG_HID_SPEEDLINK=m -+CONFIG_HID_SUNPLUS=m -+CONFIG_HID_GREENASIA=m -+CONFIG_HID_SMARTJOYPLUS=m -+CONFIG_HID_TOPSEED=m -+CONFIG_HID_THRUSTMASTER=m -+CONFIG_HID_WACOM=m -+CONFIG_HID_WIIMOTE=m -+CONFIG_HID_ZEROPLUS=m -+CONFIG_HID_ZYDACRON=m -+CONFIG_HID_PID=y -+CONFIG_USB_HIDDEV=y -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_MON=m -+CONFIG_USB_STORAGE=y -+CONFIG_USB_STORAGE_REALTEK=m -+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 -+CONFIG_USB_UAS=m -+CONFIG_USB_MDC800=m -+CONFIG_USB_MICROTEK=m -+CONFIG_USB_SERIAL=m -+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_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_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_KLSI=m -+CONFIG_USB_SERIAL_KOBIL_SCT=m -+CONFIG_USB_SERIAL_MCT_U232=m -+CONFIG_USB_SERIAL_MOS7720=m -+CONFIG_USB_SERIAL_MOS7840=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_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_OPTION=m -+CONFIG_USB_SERIAL_OMNINET=m -+CONFIG_USB_SERIAL_OPTICON=m -+CONFIG_USB_SERIAL_SSU100=m -+CONFIG_USB_SERIAL_DEBUG=m -+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=m -+CONFIG_USB_LD=m -+CONFIG_USB_TRANCEVIBRATOR=m -+CONFIG_USB_IOWARRIOR=m -+CONFIG_USB_TEST=m -+CONFIG_USB_ISIGHTFW=m -+CONFIG_USB_YUREX=m -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_UIO=m -+CONFIG_UIO_PDRV_GENIRQ=m -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_REISERFS_FS=m -+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_STATISTICS=y -+CONFIG_XFS_FS=m -+CONFIG_XFS_QUOTA=y -+CONFIG_XFS_POSIX_ACL=y -+CONFIG_XFS_RT=y -+CONFIG_GFS2_FS=m -+CONFIG_OCFS2_FS=m -+CONFIG_BTRFS_FS=m -+CONFIG_BTRFS_FS_POSIX_ACL=y -+CONFIG_NILFS2_FS=m -+CONFIG_FANOTIFY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_ISO9660_FS=m -+CONFIG_JOLIET=y -+CONFIG_ZISOFS=y -+CONFIG_UDF_FS=m -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_NTFS_FS=m -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+CONFIG_SQUASHFS=m -+CONFIG_SQUASHFS_XATTR=y -+CONFIG_SQUASHFS_LZO=y -+CONFIG_SQUASHFS_XZ=y -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_CIFS=m -+CONFIG_CIFS_WEAK_PW_HASH=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_9P_FS=m -+CONFIG_9P_FS_POSIX_ACL=y -+CONFIG_NLS_DEFAULT="utf8" -+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=y -+CONFIG_NLS_ISO8859_1=m -+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_UTF8=m -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_INFO=y -+CONFIG_DEBUG_STACK_USAGE=y -+CONFIG_DEBUG_MEMORY_INIT=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+CONFIG_LATENCYTOP=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+CONFIG_KDB_KEYBOARD=y -+CONFIG_STRICT_DEVMEM=y -+CONFIG_CRYPTO_AUTHENC=m -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_XCBC=m -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_SHA512=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+CONFIG_CRYPTO_DEFLATE=m -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index fdfa3a7..f5ffaacb 100644 --- a/arch/arm/kernel/process.c @@ -3715,7 +3243,7 @@ index 0000000..4a4a338 +#endif /* VC_MEM_H */ diff --git a/arch/arm/mach-bcm2708/include/mach/vcio.h b/arch/arm/mach-bcm2708/include/mach/vcio.h new file mode 100644 -index 0000000..8e11d67 +index 0000000..8e11d67e --- /dev/null +++ b/arch/arm/mach-bcm2708/include/mach/vcio.h @@ -0,0 +1,165 @@ @@ -5031,7 +4559,7 @@ index 0000000..5e43e85 +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bcm-mbox"); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig -index c43c714..9bedaea 100644 +index 9b4f29e..0c5d4ac 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -358,7 +358,7 @@ config CPU_PJ4B @@ -5096,7 +4624,7 @@ index 8d94c19..16ca535 100644 static struct vendor_data vendor_arm = { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h -index 9f32270..17ee61b 100644 +index 0c8cbe5..c3b84a2 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -291,6 +291,7 @@ struct mmc_host { @@ -5108,10 +4636,7354 @@ index 9f32270..17ee61b 100644 mmc_pm_flag_t pm_caps; /* supported pm features */ -From 2392ce74d3cd30172bc41023066f9f7abe7404ea Mon Sep 17 00:00:00 2001 +From debcc469754e0c81a822c8df081cea23c7ec5065 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 7 May 2013 14:32:27 +0100 +Subject: [PATCH 002/104] Add 2709 platform for Raspberry Pi 2 + +--- + arch/arm/Kconfig | 21 + + arch/arm/Makefile | 1 + + arch/arm/kernel/head.S | 8 + + arch/arm/mach-bcm2709/Kconfig | 49 + + arch/arm/mach-bcm2709/Makefile | 7 + + arch/arm/mach-bcm2709/Makefile.boot | 3 + + arch/arm/mach-bcm2709/armctrl.c | 361 +++++++ + arch/arm/mach-bcm2709/armctrl.h | 27 + + arch/arm/mach-bcm2709/bcm2708_gpio.c | 426 ++++++++ + arch/arm/mach-bcm2709/bcm2709.c | 1257 ++++++++++++++++++++++ + arch/arm/mach-bcm2709/bcm2709.h | 49 + + arch/arm/mach-bcm2709/clock.c | 61 ++ + arch/arm/mach-bcm2709/clock.h | 24 + + arch/arm/mach-bcm2709/delay.S | 21 + + arch/arm/mach-bcm2709/dma.c | 409 +++++++ + arch/arm/mach-bcm2709/dmaer.c | 886 +++++++++++++++ + arch/arm/mach-bcm2709/include/mach/arm_control.h | 493 +++++++++ + arch/arm/mach-bcm2709/include/mach/arm_power.h | 62 ++ + arch/arm/mach-bcm2709/include/mach/barriers.h | 3 + + arch/arm/mach-bcm2709/include/mach/clkdev.h | 7 + + arch/arm/mach-bcm2709/include/mach/debug-macro.S | 22 + + arch/arm/mach-bcm2709/include/mach/dma.h | 94 ++ + arch/arm/mach-bcm2709/include/mach/entry-macro.S | 123 +++ + arch/arm/mach-bcm2709/include/mach/frc.h | 38 + + arch/arm/mach-bcm2709/include/mach/gpio.h | 17 + + arch/arm/mach-bcm2709/include/mach/hardware.h | 28 + + arch/arm/mach-bcm2709/include/mach/io.h | 27 + + arch/arm/mach-bcm2709/include/mach/irqs.h | 225 ++++ + arch/arm/mach-bcm2709/include/mach/memory.h | 57 + + arch/arm/mach-bcm2709/include/mach/platform.h | 225 ++++ + arch/arm/mach-bcm2709/include/mach/power.h | 26 + + arch/arm/mach-bcm2709/include/mach/system.h | 38 + + arch/arm/mach-bcm2709/include/mach/timex.h | 23 + + arch/arm/mach-bcm2709/include/mach/uncompress.h | 84 ++ + arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 + + arch/arm/mach-bcm2709/include/mach/vc_support.h | 69 ++ + arch/arm/mach-bcm2709/include/mach/vcio.h | 165 +++ + arch/arm/mach-bcm2709/include/mach/vmalloc.h | 20 + + arch/arm/mach-bcm2709/power.c | 195 ++++ + arch/arm/mach-bcm2709/vc_mem.c | 431 ++++++++ + arch/arm/mach-bcm2709/vc_support.c | 318 ++++++ + arch/arm/mach-bcm2709/vcio.c | 474 ++++++++ + arch/arm/mm/proc-v7.S | 1 + + arch/arm/tools/mach-types | 1 + + drivers/clocksource/arm_arch_timer.c | 36 + + 45 files changed, 6947 insertions(+) + create mode 100644 arch/arm/mach-bcm2709/Kconfig + create mode 100644 arch/arm/mach-bcm2709/Makefile + create mode 100644 arch/arm/mach-bcm2709/Makefile.boot + create mode 100644 arch/arm/mach-bcm2709/armctrl.c + create mode 100644 arch/arm/mach-bcm2709/armctrl.h + create mode 100644 arch/arm/mach-bcm2709/bcm2708_gpio.c + create mode 100644 arch/arm/mach-bcm2709/bcm2709.c + create mode 100644 arch/arm/mach-bcm2709/bcm2709.h + create mode 100644 arch/arm/mach-bcm2709/clock.c + create mode 100644 arch/arm/mach-bcm2709/clock.h + create mode 100644 arch/arm/mach-bcm2709/delay.S + create mode 100644 arch/arm/mach-bcm2709/dma.c + create mode 100755 arch/arm/mach-bcm2709/dmaer.c + create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_control.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_power.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/barriers.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/clkdev.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/debug-macro.S + create mode 100644 arch/arm/mach-bcm2709/include/mach/dma.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/entry-macro.S + create mode 100644 arch/arm/mach-bcm2709/include/mach/frc.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/gpio.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/hardware.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/io.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/irqs.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/memory.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/platform.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/power.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/system.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/timex.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/uncompress.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h + create mode 100755 arch/arm/mach-bcm2709/include/mach/vc_support.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vcio.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vmalloc.h + create mode 100644 arch/arm/mach-bcm2709/power.c + create mode 100644 arch/arm/mach-bcm2709/vc_mem.c + create mode 100755 arch/arm/mach-bcm2709/vc_support.c + create mode 100644 arch/arm/mach-bcm2709/vcio.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 6876ae8..4018fea 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -793,6 +793,26 @@ config ARCH_OMAP1 + help + Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) + ++config ARCH_BCM2709 ++ bool "Broadcom BCM2709 family" ++ select ARCH_HAS_BARRIERS if SMP ++ select CPU_V7 ++ select HAVE_SMP ++ select ARM_AMBA ++ select MIGHT_HAVE_CACHE_L2X0 ++ select HAVE_SCHED_CLOCK ++ select NEED_MACH_MEMORY_H ++ select NEED_MACH_IO_H ++ select COMMON_CLK ++ select ARCH_HAS_CPUFREQ ++ select GENERIC_CLOCKEVENTS ++ select MACH_BCM2709 ++ select VC4 ++ select FIQ ++# select ZONE_DMA ++ help ++ This enables support for Broadcom BCM2709 boards. ++ + endchoice + + menu "Multiple platform selection" +@@ -984,6 +1004,7 @@ source "arch/arm/mach-vt8500/Kconfig" + + source "arch/arm/mach-w90x900/Kconfig" + source "arch/arm/mach-bcm2708/Kconfig" ++source "arch/arm/mach-bcm2709/Kconfig" + + source "arch/arm/mach-zynq/Kconfig" + +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 27c0f92..21ba7f3 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -147,6 +147,7 @@ machine-$(CONFIG_ARCH_AT91) += at91 + machine-$(CONFIG_ARCH_AXXIA) += axxia + machine-$(CONFIG_ARCH_BCM) += bcm + machine-$(CONFIG_ARCH_BCM2708) += bcm2708 ++machine-$(CONFIG_ARCH_BCM2709) += bcm2709 + machine-$(CONFIG_ARCH_BERLIN) += berlin + machine-$(CONFIG_ARCH_CLPS711X) += clps711x + machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx +diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S +index 0196327..e4d6030 100644 +--- a/arch/arm/kernel/head.S ++++ b/arch/arm/kernel/head.S +@@ -680,6 +680,14 @@ ARM_BE8(rev16 ip, ip) + ldrcc r7, [r4], #4 @ use branch for delay slot + bcc 1b + ret lr ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop + #endif + ENDPROC(__fixup_a_pv_table) + +diff --git a/arch/arm/mach-bcm2709/Kconfig b/arch/arm/mach-bcm2709/Kconfig +new file mode 100644 +index 0000000..4fb6e1b +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Kconfig +@@ -0,0 +1,49 @@ ++menu "Broadcom BCM2709 Implementations" ++ depends on ARCH_BCM2709 ++ ++config MACH_BCM2709 ++ bool "Broadcom BCM2709 Development Platform" ++ help ++ Include support for the Broadcom(R) BCM2709 platform. ++ ++config BCM2709_DT ++ bool "BCM2709 Device Tree support" ++ depends on MACH_BCM2709 ++ default n ++ select USE_OF ++ select ARCH_REQUIRE_GPIOLIB ++ select PINCTRL ++ select PINCTRL_BCM2835 ++ help ++ Enable Device Tree support for BCM2709 ++ ++config BCM2708_GPIO ++ bool "BCM2709 gpio support" ++ depends on MACH_BCM2709 ++ select ARCH_REQUIRE_GPIOLIB ++ default y ++ help ++ Include support for the Broadcom(R) BCM2709 gpio. ++ ++config BCM2708_VCMEM ++ bool "Videocore Memory" ++ depends on MACH_BCM2709 ++ default y ++ help ++ Helper for videocore memory access and total size allocation. ++ ++config BCM2708_NOL2CACHE ++ bool "Videocore L2 cache disable" ++ depends on MACH_BCM2709 ++ default y ++ help ++ Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. ++ ++config BCM2708_SPIDEV ++ bool "Bind spidev to SPI0 master" ++ depends on MACH_BCM2709 ++ depends on SPI ++ default y ++ help ++ Binds spidev driver to the SPI0 master ++endmenu +diff --git a/arch/arm/mach-bcm2709/Makefile b/arch/arm/mach-bcm2709/Makefile +new file mode 100644 +index 0000000..2a803bb +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux kernel. ++# ++ ++obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o dma.o ++obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o ++obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +diff --git a/arch/arm/mach-bcm2709/Makefile.boot b/arch/arm/mach-bcm2709/Makefile.boot +new file mode 100644 +index 0000000..67039c3 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Makefile.boot +@@ -0,0 +1,3 @@ ++ zreladdr-y := 0x00008000 ++params_phys-y := 0x00000100 ++initrd_phys-y := 0x00800000 +diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c +new file mode 100644 +index 0000000..2815b64 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/armctrl.c +@@ -0,0 +1,361 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "armctrl.h" ++ ++/* For support of kernels >= 3.0 assume only one VIC for now*/ ++static unsigned int remap_irqs[(INTERRUPT_ARASANSDIO + 1) - INTERRUPT_JPEG] = { ++ INTERRUPT_VC_JPEG, ++ INTERRUPT_VC_USB, ++ INTERRUPT_VC_3D, ++ INTERRUPT_VC_DMA2, ++ INTERRUPT_VC_DMA3, ++ INTERRUPT_VC_I2C, ++ INTERRUPT_VC_SPI, ++ INTERRUPT_VC_I2SPCM, ++ INTERRUPT_VC_SDIO, ++ INTERRUPT_VC_UART, ++ INTERRUPT_VC_ARASANSDIO ++}; ++ ++static void armctrl_mask_irq(struct irq_data *d) ++{ ++ static const unsigned int disables[4] = { ++ ARM_IRQ_DIBL1, ++ ARM_IRQ_DIBL2, ++ ARM_IRQ_DIBL3, ++ 0 ++ }; ++ int i; ++ if (d->irq >= FIQ_START) { ++ writel(0, __io_address(ARM_IRQ_FAST)); ++ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { ++#if 1 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; ++ for (i=0; i<4; i++) // i = raw_smp_processor_id(); // ++ { ++ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ writel(val &~ (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { ++#if 0 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; ++ for (i=0; i<4; i++) { ++ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ writel(val &~ (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { ++ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_CLR)); ++ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } ++} ++ ++static void armctrl_unmask_irq(struct irq_data *d) ++{ ++ static const unsigned int enables[4] = { ++ ARM_IRQ_ENBL1, ++ ARM_IRQ_ENBL2, ++ ARM_IRQ_ENBL3, ++ 0 ++ }; ++ int i; ++ if (d->irq >= FIQ_START) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); ++ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { ++#if 1 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; ++ for (i=0; i<4; i++) // i = raw_smp_processor_id(); ++ { ++ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ writel(val | (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { ++#if 0 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; ++ for (i=0; i<4; i++) { ++ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ writel(val | (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { ++ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_SET)); ++ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } ++} ++ ++#ifdef CONFIG_OF ++ ++#define NR_IRQS_BANK0 21 ++#define NR_BANKS 4 ++#define IRQS_PER_BANK 32 ++ ++/* from drivers/irqchip/irq-bcm2835.c */ ++static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, ++ const u32 *intspec, unsigned int intsize, ++ unsigned long *out_hwirq, unsigned int *out_type) ++{ ++ if (WARN_ON(intsize != 2)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] >= NR_BANKS)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5 && intspec[1] != 9)) ++ return -EINVAL; ++ ++ if (intspec[0] == 0) ++ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; ++ else if (intspec[0] == 1) ++ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; ++ else if (intspec[0] == 2) ++ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; ++ else ++ *out_hwirq = ARM_IRQ_LOCAL_BASE + intspec[1]; ++ ++ /* reverse remap_irqs[] */ ++ switch (*out_hwirq) { ++ case INTERRUPT_VC_JPEG: ++ *out_hwirq = INTERRUPT_JPEG; ++ break; ++ case INTERRUPT_VC_USB: ++ *out_hwirq = INTERRUPT_USB; ++ break; ++ case INTERRUPT_VC_3D: ++ *out_hwirq = INTERRUPT_3D; ++ break; ++ case INTERRUPT_VC_DMA2: ++ *out_hwirq = INTERRUPT_DMA2; ++ break; ++ case INTERRUPT_VC_DMA3: ++ *out_hwirq = INTERRUPT_DMA3; ++ break; ++ case INTERRUPT_VC_I2C: ++ *out_hwirq = INTERRUPT_I2C; ++ break; ++ case INTERRUPT_VC_SPI: ++ *out_hwirq = INTERRUPT_SPI; ++ break; ++ case INTERRUPT_VC_I2SPCM: ++ *out_hwirq = INTERRUPT_I2SPCM; ++ break; ++ case INTERRUPT_VC_SDIO: ++ *out_hwirq = INTERRUPT_SDIO; ++ break; ++ case INTERRUPT_VC_UART: ++ *out_hwirq = INTERRUPT_UART; ++ break; ++ case INTERRUPT_VC_ARASANSDIO: ++ *out_hwirq = INTERRUPT_ARASANSDIO; ++ break; ++ } ++ ++ *out_type = IRQ_TYPE_NONE; ++ return 0; ++} ++ ++static struct irq_domain_ops armctrl_ops = { ++ .xlate = armctrl_xlate ++}; ++ ++void __init armctrl_dt_init(void) ++{ ++ struct device_node *np; ++ struct irq_domain *domain; ++ ++ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); ++ if (!np) ++ return; ++ ++ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, ++ IRQ_ARMCTRL_START, 0, ++ &armctrl_ops, NULL); ++ WARN_ON(!domain); ++} ++#else ++void __init armctrl_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ ++#if defined(CONFIG_PM) ++ ++/* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ ++ ++/* Static defines ++ * struct armctrl_device - VIC PM device (< 3.xx) ++ * @sysdev: The system device which is registered. (< 3.xx) ++ * @irq: The IRQ number for the base of the VIC. ++ * @base: The register base for the VIC. ++ * @resume_sources: A bitmask of interrupts for resume. ++ * @resume_irqs: The IRQs enabled for resume. ++ * @int_select: Save for VIC_INT_SELECT. ++ * @int_enable: Save for VIC_INT_ENABLE. ++ * @soft_int: Save for VIC_INT_SOFT. ++ * @protect: Save for VIC_PROTECT. ++ */ ++struct armctrl_info { ++ void __iomem *base; ++ int irq; ++ u32 resume_sources; ++ u32 resume_irqs; ++ u32 int_select; ++ u32 int_enable; ++ u32 soft_int; ++ u32 protect; ++} armctrl; ++ ++static int armctrl_suspend(void) ++{ ++ return 0; ++} ++ ++static void armctrl_resume(void) ++{ ++ return; ++} ++ ++/** ++ * armctrl_pm_register - Register a VIC for later power management control ++ * @base: The base address of the VIC. ++ * @irq: The base IRQ for the VIC. ++ * @resume_sources: bitmask of interrupts allowed for resume sources. ++ * ++ * For older kernels (< 3.xx) do - ++ * Register the VIC with the system device tree so that it can be notified ++ * of suspend and resume requests and ensure that the correct actions are ++ * taken to re-instate the settings on resume. ++ */ ++static void __init armctrl_pm_register(void __iomem * base, unsigned int irq, ++ u32 resume_sources) ++{ ++ armctrl.base = base; ++ armctrl.resume_sources = resume_sources; ++ armctrl.irq = irq; ++} ++ ++static int armctrl_set_wake(struct irq_data *d, unsigned int on) ++{ ++ unsigned int off = d->irq & 31; ++ u32 bit = 1 << off; ++ ++ if (!(bit & armctrl.resume_sources)) ++ return -EINVAL; ++ ++ if (on) ++ armctrl.resume_irqs |= bit; ++ else ++ armctrl.resume_irqs &= ~bit; ++ ++ return 0; ++} ++ ++#else ++static inline void armctrl_pm_register(void __iomem * base, unsigned int irq, ++ u32 arg1) ++{ ++} ++ ++#define armctrl_suspend NULL ++#define armctrl_resume NULL ++#define armctrl_set_wake NULL ++#endif /* CONFIG_PM */ ++ ++static struct syscore_ops armctrl_syscore_ops = { ++ .suspend = armctrl_suspend, ++ .resume = armctrl_resume, ++}; ++ ++/** ++ * armctrl_syscore_init - initicall to register VIC pm functions ++ * ++ * This is called via late_initcall() to register ++ * the resources for the VICs due to the early ++ * nature of the VIC's registration. ++*/ ++static int __init armctrl_syscore_init(void) ++{ ++ register_syscore_ops(&armctrl_syscore_ops); ++ return 0; ++} ++ ++late_initcall(armctrl_syscore_init); ++ ++static struct irq_chip armctrl_chip = { ++ .name = "ARMCTRL", ++ .irq_ack = NULL, ++ .irq_mask = armctrl_mask_irq, ++ .irq_unmask = armctrl_unmask_irq, ++ .irq_set_wake = armctrl_set_wake, ++}; ++ ++/** ++ * armctrl_init - initialise a vectored interrupt controller ++ * @base: iomem base address ++ * @irq_start: starting interrupt number, must be muliple of 32 ++ * @armctrl_sources: bitmask of interrupt sources to allow ++ * @resume_sources: bitmask of interrupt sources to allow for resume ++ */ ++int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources) ++{ ++ unsigned int irq; ++ ++ for (irq = 0; irq < BCM2708_ALLOC_IRQS; irq++) { ++ unsigned int data = irq; ++ if (irq >= INTERRUPT_JPEG && irq <= INTERRUPT_ARASANSDIO) ++ data = remap_irqs[irq - INTERRUPT_JPEG]; ++ if (irq >= IRQ_ARM_LOCAL_CNTPSIRQ && irq <= IRQ_ARM_LOCAL_TIMER) { ++ irq_set_percpu_devid(irq); ++ irq_set_chip_and_handler(irq, &armctrl_chip, handle_percpu_devid_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); ++ } else { ++ irq_set_chip_and_handler(irq, &armctrl_chip, handle_level_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE | IRQF_DISABLED); ++ } ++ irq_set_chip_data(irq, (void *)data); ++ } ++ ++ armctrl_pm_register(base, irq_start, resume_sources); ++ init_FIQ(FIQ_START); ++ armctrl_dt_init(); ++ return 0; ++} +diff --git a/arch/arm/mach-bcm2709/armctrl.h b/arch/arm/mach-bcm2709/armctrl.h +new file mode 100644 +index 0000000..0aa916e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/armctrl.h +@@ -0,0 +1,27 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARMCTRL_H ++#define __BCM2708_ARMCTRL_H ++ ++extern int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/bcm2708_gpio.c b/arch/arm/mach-bcm2709/bcm2708_gpio.c +new file mode 100644 +index 0000000..c1e9254 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2708_gpio.c +@@ -0,0 +1,426 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++#define DRIVER_NAME BCM_GPIO_DRIVER_NAME ++#define BCM_GPIO_USE_IRQ 1 ++ ++#define GPIOFSEL(x) (0x00+(x)*4) ++#define GPIOSET(x) (0x1c+(x)*4) ++#define GPIOCLR(x) (0x28+(x)*4) ++#define GPIOLEV(x) (0x34+(x)*4) ++#define GPIOEDS(x) (0x40+(x)*4) ++#define GPIOREN(x) (0x4c+(x)*4) ++#define GPIOFEN(x) (0x58+(x)*4) ++#define GPIOHEN(x) (0x64+(x)*4) ++#define GPIOLEN(x) (0x70+(x)*4) ++#define GPIOAREN(x) (0x7c+(x)*4) ++#define GPIOAFEN(x) (0x88+(x)*4) ++#define GPIOUD(x) (0x94+(x)*4) ++#define GPIOUDCLK(x) (0x98+(x)*4) ++ ++#define GPIO_BANKS 2 ++ ++enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, ++ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, ++ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, ++ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, ++}; ++ ++ /* Each of the two spinlocks protects a different set of hardware ++ * regiters and data structurs. This decouples the code of the IRQ from ++ * the GPIO code. This also makes the case of a GPIO routine call from ++ * the IRQ code simpler. ++ */ ++static DEFINE_SPINLOCK(lock); /* GPIO registers */ ++ ++struct bcm2708_gpio { ++ struct list_head list; ++ void __iomem *base; ++ struct gpio_chip gc; ++ unsigned long rising[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long falling[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long high[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long low[(BCM2708_NR_GPIOS + 31) / 32]; ++}; ++ ++static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, ++ int function) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned long flags; ++ unsigned gpiodir; ++ unsigned gpio_bank = offset / 10; ++ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; ++ ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ gpiodir &= ~(7 << gpio_field_offset); ++ gpiodir |= function << gpio_field_offset; ++ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank)); ++ spin_unlock_irqrestore(&lock, flags); ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ ++ return 0; ++} ++ ++static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset) ++{ ++ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value); ++static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset, ++ int value) ++{ ++ int ret; ++ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT); ++ if (ret >= 0) ++ bcm2708_gpio_set(gc, offset, value); ++ return ret; ++} ++ ++static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++ unsigned lev; ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return 0; ++ lev = readl(gpio->base + GPIOLEV(gpio_bank)); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); ++ return 0x1 & (lev >> gpio_field_offset); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); ++ if (offset >= BCM2708_NR_GPIOS) ++ return; ++ if (value) ++ writel(1 << gpio_field_offset, gpio->base + GPIOSET(gpio_bank)); ++ else ++ writel(1 << gpio_field_offset, gpio->base + GPIOCLR(gpio_bank)); ++} ++ ++/********************** ++ * extension to configure pullups ++ */ ++int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, ++ bcm2708_gpio_pull_t value) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ switch (value) { ++ case BCM2708_PULL_UP: ++ writel(2, gpio->base + GPIOUD(0)); ++ break; ++ case BCM2708_PULL_DOWN: ++ writel(1, gpio->base + GPIOUD(0)); ++ break; ++ case BCM2708_PULL_OFF: ++ writel(0, gpio->base + GPIOUD(0)); ++ break; ++ } ++ ++ udelay(5); ++ writel(1 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); ++ udelay(5); ++ writel(0, gpio->base + GPIOUD(0)); ++ writel(0 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(bcm2708_gpio_setpull); ++ ++/************************************************************************************************************************* ++ * bcm2708 GPIO IRQ ++ */ ++ ++#if BCM_GPIO_USE_IRQ ++ ++static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) ++{ ++ return gpio_to_irq(gpio); ++} ++ ++static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned go = gn % 32; ++ ++ gpio->rising[gb] &= ~(1 << go); ++ gpio->falling[gb] &= ~(1 << go); ++ gpio->high[gb] &= ~(1 << go); ++ gpio->low[gb] &= ~(1 << go); ++ ++ if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) ++ return -EINVAL; ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ gpio->rising[gb] |= (1 << go); ++ if (type & IRQ_TYPE_EDGE_FALLING) ++ gpio->falling[gb] |= (1 << go); ++ if (type & IRQ_TYPE_LEVEL_HIGH) ++ gpio->high[gb] |= (1 << go); ++ if (type & IRQ_TYPE_LEVEL_LOW) ++ gpio->low[gb] |= (1 << go); ++ return 0; ++} ++ ++static void bcm2708_gpio_irq_mask(struct irq_data *d) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned long rising = readl(gpio->base + GPIOREN(gb)); ++ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); ++ unsigned long high = readl(gpio->base + GPIOHEN(gb)); ++ unsigned long low = readl(gpio->base + GPIOLEN(gb)); ++ ++ gn = gn % 32; ++ ++ writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); ++ writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); ++ writel(high & ~(1 << gn), gpio->base + GPIOHEN(gb)); ++ writel(low & ~(1 << gn), gpio->base + GPIOLEN(gb)); ++} ++ ++static void bcm2708_gpio_irq_unmask(struct irq_data *d) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned go = gn % 32; ++ unsigned long rising = readl(gpio->base + GPIOREN(gb)); ++ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); ++ unsigned long high = readl(gpio->base + GPIOHEN(gb)); ++ unsigned long low = readl(gpio->base + GPIOLEN(gb)); ++ ++ if (gpio->rising[gb] & (1 << go)) { ++ writel(rising | (1 << go), gpio->base + GPIOREN(gb)); ++ } else { ++ writel(rising & ~(1 << go), gpio->base + GPIOREN(gb)); ++ } ++ ++ if (gpio->falling[gb] & (1 << go)) { ++ writel(falling | (1 << go), gpio->base + GPIOFEN(gb)); ++ } else { ++ writel(falling & ~(1 << go), gpio->base + GPIOFEN(gb)); ++ } ++ ++ if (gpio->high[gb] & (1 << go)) { ++ writel(high | (1 << go), gpio->base + GPIOHEN(gb)); ++ } else { ++ writel(high & ~(1 << go), gpio->base + GPIOHEN(gb)); ++ } ++ ++ if (gpio->low[gb] & (1 << go)) { ++ writel(low | (1 << go), gpio->base + GPIOLEN(gb)); ++ } else { ++ writel(low & ~(1 << go), gpio->base + GPIOLEN(gb)); ++ } ++} ++ ++static struct irq_chip bcm2708_irqchip = { ++ .name = "GPIO", ++ .irq_enable = bcm2708_gpio_irq_unmask, ++ .irq_disable = bcm2708_gpio_irq_mask, ++ .irq_unmask = bcm2708_gpio_irq_unmask, ++ .irq_mask = bcm2708_gpio_irq_mask, ++ .irq_set_type = bcm2708_gpio_irq_set_type, ++}; ++ ++static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id) ++{ ++ unsigned long edsr; ++ unsigned bank; ++ int i; ++ unsigned gpio; ++ unsigned level_bits; ++ struct bcm2708_gpio *gpio_data = dev_id; ++ ++ for (bank = 0; bank < GPIO_BANKS; bank++) { ++ edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank)); ++ level_bits = gpio_data->high[bank] | gpio_data->low[bank]; ++ ++ for_each_set_bit(i, &edsr, 32) { ++ gpio = i + bank * 32; ++ /* ack edge triggered IRQs immediately */ ++ if (!(level_bits & (1<gc.to_irq = bcm2708_gpio_to_irq; ++ ++ for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) { ++ irq_set_chip_data(irq, ucb); ++ irq_set_chip_and_handler(irq, &bcm2708_irqchip, ++ handle_simple_irq); ++ set_irq_flags(irq, IRQF_VALID); ++ } ++ ++ bcm2708_gpio_irq.dev_id = ucb; ++ setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq); ++} ++ ++#else ++ ++static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) ++{ ++} ++ ++#endif /* #if BCM_GPIO_USE_IRQ ***************************************************************************************************************** */ ++ ++static int bcm2708_gpio_probe(struct platform_device *dev) ++{ ++ struct bcm2708_gpio *ucb; ++ struct resource *res; ++ int bank; ++ int err = 0; ++ ++ printk(KERN_INFO DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev); ++ ++ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL); ++ if (NULL == ucb) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ res = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ ++ platform_set_drvdata(dev, ucb); ++ ucb->base = __io_address(GPIO_BASE); ++ ++ ucb->gc.label = "bcm2708_gpio"; ++ ucb->gc.base = 0; ++ ucb->gc.ngpio = BCM2708_NR_GPIOS; ++ ucb->gc.owner = THIS_MODULE; ++ ++ ucb->gc.direction_input = bcm2708_gpio_dir_in; ++ ucb->gc.direction_output = bcm2708_gpio_dir_out; ++ ucb->gc.get = bcm2708_gpio_get; ++ ucb->gc.set = bcm2708_gpio_set; ++ ucb->gc.can_sleep = 0; ++ ++ for (bank = 0; bank < GPIO_BANKS; bank++) { ++ writel(0, ucb->base + GPIOREN(bank)); ++ writel(0, ucb->base + GPIOFEN(bank)); ++ writel(0, ucb->base + GPIOHEN(bank)); ++ writel(0, ucb->base + GPIOLEN(bank)); ++ writel(0, ucb->base + GPIOAREN(bank)); ++ writel(0, ucb->base + GPIOAFEN(bank)); ++ writel(~0, ucb->base + GPIOEDS(bank)); ++ } ++ ++ bcm2708_gpio_irq_init(ucb); ++ ++ err = gpiochip_add(&ucb->gc); ++ ++err: ++ return err; ++ ++} ++ ++static int bcm2708_gpio_remove(struct platform_device *dev) ++{ ++ int err = 0; ++ struct bcm2708_gpio *ucb = platform_get_drvdata(dev); ++ ++ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev); ++ ++ gpiochip_remove(&ucb->gc); ++ ++ platform_set_drvdata(dev, NULL); ++ kfree(ucb); ++ ++ return err; ++} ++ ++static struct platform_driver bcm2708_gpio_driver = { ++ .probe = bcm2708_gpio_probe, ++ .remove = bcm2708_gpio_remove, ++ .driver = { ++ .name = "bcm2708_gpio"}, ++}; ++ ++static int __init bcm2708_gpio_init(void) ++{ ++ return platform_driver_register(&bcm2708_gpio_driver); ++} ++ ++static void __exit bcm2708_gpio_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_gpio_driver); ++} ++ ++module_init(bcm2708_gpio_init); ++module_exit(bcm2708_gpio_exit); ++ ++MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +new file mode 100644 +index 0000000..c089f4a +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -0,0 +1,1257 @@ ++/* ++ * linux/arch/arm/mach-bcm2709/bcm2709.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "bcm2709.h" ++#include "armctrl.h" ++ ++#ifdef CONFIG_BCM_VC_CMA ++#include ++#endif ++ ++//#define SYSTEM_TIMER ++ ++/* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to ++ * give us IO access only to 64Mbytes of physical memory (26 bits). We could ++ * represent this window by setting our dmamasks to 26 bits but, in fact ++ * we're not going to use addresses outside this range (they're not in real ++ * memory) so we don't bother. ++ * ++ * In the future we might include code to use this IOMMU to remap other ++ * physical addresses onto VideoCore memory then the use of 32-bits would be ++ * more legitimate. ++ */ ++#define DMA_MASK_BITS_COMMON 32 ++ ++// use GPIO 4 for the one-wire GPIO pin, if enabled ++#define W1_GPIO 4 ++// ensure one-wire GPIO pullup is disabled by default ++#define W1_PULLUP -1 ++ ++/* command line parameters */ ++static unsigned boardrev, serial; ++static unsigned uart_clock = UART0_CLOCK; ++static unsigned disk_led_gpio = 16; ++static unsigned disk_led_active_low = 1; ++static unsigned reboot_part = 0; ++static unsigned w1_gpio_pin = W1_GPIO; ++static unsigned w1_gpio_pullup = W1_PULLUP; ++ ++static unsigned use_dt = 0; ++ ++static void __init bcm2709_init_led(void); ++ ++void __init bcm2709_init_irq(void) ++{ ++ armctrl_init(__io_address(ARMCTRL_IC_BASE), 0, 0, 0); ++} ++ ++static struct map_desc bcm2709_io_desc[] __initdata = { ++ { ++ .virtual = IO_ADDRESS(ARMCTRL_BASE), ++ .pfn = __phys_to_pfn(ARMCTRL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART0_BASE), ++ .pfn = __phys_to_pfn(UART0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART1_BASE), ++ .pfn = __phys_to_pfn(UART1_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(DMA_BASE), ++ .pfn = __phys_to_pfn(DMA_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(MCORE_BASE), ++ .pfn = __phys_to_pfn(MCORE_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(ST_BASE), ++ .pfn = __phys_to_pfn(ST_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(USB_BASE), ++ .pfn = __phys_to_pfn(USB_BASE), ++ .length = SZ_128K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(PM_BASE), ++ .pfn = __phys_to_pfn(PM_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(GPIO_BASE), ++ .pfn = __phys_to_pfn(GPIO_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(ARM_LOCAL_BASE), ++ .pfn = __phys_to_pfn(ARM_LOCAL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++}; ++ ++void __init bcm2709_map_io(void) ++{ ++ iotable_init(bcm2709_io_desc, ARRAY_SIZE(bcm2709_io_desc)); ++} ++ ++#ifdef SYSTEM_TIMER ++ ++/* The STC is a free running counter that increments at the rate of 1MHz */ ++#define STC_FREQ_HZ 1000000 ++ ++static inline uint32_t timer_read(void) ++{ ++ /* STC: a free running counter that increments at the rate of 1MHz */ ++ return readl(__io_address(ST_BASE + 0x04)); ++} ++ ++static unsigned long bcm2709_read_current_timer(void) ++{ ++ return timer_read(); ++} ++ ++static u64 notrace bcm2709_read_sched_clock(void) ++{ ++ return timer_read(); ++} ++ ++static cycle_t clksrc_read(struct clocksource *cs) ++{ ++ return timer_read(); ++} ++ ++static struct clocksource clocksource_stc = { ++ .name = "stc", ++ .rating = 300, ++ .read = clksrc_read, ++ .mask = CLOCKSOURCE_MASK(32), ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++}; ++ ++unsigned long frc_clock_ticks32(void) ++{ ++ return timer_read(); ++} ++ ++static void __init bcm2709_clocksource_init(void) ++{ ++ if (clocksource_register_hz(&clocksource_stc, STC_FREQ_HZ)) { ++ printk(KERN_ERR "timer: failed to initialize clock " ++ "source %s\n", clocksource_stc.name); ++ } ++} ++#endif ++ ++struct clk __init *bcm2709_clk_register(const char *name, unsigned long fixed_rate) ++{ ++ struct clk *clk; ++ ++ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, ++ fixed_rate); ++ if (IS_ERR(clk)) ++ pr_err("%s not registered\n", name); ++ ++ return clk; ++} ++ ++void __init bcm2709_register_clkdev(struct clk *clk, const char *name) ++{ ++ int ret; ++ ++ ret = clk_register_clkdev(clk, NULL, name); ++ if (ret) ++ pr_err("%s alias not registered\n", name); ++} ++ ++void __init bcm2709_init_clocks(void) ++{ ++ struct clk *clk; ++ ++ clk = bcm2709_clk_register("uart0_clk", uart_clock); ++ bcm2709_register_clkdev(clk, "dev:f1"); ++ ++ clk = bcm2709_clk_register("sdhost_clk", 250000000); ++ bcm2709_register_clkdev(clk, "bcm2708_spi.0"); ++ bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); ++ bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); ++} ++ ++#define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } ++#define UART0_DMA { 15, 14 } ++ ++AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); ++ ++static struct amba_device *amba_devs[] __initdata = { ++ &uart0_device, ++}; ++ ++static struct resource bcm2708_dmaman_resources[] = { ++ { ++ .start = DMA_BASE, ++ .end = DMA_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_dmaman_device = { ++ .name = BCM_DMAMAN_DRIVER_NAME, ++ .id = 0, /* first bcm2708_dma */ ++ .resource = bcm2708_dmaman_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), ++}; ++ ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++static struct w1_gpio_platform_data w1_gpio_pdata = { ++ .pin = W1_GPIO, ++ .ext_pullup_enable_pin = W1_PULLUP, ++ .is_open_drain = 0, ++}; ++ ++static struct platform_device w1_device = { ++ .name = "w1-gpio", ++ .id = -1, ++ .dev.platform_data = &w1_gpio_pdata, ++}; ++#endif ++ ++static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_fb_device = { ++ .name = "bcm2708_fb", ++ .id = -1, /* only one bcm2708_fb */ ++ .resource = NULL, ++ .num_resources = 0, ++ .dev = { ++ .dma_mask = &fb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { ++ { ++ .mapbase = UART1_BASE + 0x40, ++ .irq = IRQ_AUX, ++ .uartclk = 125000000, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, ++ .type = PORT_8250, ++ }, ++ {}, ++}; ++ ++static struct platform_device bcm2708_uart1_device = { ++ .name = "serial8250", ++ .id = PLAT8250_DEV_PLATFORM, ++ .dev = { ++ .platform_data = bcm2708_uart1_platform_data, ++ }, ++}; ++ ++static struct resource bcm2708_usb_resources[] = { ++ [0] = { ++ .start = USB_BASE, ++ .end = USB_BASE + SZ_128K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = MPHI_BASE, ++ .end = MPHI_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = IRQ_HOSTPORT, ++ .end = IRQ_HOSTPORT, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [3] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [4] = { ++ .start = ARM_LOCAL_BASE, ++ .end = ARM_LOCAL_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [5] = { ++ .start = IRQ_ARM_LOCAL_MAILBOX1, ++ .end = IRQ_ARM_LOCAL_MAILBOX1, ++ .flags = IORESOURCE_IRQ ++ }, ++}; ++ ++ ++static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_usb_device = { ++ .name = "bcm2708_usb", ++ .id = -1, /* only one bcm2708_usb */ ++ .resource = bcm2708_usb_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), ++ .dev = { ++ .dma_mask = &usb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct resource bcm2708_vcio_resources[] = { ++ [0] = { /* mailbox/semaphore/doorbell access */ ++ .start = MCORE_BASE, ++ .end = MCORE_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_vcio_device = { ++ .name = BCM_VCIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vcio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), ++ .dev = { ++ .dma_mask = &vcio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++#ifdef CONFIG_BCM2708_GPIO ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++ ++static struct resource bcm2708_gpio_resources[] = { ++ [0] = { /* general purpose I/O */ ++ .start = GPIO_BASE, ++ .end = GPIO_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_gpio_device = { ++ .name = BCM_GPIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_gpio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), ++ .dev = { ++ .dma_mask = &gpio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef SYSTEM_TIMER ++static struct resource bcm2708_systemtimer_resources[] = { ++ [0] = { /* system timer access */ ++ .start = ST_BASE, ++ .end = ST_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = IRQ_TIMER3, ++ .end = IRQ_TIMER3, ++ .flags = IORESOURCE_IRQ, ++ } ++ ++}; ++ ++static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_systemtimer_device = { ++ .name = "bcm2708_systemtimer", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_systemtimer_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), ++ .dev = { ++ .dma_mask = &systemtimer_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ ++static struct resource bcm2835_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2835_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2835_emmc_device = { ++ .name = "mmc-bcm2835", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), ++ .resource = bcm2835_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2835_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++#endif /* CONFIG_MMC_BCM2835 */ ++ ++static struct resource bcm2708_powerman_resources[] = { ++ [0] = { ++ .start = PM_BASE, ++ .end = PM_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++struct platform_device bcm2708_powerman_device = { ++ .name = "bcm2708_powerman", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), ++ .resource = bcm2708_powerman_resources, ++ .dev = { ++ .dma_mask = &powerman_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++ ++ ++static struct platform_device bcm2708_alsa_devices[] = { ++ [0] = { ++ .name = "bcm2835_AUD0", ++ .id = 0, /* first audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [1] = { ++ .name = "bcm2835_AUD1", ++ .id = 1, /* second audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [2] = { ++ .name = "bcm2835_AUD2", ++ .id = 2, /* third audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [3] = { ++ .name = "bcm2835_AUD3", ++ .id = 3, /* forth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [4] = { ++ .name = "bcm2835_AUD4", ++ .id = 4, /* fifth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [5] = { ++ .name = "bcm2835_AUD5", ++ .id = 5, /* sixth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [6] = { ++ .name = "bcm2835_AUD6", ++ .id = 6, /* seventh audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [7] = { ++ .name = "bcm2835_AUD7", ++ .id = 7, /* eighth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++}; ++ ++static struct resource bcm2708_spi_resources[] = { ++ { ++ .start = SPI0_BASE, ++ .end = SPI0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SPI, ++ .end = IRQ_SPI, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++static struct platform_device bcm2708_spi_device = { ++ .name = "bcm2708_spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), ++ .resource = bcm2708_spi_resources, ++ .dev = { ++ .dma_mask = &bcm2708_spi_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, ++}; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++static struct spi_board_info bcm2708_spi_devices[] = { ++#ifdef CONFIG_SPI_SPIDEV ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = SPI_MODE_0, ++ }, { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 1, ++ .mode = SPI_MODE_0, ++ } ++#endif ++}; ++#endif ++ ++static struct resource bcm2708_bsc0_resources[] = { ++ { ++ .start = BSC0_BASE, ++ .end = BSC0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc0_device = { ++ .name = "bcm2708_i2c", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), ++ .resource = bcm2708_bsc0_resources, ++}; ++ ++ ++static struct resource bcm2708_bsc1_resources[] = { ++ { ++ .start = BSC1_BASE, ++ .end = BSC1_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc1_device = { ++ .name = "bcm2708_i2c", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), ++ .resource = bcm2708_bsc1_resources, ++}; ++ ++static struct platform_device bcm2835_hwmon_device = { ++ .name = "bcm2835_hwmon", ++}; ++ ++static struct platform_device bcm2835_thermal_device = { ++ .name = "bcm2835_thermal", ++}; ++ ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++static struct resource bcm2708_i2s_resources[] = { ++ { ++ .start = I2S_BASE, ++ .end = I2S_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PCM_CLOCK_BASE, ++ .end = PCM_CLOCK_BASE + 0x02, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_i2s_device = { ++ .name = "bcm2708-i2s", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), ++ .resource = bcm2708_i2s_resources, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++static struct platform_device snd_hifiberry_dac_device = { ++ .name = "snd-hifiberry-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm5102a_codec_device = { ++ .name = "pcm5102a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) ++static struct platform_device snd_rpi_hifiberry_dacplus_device = { ++ .name = "snd-rpi-hifiberry-dacplus", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_pcm512x_hbdacplus_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4d) ++ }, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++static struct platform_device snd_hifiberry_digi_device = { ++ .name = "snd-hifiberry-digi", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("wm8804", 0x3b) ++ }, ++}; ++ ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) ++static struct platform_device snd_hifiberry_amp_device = { ++ .name = "snd-hifiberry-amp", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_tas5713_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("tas5713", 0x1b) ++ }, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++static struct platform_device snd_rpi_dac_device = { ++ .name = "snd-rpi-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm1794a_codec_device = { ++ .name = "pcm1794a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++static struct platform_device snd_rpi_iqaudio_dac_device = { ++ .name = "snd-rpi-iqaudio-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++// Use the actual device name rather than generic driver name ++static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4c) ++ }, ++}; ++#endif ++ ++int __init bcm_register_device(struct platform_device *pdev) ++{ ++ int ret; ++ ++ ret = platform_device_register(pdev); ++ if (ret) ++ pr_debug("Unable to register platform device '%s': %d\n", ++ pdev->name, ret); ++ ++ return ret; ++} ++ ++/* ++ * Use these macros for platform and i2c devices that are present in the ++ * Device Tree. This way the devices are only added on non-DT systems. ++ */ ++#define bcm_register_device_dt(pdev) \ ++ if (!use_dt) bcm_register_device(pdev) ++ ++#define i2c_register_board_info_dt(busnum, info, n) \ ++ if (!use_dt) i2c_register_board_info(busnum, info, n) ++ ++int calc_rsts(int partition) ++{ ++ return PM_PASSWORD | ++ ((partition & (1 << 0)) << 0) | ++ ((partition & (1 << 1)) << 1) | ++ ((partition & (1 << 2)) << 2) | ++ ((partition & (1 << 3)) << 3) | ++ ((partition & (1 << 4)) << 4) | ++ ((partition & (1 << 5)) << 5); ++} ++ ++static void bcm2709_restart(enum reboot_mode mode, const char *cmd) ++{ ++ extern char bcm2708_reboot_mode; ++ uint32_t pm_rstc, pm_wdog; ++ uint32_t timeout = 10; ++ uint32_t pm_rsts = 0; ++ ++ if(bcm2708_reboot_mode == 'q') ++ { ++ // NOOBS < 1.3 booting with reboot=q ++ pm_rsts = readl(__io_address(PM_RSTS)); ++ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRQ_SET; ++ } ++ else if(bcm2708_reboot_mode == 'p') ++ { ++ // NOOBS < 1.3 halting ++ pm_rsts = readl(__io_address(PM_RSTS)); ++ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRH_SET; ++ } ++ else ++ { ++ pm_rsts = calc_rsts(reboot_part); ++ } ++ ++ writel(pm_rsts, __io_address(PM_RSTS)); ++ ++ /* Setup watchdog for reset */ ++ pm_rstc = readl(__io_address(PM_RSTC)); ++ ++ pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0) ++ pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; ++ ++ writel(pm_wdog, __io_address(PM_WDOG)); ++ writel(pm_rstc, __io_address(PM_RSTC)); ++} ++ ++/* We can't really power off, but if we do the normal reset scheme, and indicate to bootcode.bin not to reboot, then most of the chip will be powered off */ ++static void bcm2709_power_off(void) ++{ ++ extern char bcm2708_reboot_mode; ++ if(bcm2708_reboot_mode == 'q') ++ { ++ // NOOBS < v1.3 ++ bcm2709_restart('p', ""); ++ } ++ else ++ { ++ /* partition 63 is special code for HALT the bootloader knows not to boot*/ ++ reboot_part = 63; ++ /* continue with normal reset mechanism */ ++ bcm2709_restart(0, ""); ++ } ++} ++ ++#ifdef CONFIG_OF ++static void __init bcm2709_dt_init(void) ++{ ++ int ret; ++ ++ of_clk_init(NULL); ++ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++ if (ret) { ++ pr_err("of_platform_populate failed: %d\n", ret); ++ /* Proceed as if CONFIG_OF was not defined */ ++ } else { ++ use_dt = 1; ++ } ++} ++#else ++static void __init bcm2709_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ ++void __init bcm2709_init(void) ++{ ++ int i; ++ ++#if defined(CONFIG_BCM_VC_CMA) ++ vc_cma_early_init(); ++#endif ++ printk("bcm2709.uart_clock = %d\n", uart_clock); ++ pm_power_off = bcm2709_power_off; ++ ++ bcm2709_init_clocks(); ++ bcm2709_dt_init(); ++ ++ bcm_register_device(&bcm2708_dmaman_device); ++ bcm_register_device(&bcm2708_vcio_device); ++#ifdef CONFIG_BCM2708_GPIO ++ bcm_register_device_dt(&bcm2708_gpio_device); ++#endif ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++ w1_gpio_pdata.pin = w1_gpio_pin; ++ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; ++ bcm_register_device_dt(&w1_device); ++#endif ++#ifdef SYSTEM_TIMER ++ bcm_register_device(&bcm2708_systemtimer_device); ++#endif ++ bcm_register_device(&bcm2708_fb_device); ++ bcm_register_device(&bcm2708_usb_device); ++ bcm_register_device(&bcm2708_uart1_device); ++ bcm_register_device(&bcm2708_powerman_device); ++ ++#ifdef CONFIG_MMC_BCM2835 ++ bcm_register_device(&bcm2835_emmc_device); ++#endif ++ bcm2709_init_led(); ++ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) ++ bcm_register_device(&bcm2708_alsa_devices[i]); ++ ++ bcm_register_device(&bcm2835_hwmon_device); ++ bcm_register_device(&bcm2835_thermal_device); ++ ++ bcm_register_device_dt(&bcm2708_spi_device); ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++ bcm_register_device_dt(&bcm2708_i2s_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_dac_device); ++ bcm_register_device_dt(&snd_pcm5102a_codec_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) ++ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_digi_device); ++ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_amp_device); ++ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_dac_device); ++ bcm_register_device_dt(&snd_pcm1794a_codec_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); ++#endif ++ ++ ++ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { ++ struct amba_device *d = amba_devs[i]; ++ amba_device_register(d, &iomem_resource); ++ } ++ system_rev = boardrev; ++ system_serial_low = serial; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++ if (!use_dt) ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); ++#endif ++} ++ ++#ifdef SYSTEM_TIMER ++static void timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *clk) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: /* Leave the timer disabled, .set_next_event will enable it */ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_PERIODIC: ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_RESUME: ++ ++ default: ++ printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", ++ (int)mode); ++ break; ++ } ++ ++} ++ ++static int timer_set_next_event(unsigned long cycles, ++ struct clock_event_device *unused) ++{ ++ unsigned long stc; ++ do { ++ stc = readl(__io_address(ST_BASE + 0x04)); ++ /* We could take a FIQ here, which may push ST above STC3 */ ++ writel(stc + cycles, __io_address(ST_BASE + 0x18)); ++ } while ((signed long) cycles >= 0 && ++ (signed long) (readl(__io_address(ST_BASE + 0x04)) - stc) ++ >= (signed long) cycles); ++ return 0; ++} ++ ++static struct clock_event_device timer0_clockevent = { ++ .name = "timer0", ++ .shift = 32, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_mode = timer_set_mode, ++ .set_next_event = timer_set_next_event, ++}; ++ ++/* ++ * IRQ handler for the timer ++ */ ++static irqreturn_t bcm2709_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = &timer0_clockevent; ++ ++ writel(1 << 3, __io_address(ST_BASE + 0x00)); /* stcs clear timer int */ ++ ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction bcm2709_timer_irq = { ++ .name = "BCM2709 Timer Tick", ++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, ++ .handler = bcm2709_timer_interrupt, ++}; ++ ++/* ++ * Set up timer interrupt, and return the current time in seconds. ++ */ ++ ++static struct delay_timer bcm2709_delay_timer = { ++ .read_current_timer = bcm2709_read_current_timer, ++ .freq = STC_FREQ_HZ, ++}; ++ ++static void __init bcm2709_timer_init(void) ++{ ++ /* init high res timer */ ++ bcm2709_clocksource_init(); ++ ++ /* ++ * Make irqs happen for the system timer ++ */ ++ setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); ++ ++ sched_clock_register(bcm2709_read_sched_clock, 32, STC_FREQ_HZ); ++ ++ timer0_clockevent.mult = ++ div_sc(STC_FREQ_HZ, NSEC_PER_SEC, timer0_clockevent.shift); ++ timer0_clockevent.max_delta_ns = ++ clockevent_delta2ns(0xffffffff, &timer0_clockevent); ++ timer0_clockevent.min_delta_ns = ++ clockevent_delta2ns(0xf, &timer0_clockevent); ++ ++ timer0_clockevent.cpumask = cpumask_of(0); ++ clockevents_register_device(&timer0_clockevent); ++ ++ register_current_timer_delay(&bcm2708_delay_timer); ++} ++ ++#else ++ ++static void __init bcm2709_timer_init(void) ++{ ++ extern void dc4_arch_timer_init(void); ++ // timer control ++ writel(0, __io_address(ARM_LOCAL_CONTROL)); ++ // timer pre_scaler ++ writel(0x80000000, __io_address(ARM_LOCAL_PRESCALER)); // 19.2MHz ++ //writel(0x06AAAAAB, __io_address(ARM_LOCAL_PRESCALER)); // 1MHz ++ ++ if (use_dt) ++ { ++ of_clk_init(NULL); ++ clocksource_of_init(); ++ } ++ else ++ dc4_arch_timer_init(); ++} ++ ++#endif ++ ++#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) ++#include ++ ++static struct gpio_led bcm2709_leds[] = { ++ [0] = { ++ .gpio = 16, ++ .name = "led0", ++ .default_trigger = "mmc0", ++ .active_low = 1, ++ }, ++}; ++ ++static struct gpio_led_platform_data bcm2709_led_pdata = { ++ .num_leds = ARRAY_SIZE(bcm2709_leds), ++ .leds = bcm2709_leds, ++}; ++ ++static struct platform_device bcm2709_led_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &bcm2709_led_pdata, ++ }, ++}; ++ ++static void __init bcm2709_init_led(void) ++{ ++ bcm2709_leds[0].gpio = disk_led_gpio; ++ bcm2709_leds[0].active_low = disk_led_active_low; ++ bcm_register_device_dt(&bcm2709_led_device); ++} ++#else ++static inline void bcm2709_init_led(void) ++{ ++} ++#endif ++ ++void __init bcm2709_init_early(void) ++{ ++ /* ++ * Some devices allocate their coherent buffers from atomic ++ * context. Increase size of atomic coherent pool to make sure such ++ * the allocations won't fail. ++ */ ++ init_dma_coherent_pool_size(SZ_4M); ++} ++ ++static void __init board_reserve(void) ++{ ++#if defined(CONFIG_BCM_VC_CMA) ++ vc_cma_reserve(); ++#endif ++} ++ ++ ++#include ++ ++#include ++#include ++#include ++int dc4=0; ++//void dc4_log(unsigned x) { if (dc4) writel((x), __io_address(ST_BASE+10 + raw_smp_processor_id()*4)); } ++void dc4_log_dead(unsigned x) { if (dc4) writel((readl(__io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)) & 0xffff) | ((x)<<16), __io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)); } ++ ++static void bcm2835_send_doorbell(const struct cpumask *mask, unsigned int irq) ++{ ++ int cpu; ++ /* ++ * Ensure that stores to Normal memory are visible to the ++ * other CPUs before issuing the IPI. ++ */ ++ dsb(); ++ ++ /* Convert our logical CPU mask into a physical one. */ ++ for_each_cpu(cpu, mask) ++ { ++ /* submit softirq */ ++ writel(1<%x)\n", __FUNCTION__, (unsigned)virt_to_phys((void *)secondary_startup), (unsigned)__io_address(ST_BASE + 0x10)); ++ printk("[%s] ncores=%d\n", __FUNCTION__, ncores); ++ ++ for (i = 0; i < ncores; i++) { ++ set_cpu_possible(i, true); ++ /* enable IRQ (not FIQ) */ ++ writel(0x1, __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i)); ++ //writel(0xf, __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 0x4 * i)); ++ } ++ set_smp_cross_call(bcm2835_send_doorbell); ++} ++ ++/* ++ * for arch/arm/kernel/smp.c:smp_prepare_cpus(unsigned int max_cpus) ++ */ ++void __init bcm2709_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ //void __iomem *scu_base; ++ ++ printk("[%s] enter\n", __FUNCTION__); ++ //scu_base = scu_base_addr(); ++ //scu_enable(scu_base); ++} ++ ++/* ++ * for linux/arch/arm/kernel/smp.c:secondary_start_kernel(void) ++ */ ++void __cpuinit bcm2709_secondary_init(unsigned int cpu) ++{ ++ printk("[%s] enter cpu:%d\n", __FUNCTION__, cpu); ++ //gic_secondary_init(0); ++} ++ ++/* ++ * for linux/arch/arm/kernel/smp.c:__cpu_up(..) ++ */ ++int __cpuinit bcm2709_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ void secondary_startup(void); ++ void *mbox_set = __io_address(ARM_LOCAL_MAILBOX3_SET0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); ++ void *mbox_clr = __io_address(ARM_LOCAL_MAILBOX3_CLR0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); ++ unsigned secondary_boot = (unsigned)virt_to_phys((void *)secondary_startup); ++ int timeout=20; ++ unsigned t = -1; ++ //printk("[%s] enter cpu:%d (%x->%p) %x\n", __FUNCTION__, cpu, secondary_boot, wake, readl(wake)); ++ ++ dsb(); ++ BUG_ON(readl(mbox_clr) != 0); ++ writel(secondary_boot, mbox_set); ++ ++ while (--timeout > 0) { ++ t = readl(mbox_clr); ++ if (t == 0) break; ++ cpu_relax(); ++ } ++ if (timeout==0) ++ printk("[%s] cpu:%d failed to start (%x)\n", __FUNCTION__, cpu, t); ++ else ++ printk("[%s] cpu:%d started (%x) %d\n", __FUNCTION__, cpu, t, timeout); ++ ++ return 0; ++} ++ ++ ++struct smp_operations bcm2709_smp_ops __initdata = { ++ .smp_init_cpus = bcm2709_smp_init_cpus, ++ .smp_prepare_cpus = bcm2709_smp_prepare_cpus, ++ .smp_secondary_init = bcm2709_secondary_init, ++ .smp_boot_secondary = bcm2709_boot_secondary, ++}; ++ ++static const char * const bcm2709_compat[] = { ++ "brcm,bcm2709", ++ "brcm,bcm2708", /* Could use bcm2708 in a pinch */ ++ NULL ++}; ++ ++MACHINE_START(BCM2709, "BCM2709") ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .smp = smp_ops(bcm2709_smp_ops), ++ .map_io = bcm2709_map_io, ++ .init_irq = bcm2709_init_irq, ++ .init_time = bcm2709_timer_init, ++ .init_machine = bcm2709_init, ++ .init_early = bcm2709_init_early, ++ .reserve = board_reserve, ++ .restart = bcm2709_restart, ++ .dt_compat = bcm2709_compat, ++MACHINE_END ++ ++MACHINE_START(BCM2708, "BCM2709") ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .smp = smp_ops(bcm2709_smp_ops), ++ .map_io = bcm2709_map_io, ++ .init_irq = bcm2709_init_irq, ++ .init_time = bcm2709_timer_init, ++ .init_machine = bcm2709_init, ++ .init_early = bcm2709_init_early, ++ .reserve = board_reserve, ++ .restart = bcm2709_restart, ++ .dt_compat = bcm2709_compat, ++MACHINE_END ++ ++module_param(boardrev, uint, 0644); ++module_param(serial, uint, 0644); ++module_param(uart_clock, uint, 0644); ++module_param(disk_led_gpio, uint, 0644); ++module_param(disk_led_active_low, uint, 0644); ++module_param(reboot_part, uint, 0644); ++module_param(w1_gpio_pin, uint, 0644); ++module_param(w1_gpio_pullup, uint, 0644); +diff --git a/arch/arm/mach-bcm2709/bcm2709.h b/arch/arm/mach-bcm2709/bcm2709.h +new file mode 100644 +index 0000000..e339a93 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2709.h +@@ -0,0 +1,49 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708.h ++ * ++ * BCM2708 machine support header ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_BCM2708_H ++#define __BCM2708_BCM2708_H ++ ++#include ++ ++extern void __init bcm2708_init(void); ++extern void __init bcm2708_init_irq(void); ++extern void __init bcm2708_map_io(void); ++extern struct sys_timer bcm2708_timer; ++extern unsigned int mmc_status(struct device *dev); ++ ++#define AMBA_DEVICE(name, busid, base, plat) \ ++static struct amba_device name##_device = { \ ++ .dev = { \ ++ .coherent_dma_mask = ~0, \ ++ .init_name = busid, \ ++ .platform_data = plat, \ ++ }, \ ++ .res = { \ ++ .start = base##_BASE, \ ++ .end = (base##_BASE) + SZ_4K - 1,\ ++ .flags = IORESOURCE_MEM, \ ++ }, \ ++ .irq = base##_IRQ, \ ++} ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/clock.c b/arch/arm/mach-bcm2709/clock.c +new file mode 100644 +index 0000000..4fc556e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/clock.c +@@ -0,0 +1,61 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "clock.h" ++ ++int clk_enable(struct clk *clk) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++void clk_disable(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_disable); ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_round_rate); ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ return -EIO; ++} ++EXPORT_SYMBOL(clk_set_rate); +diff --git a/arch/arm/mach-bcm2709/clock.h b/arch/arm/mach-bcm2709/clock.h +new file mode 100644 +index 0000000..5f9d725 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/clock.h +@@ -0,0 +1,24 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++struct module; ++ ++struct clk { ++ unsigned long rate; ++}; +diff --git a/arch/arm/mach-bcm2709/delay.S b/arch/arm/mach-bcm2709/delay.S +new file mode 100644 +index 0000000..06f4780 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/delay.S +@@ -0,0 +1,21 @@ ++/* ++ * linux/arch/arm/lib/delay.S ++ * ++ * Copyright (C) 1995, 1996 Russell King ++ * ++ * 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 ++#include ++ ++ .text ++.align 3 @ 8 byte alignment seems to be needed to avoid fetching stalls ++@ Delay routine ++ENTRY(bcm2708_delay) ++ subs r0, r0, #1 ++ bhi bcm2708_delay ++ mov pc, lr ++ENDPROC(bcm2708_delay) +diff --git a/arch/arm/mach-bcm2709/dma.c b/arch/arm/mach-bcm2709/dma.c +new file mode 100644 +index 0000000..a5e58d1 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/dma.c +@@ -0,0 +1,409 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/dma.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++/*****************************************************************************\ ++ * * ++ * Configuration * ++ * * ++\*****************************************************************************/ ++ ++#define CACHE_LINE_MASK 31 ++#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME ++#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ ++ ++/* valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */ ++#define BCM2708_DMA_CHANIO(dma_base, n) \ ++ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n))) ++ ++ ++/*****************************************************************************\ ++ * * ++ * DMA Auxilliary Functions * ++ * * ++\*****************************************************************************/ ++ ++/* A DMA buffer on an arbitrary boundary may separate a cache line into a ++ section inside the DMA buffer and another section outside it. ++ Even if we flush DMA buffers from the cache there is always the chance that ++ during a DMA someone will access the part of a cache line that is outside ++ the DMA buffer - which will then bring in unwelcome data. ++ Without being able to dictate our own buffer pools we must insist that ++ DMA buffers consist of a whole number of cache lines. ++*/ ++ ++extern int ++bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) ++{ ++ int i; ++ ++ for (i = 0; i < sg_len; i++) { ++ if (sg_ptr[i].offset & CACHE_LINE_MASK || ++ sg_ptr[i].length & CACHE_LINE_MASK) ++ return 0; ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); ++ ++extern void ++bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block) ++{ ++ dsb(); /* ARM data synchronization (push) operation */ ++ ++ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); ++ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); ++} ++ ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) ++{ ++ dsb(); ++ ++ /* ugly busy wait only option for now */ ++ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE) ++ cpu_relax(); ++} ++ ++EXPORT_SYMBOL_GPL(bcm_dma_start); ++ ++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base) ++{ ++ dsb(); ++ ++ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_is_busy); ++ ++/* Complete an ongoing DMA (assuming its results are to be ignored) ++ Does nothing if there is no DMA in progress. ++ This routine waits for the current AXI transfer to complete before ++ terminating the current DMA. If the current transfer is hung on a DREQ used ++ by an uncooperative peripheral the AXI transfer may never complete. In this ++ case the routine times out and return a non-zero error code. ++ Use of this routine doesn't guarantee that the ongoing or aborted DMA ++ does not produce an interrupt. ++*/ ++extern int ++bcm_dma_abort(void __iomem *dma_chan_base) ++{ ++ unsigned long int cs; ++ int rc = 0; ++ ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (BCM2708_DMA_ACTIVE & cs) { ++ long int timeout = 10000; ++ ++ /* write 0 to the active bit - pause the DMA */ ++ writel(0, dma_chan_base + BCM2708_DMA_CS); ++ ++ /* wait for any current AXI transfer to complete */ ++ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (0 != (cs & BCM2708_DMA_ISPAUSED)) { ++ /* we'll un-pause when we set of our next DMA */ ++ rc = -ETIMEDOUT; ++ ++ } else if (BCM2708_DMA_ACTIVE & cs) { ++ /* terminate the control block chain */ ++ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); ++ ++ /* abort the whole DMA */ ++ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, ++ dma_chan_base + BCM2708_DMA_CS); ++ } ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_abort); ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Device Methods * ++ * * ++\*****************************************************************************/ ++ ++struct vc_dmaman { ++ void __iomem *dma_base; ++ u32 chan_available; /* bitmap of available channels */ ++ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ ++}; ++ ++static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, ++ u32 chans_available) ++{ ++ dmaman->dma_base = dma_base; ++ dmaman->chan_available = chans_available; ++ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ ++} ++ ++static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, ++ unsigned preferred_feature_set) ++{ ++ u32 chans; ++ int feature; ++ ++ chans = dmaman->chan_available; ++ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) ++ /* select the subset of available channels with the desired ++ feature so long as some of the candidate channels have that ++ feature */ ++ if ((preferred_feature_set & (1 << feature)) && ++ (chans & dmaman->has_feature[feature])) ++ chans &= dmaman->has_feature[feature]; ++ ++ if (chans) { ++ int chan = 0; ++ /* return the ordinal of the first channel in the bitmap */ ++ while (chans != 0 && (chans & 1) == 0) { ++ chans >>= 1; ++ chan++; ++ } ++ /* claim the channel */ ++ dmaman->chan_available &= ~(1 << chan); ++ return chan; ++ } else ++ return -ENOMEM; ++} ++ ++static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) ++{ ++ if (chan < 0) ++ return -EINVAL; ++ else if ((1 << chan) & dmaman->chan_available) ++ return -EIDRM; ++ else { ++ dmaman->chan_available |= (1 << chan); ++ return 0; ++ } ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA IRQs * ++ * * ++\*****************************************************************************/ ++ ++static unsigned char bcm_dma_irqs[] = { ++ IRQ_DMA0, ++ IRQ_DMA1, ++ IRQ_DMA2, ++ IRQ_DMA3, ++ IRQ_DMA4, ++ IRQ_DMA5, ++ IRQ_DMA6, ++ IRQ_DMA7, ++ IRQ_DMA8, ++ IRQ_DMA9, ++ IRQ_DMA10, ++ IRQ_DMA11, ++ IRQ_DMA12 ++}; ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Monitor * ++ * * ++\*****************************************************************************/ ++ ++static struct device *dmaman_dev; /* we assume there's only one! */ ++ ++extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, ++ void __iomem **out_dma_base, int *out_dma_irq) ++{ ++ if (!dmaman_dev) ++ return -ENODEV; ++ else { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); ++ if (rc >= 0) { ++ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, ++ rc); ++ *out_dma_irq = bcm_dma_irqs[rc]; ++ } ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); ++ ++extern int bcm_dma_chan_free(int channel) ++{ ++ if (dmaman_dev) { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_free(dmaman, channel); ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_free); ++ ++static int dev_dmaman_register(const char *dev_name, struct device *dev) ++{ ++ int rc = dmaman_dev ? -EINVAL : 0; ++ dmaman_dev = dev; ++ return rc; ++} ++ ++static void dev_dmaman_deregister(const char *dev_name, struct device *dev) ++{ ++ dmaman_dev = NULL; ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA Device * ++ * * ++\*****************************************************************************/ ++ ++static int dmachans = -1; /* module parameter */ ++ ++static int bcm_dmaman_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_dmaman *dmaman; ++ struct resource *dma_res = NULL; ++ void __iomem *dma_base = NULL; ++ int have_dma_region = 0; ++ ++ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); ++ if (NULL == dmaman) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "DMA management memory\n"); ++ ret = -ENOMEM; ++ } else { ++ ++ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (dma_res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ } else if (!request_mem_region(dma_res->start, ++ resource_size(dma_res), ++ DRIVER_NAME)) { ++ dev_err(&pdev->dev, "cannot obtain DMA region\n"); ++ ret = -EBUSY; ++ } else { ++ have_dma_region = 1; ++ dma_base = ioremap(dma_res->start, ++ resource_size(dma_res)); ++ if (!dma_base) { ++ dev_err(&pdev->dev, "cannot map DMA region\n"); ++ ret = -ENOMEM; ++ } else { ++ /* use module parameter if one was provided */ ++ if (dmachans > 0) ++ vc_dmaman_init(dmaman, dma_base, ++ dmachans); ++ else ++ vc_dmaman_init(dmaman, dma_base, ++ DEFAULT_DMACHAN_BITMAP); ++ ++ platform_set_drvdata(pdev, dmaman); ++ dev_dmaman_register(DRIVER_NAME, &pdev->dev); ++ ++ printk(KERN_INFO DRIVER_NAME ": DMA manager " ++ "at %p\n", dma_base); ++ } ++ } ++ } ++ if (ret != 0) { ++ if (dma_base) ++ iounmap(dma_base); ++ if (dma_res && have_dma_region) ++ release_mem_region(dma_res->start, ++ resource_size(dma_res)); ++ if (dmaman) ++ kfree(dmaman); ++ } ++ return ret; ++} ++ ++static int bcm_dmaman_remove(struct platform_device *pdev) ++{ ++ struct vc_dmaman *dmaman = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); ++ kfree(dmaman); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_dmaman_driver = { ++ .probe = bcm_dmaman_probe, ++ .remove = bcm_dmaman_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Driver init/exit * ++ * * ++\*****************************************************************************/ ++ ++static int __init bcm_dmaman_drv_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&bcm_dmaman_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_dmaman_drv_exit(void) ++{ ++ platform_driver_unregister(&bcm_dmaman_driver); ++} ++ ++module_init(bcm_dmaman_drv_init); ++module_exit(bcm_dmaman_drv_exit); ++ ++module_param(dmachans, int, 0644); ++ ++MODULE_AUTHOR("Gray Girling "); ++MODULE_DESCRIPTION("DMA channel manager driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); +diff --git a/arch/arm/mach-bcm2709/dmaer.c b/arch/arm/mach-bcm2709/dmaer.c +new file mode 100755 +index 0000000..5b0f0ff +--- /dev/null ++++ b/arch/arm/mach-bcm2709/dmaer.c +@@ -0,0 +1,886 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#ifdef ECLIPSE_IGNORE ++ ++#define __user ++#define __init ++#define __exit ++#define __iomem ++#define KERN_DEBUG ++#define KERN_ERR ++#define KERN_WARNING ++#define KERN_INFO ++#define _IOWR(a, b, c) b ++#define _IOW(a, b, c) b ++#define _IO(a, b) b ++ ++#endif ++ ++//#define inline ++ ++#define PRINTK(args...) printk(args) ++//#define PRINTK_VERBOSE(args...) printk(args) ++//#define PRINTK(args...) ++#define PRINTK_VERBOSE(args...) ++ ++/***** TYPES ****/ ++#define PAGES_PER_LIST 500 ++struct PageList ++{ ++ struct page *m_pPages[PAGES_PER_LIST]; ++ unsigned int m_used; ++ struct PageList *m_pNext; ++}; ++ ++struct VmaPageList ++{ ++ //each vma has a linked list of pages associated with it ++ struct PageList *m_pPageHead; ++ struct PageList *m_pPageTail; ++ unsigned int m_refCount; ++}; ++ ++struct DmaControlBlock ++{ ++ unsigned int m_transferInfo; ++ void __user *m_pSourceAddr; ++ void __user *m_pDestAddr; ++ unsigned int m_xferLen; ++ unsigned int m_tdStride; ++ struct DmaControlBlock *m_pNext; ++ unsigned int m_blank1, m_blank2; ++}; ++ ++/***** DEFINES ******/ ++//magic number defining the module ++#define DMA_MAGIC 0xdd ++ ++//do user virtual to physical translation of the CB chain ++#define DMA_PREPARE _IOWR(DMA_MAGIC, 0, struct DmaControlBlock *) ++ ++//kick the pre-prepared CB chain ++#define DMA_KICK _IOW(DMA_MAGIC, 1, struct DmaControlBlock *) ++ ++//prepare it, kick it, wait for it ++#define DMA_PREPARE_KICK_WAIT _IOWR(DMA_MAGIC, 2, struct DmaControlBlock *) ++ ++//prepare it, kick it, don't wait for it ++#define DMA_PREPARE_KICK _IOWR(DMA_MAGIC, 3, struct DmaControlBlock *) ++ ++//not currently implemented ++#define DMA_WAIT_ONE _IO(DMA_MAGIC, 4, struct DmaControlBlock *) ++ ++//wait on all kicked CB chains ++#define DMA_WAIT_ALL _IO(DMA_MAGIC, 5) ++ ++//in order to discover the largest AXI burst that should be programmed into the transfer params ++#define DMA_MAX_BURST _IO(DMA_MAGIC, 6) ++ ++//set the address range through which the user address is assumed to already by a physical address ++#define DMA_SET_MIN_PHYS _IOW(DMA_MAGIC, 7, unsigned long) ++#define DMA_SET_MAX_PHYS _IOW(DMA_MAGIC, 8, unsigned long) ++#define DMA_SET_PHYS_OFFSET _IOW(DMA_MAGIC, 9, unsigned long) ++ ++//used to define the size for the CMA-based allocation *in pages*, can only be done once once the file is opened ++#define DMA_CMA_SET_SIZE _IOW(DMA_MAGIC, 10, unsigned long) ++ ++//used to get the version of the module, to test for a capability ++#define DMA_GET_VERSION _IO(DMA_MAGIC, 99) ++ ++#define VERSION_NUMBER 1 ++ ++#define VIRT_TO_BUS_CACHE_SIZE 8 ++ ++/***** FILE OPS *****/ ++static int Open(struct inode *pInode, struct file *pFile); ++static int Release(struct inode *pInode, struct file *pFile); ++static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg); ++static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp); ++static int Mmap(struct file *pFile, struct vm_area_struct *pVma); ++ ++/***** VMA OPS ****/ ++static void VmaOpen4k(struct vm_area_struct *pVma); ++static void VmaClose4k(struct vm_area_struct *pVma); ++static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf); ++ ++/**** DMA PROTOTYPES */ ++static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError); ++static int DmaKick(struct DmaControlBlock __user *pUserCB); ++static void DmaWaitAll(void); ++ ++/**** GENERIC ****/ ++static int __init dmaer_init(void); ++static void __exit dmaer_exit(void); ++ ++/*** OPS ***/ ++static struct vm_operations_struct g_vmOps4k = { ++ .open = VmaOpen4k, ++ .close = VmaClose4k, ++ .fault = VmaFault4k, ++}; ++ ++static struct file_operations g_fOps = { ++ .owner = THIS_MODULE, ++ .llseek = 0, ++ .read = Read, ++ .write = 0, ++ .unlocked_ioctl = Ioctl, ++ .open = Open, ++ .release = Release, ++ .mmap = Mmap, ++}; ++ ++/***** GLOBALS ******/ ++static dev_t g_majorMinor; ++ ++//tracking usage of the two files ++static atomic_t g_oneLock4k = ATOMIC_INIT(1); ++ ++//device operations ++static struct cdev g_cDev; ++static int g_trackedPages = 0; ++ ++//dma control ++static unsigned int *g_pDmaChanBase; ++static int g_dmaIrq; ++static int g_dmaChan; ++ ++//cma allocation ++static int g_cmaHandle; ++ ++//user virtual to bus address translation acceleration ++static unsigned long g_virtAddr[VIRT_TO_BUS_CACHE_SIZE]; ++static unsigned long g_busAddr[VIRT_TO_BUS_CACHE_SIZE]; ++static unsigned long g_cbVirtAddr; ++static unsigned long g_cbBusAddr; ++static int g_cacheInsertAt; ++static int g_cacheHit, g_cacheMiss; ++ ++//off by default ++static void __user *g_pMinPhys; ++static void __user *g_pMaxPhys; ++static unsigned long g_physOffset; ++ ++/****** CACHE OPERATIONS ********/ ++static inline void FlushAddrCache(void) ++{ ++ int count = 0; ++ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) ++ g_virtAddr[count] = 0xffffffff; //never going to match as we always chop the bottom bits anyway ++ ++ g_cbVirtAddr = 0xffffffff; ++ ++ g_cacheInsertAt = 0; ++} ++ ++//translate from a user virtual address to a bus address by mapping the page ++//NB this won't lock a page in memory, so to avoid potential paging issues using kernel logical addresses ++static inline void __iomem *UserVirtualToBus(void __user *pUser) ++{ ++ int mapped; ++ struct page *pPage; ++ void *phys; ++ ++ //map it (requiring that the pointer points to something that does not hang off the page boundary) ++ mapped = get_user_pages(current, current->mm, ++ (unsigned long)pUser, 1, ++ 1, 0, ++ &pPage, ++ 0); ++ ++ if (mapped <= 0) //error ++ return 0; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "user virtual %p arm phys %p bus %p\n", ++ pUser, page_address(pPage), (void __iomem *)__virt_to_bus(page_address(pPage))); ++ ++ //get the arm physical address ++ phys = page_address(pPage) + offset_in_page(pUser); ++ page_cache_release(pPage); ++ ++ //and now the bus address ++ return (void __iomem *)__virt_to_bus(phys); ++} ++ ++static inline void __iomem *UserVirtualToBusViaCbCache(void __user *pUser) ++{ ++ unsigned long virtual_page = (unsigned long)pUser & ~4095; ++ unsigned long page_offset = (unsigned long)pUser & 4095; ++ unsigned long bus_addr; ++ ++ if (g_cbVirtAddr == virtual_page) ++ { ++ bus_addr = g_cbBusAddr + page_offset; ++ g_cacheHit++; ++ return (void __iomem *)bus_addr; ++ } ++ else ++ { ++ bus_addr = (unsigned long)UserVirtualToBus(pUser); ++ ++ if (!bus_addr) ++ return 0; ++ ++ g_cbVirtAddr = virtual_page; ++ g_cbBusAddr = bus_addr & ~4095; ++ g_cacheMiss++; ++ ++ return (void __iomem *)bus_addr; ++ } ++} ++ ++//do the same as above, by query our virt->bus cache ++static inline void __iomem *UserVirtualToBusViaCache(void __user *pUser) ++{ ++ int count; ++ //get the page and its offset ++ unsigned long virtual_page = (unsigned long)pUser & ~4095; ++ unsigned long page_offset = (unsigned long)pUser & 4095; ++ unsigned long bus_addr; ++ ++ if (pUser >= g_pMinPhys && pUser < g_pMaxPhys) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "user->phys passthrough on %p\n", pUser); ++ return (void __iomem *)((unsigned long)pUser + g_physOffset); ++ } ++ ++ //check the cache for our entry ++ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) ++ if (g_virtAddr[count] == virtual_page) ++ { ++ bus_addr = g_busAddr[count] + page_offset; ++ g_cacheHit++; ++ return (void __iomem *)bus_addr; ++ } ++ ++ //not found, look up manually and then insert its page address ++ bus_addr = (unsigned long)UserVirtualToBus(pUser); ++ ++ if (!bus_addr) ++ return 0; ++ ++ g_virtAddr[g_cacheInsertAt] = virtual_page; ++ g_busAddr[g_cacheInsertAt] = bus_addr & ~4095; ++ ++ //round robin ++ g_cacheInsertAt++; ++ if (g_cacheInsertAt == VIRT_TO_BUS_CACHE_SIZE) ++ g_cacheInsertAt = 0; ++ ++ g_cacheMiss++; ++ ++ return (void __iomem *)bus_addr; ++} ++ ++/***** FILE OPERATIONS ****/ ++static int Open(struct inode *pInode, struct file *pFile) ++{ ++ PRINTK(KERN_DEBUG "file opening: %d/%d\n", imajor(pInode), iminor(pInode)); ++ ++ //check which device we are ++ if (iminor(pInode) == 0) //4k ++ { ++ //only one at a time ++ if (!atomic_dec_and_test(&g_oneLock4k)) ++ { ++ atomic_inc(&g_oneLock4k); ++ return -EBUSY; ++ } ++ } ++ else ++ return -EINVAL; ++ ++ //todo there will be trouble if two different processes open the files ++ ++ //reset after any file is opened ++ g_pMinPhys = (void __user *)-1; ++ g_pMaxPhys = (void __user *)0; ++ g_physOffset = 0; ++ g_cmaHandle = 0; ++ ++ return 0; ++} ++ ++static int Release(struct inode *pInode, struct file *pFile) ++{ ++ PRINTK(KERN_DEBUG "file closing, %d pages tracked\n", g_trackedPages); ++ if (g_trackedPages) ++ PRINTK(KERN_ERR "we\'re leaking memory!\n"); ++ ++ //wait for any dmas to finish ++ DmaWaitAll(); ++ ++ //free this memory on the application closing the file or it crashing (implicitly closing the file) ++ if (g_cmaHandle) ++ { ++ PRINTK(KERN_DEBUG "unlocking vc memory\n"); ++ if (UnlockVcMemory(g_cmaHandle)) ++ PRINTK(KERN_ERR "uh-oh, unable to unlock vc memory!\n"); ++ PRINTK(KERN_DEBUG "releasing vc memory\n"); ++ if (ReleaseVcMemory(g_cmaHandle)) ++ PRINTK(KERN_ERR "uh-oh, unable to release vc memory!\n"); ++ } ++ ++ if (iminor(pInode) == 0) ++ atomic_inc(&g_oneLock4k); ++ else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError) ++{ ++ struct DmaControlBlock kernCB; ++ struct DmaControlBlock __user *pUNext; ++ void __iomem *pSourceBus, __iomem *pDestBus; ++ ++ //get the control block into kernel memory so we can work on it ++ if (copy_from_user(&kernCB, pUserCB, sizeof(struct DmaControlBlock)) != 0) ++ { ++ PRINTK(KERN_ERR "copy_from_user failed for user cb %p\n", pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ if (kernCB.m_pSourceAddr == 0 || kernCB.m_pDestAddr == 0) ++ { ++ PRINTK(KERN_ERR "faulty source (%p) dest (%p) addresses for user cb %p\n", ++ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ pSourceBus = UserVirtualToBusViaCache(kernCB.m_pSourceAddr); ++ pDestBus = UserVirtualToBusViaCache(kernCB.m_pDestAddr); ++ ++ if (!pSourceBus || !pDestBus) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for source/dest %p/%p->%p/%p\n", ++ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, ++ pSourceBus, pDestBus); ++ *pError = 1; ++ return 0; ++ } ++ ++ //update the user structure with the new bus addresses ++ kernCB.m_pSourceAddr = pSourceBus; ++ kernCB.m_pDestAddr = pDestBus; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "final source %p dest %p\n", kernCB.m_pSourceAddr, kernCB.m_pDestAddr); ++ ++ //sort out the bus address for the next block ++ pUNext = kernCB.m_pNext; ++ ++ if (kernCB.m_pNext) ++ { ++ void __iomem *pNextBus; ++ pNextBus = UserVirtualToBusViaCbCache(kernCB.m_pNext); ++ ++ if (!pNextBus) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for m_pNext\n"); ++ *pError = 1; ++ return 0; ++ } ++ ++ //update the pointer with the bus address ++ kernCB.m_pNext = pNextBus; ++ } ++ ++ //write it back to user space ++ if (copy_to_user(pUserCB, &kernCB, sizeof(struct DmaControlBlock)) != 0) ++ { ++ PRINTK(KERN_ERR "copy_to_user failed for cb %p\n", pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ __cpuc_flush_dcache_area(pUserCB, 32); ++ ++ *pError = 0; ++ return pUNext; ++} ++ ++static int DmaKick(struct DmaControlBlock __user *pUserCB) ++{ ++ void __iomem *pBusCB; ++ ++ pBusCB = UserVirtualToBusViaCbCache(pUserCB); ++ if (!pBusCB) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for cb\n"); ++ return 1; ++ } ++ ++ //flush_cache_all(); ++ ++ bcm_dma_start(g_pDmaChanBase, (dma_addr_t)pBusCB); ++ ++ return 0; ++} ++ ++static void DmaWaitAll(void) ++{ ++ int counter = 0; ++ volatile int inner_count; ++ volatile unsigned int cs; ++ unsigned long time_before, time_after; ++ ++ time_before = jiffies; ++ //bcm_dma_wait_idle(g_pDmaChanBase); ++ dsb(); ++ ++ cs = readl(g_pDmaChanBase); ++ ++ while ((cs & 1) == 1) ++ { ++ cs = readl(g_pDmaChanBase); ++ counter++; ++ ++ for (inner_count = 0; inner_count < 32; inner_count++); ++ ++ asm volatile ("MCR p15,0,r0,c7,c0,4 \n"); ++ //cpu_do_idle(); ++ if (counter >= 1000000) ++ { ++ PRINTK(KERN_WARNING "DMA failed to finish in a timely fashion\n"); ++ break; ++ } ++ } ++ time_after = jiffies; ++ PRINTK_VERBOSE(KERN_DEBUG "done, counter %d, cs %08x", counter, cs); ++ PRINTK_VERBOSE(KERN_DEBUG "took %ld jiffies, %d HZ\n", time_after - time_before, HZ); ++} ++ ++static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg) ++{ ++ int error = 0; ++ PRINTK_VERBOSE(KERN_DEBUG "ioctl cmd %x arg %lx\n", cmd, arg); ++ ++ switch (cmd) ++ { ++ case DMA_PREPARE: ++ case DMA_PREPARE_KICK: ++ case DMA_PREPARE_KICK_WAIT: ++ { ++ struct DmaControlBlock __user *pUCB = (struct DmaControlBlock *)arg; ++ int steps = 0; ++ unsigned long start_time = jiffies; ++ (void)start_time; ++ ++ //flush our address cache ++ FlushAddrCache(); ++ ++ PRINTK_VERBOSE(KERN_DEBUG "dma prepare\n"); ++ ++ //do virtual to bus translation for each entry ++ do ++ { ++ pUCB = DmaPrepare(pUCB, &error); ++ } while (error == 0 && ++steps && pUCB); ++ PRINTK_VERBOSE(KERN_DEBUG "prepare done in %d steps, %ld\n", steps, jiffies - start_time); ++ ++ //carry straight on if we want to kick too ++ if (cmd == DMA_PREPARE || error) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "falling out\n"); ++ return error ? -EINVAL : 0; ++ } ++ } ++ case DMA_KICK: ++ PRINTK_VERBOSE(KERN_DEBUG "dma begin\n"); ++ ++ if (cmd == DMA_KICK) ++ FlushAddrCache(); ++ ++ DmaKick((struct DmaControlBlock __user *)arg); ++ ++ if (cmd != DMA_PREPARE_KICK_WAIT) ++ break; ++/* case DMA_WAIT_ONE: ++ //PRINTK(KERN_DEBUG "dma wait one\n"); ++ break;*/ ++ case DMA_WAIT_ALL: ++ //PRINTK(KERN_DEBUG "dma wait all\n"); ++ DmaWaitAll(); ++ break; ++ case DMA_MAX_BURST: ++ if (g_dmaChan == 0) ++ return 10; ++ else ++ return 5; ++ case DMA_SET_MIN_PHYS: ++ g_pMinPhys = (void __user *)arg; ++ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); ++ break; ++ case DMA_SET_MAX_PHYS: ++ g_pMaxPhys = (void __user *)arg; ++ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); ++ break; ++ case DMA_SET_PHYS_OFFSET: ++ g_physOffset = arg; ++ PRINTK(KERN_DEBUG "user/phys bypass offset set to %ld\n", g_physOffset); ++ break; ++ case DMA_CMA_SET_SIZE: ++ { ++ unsigned int pBusAddr; ++ ++ if (g_cmaHandle) ++ { ++ PRINTK(KERN_ERR "memory has already been allocated (handle %d)\n", g_cmaHandle); ++ return -EINVAL; ++ } ++ ++ PRINTK(KERN_INFO "allocating %ld bytes of VC memory\n", arg * 4096); ++ ++ //get the memory ++ if (AllocateVcMemory(&g_cmaHandle, arg * 4096, 4096, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_NO_INIT | MEM_FLAG_HINT_PERMALOCK)) ++ { ++ PRINTK(KERN_ERR "failed to allocate %ld bytes of VC memory\n", arg * 4096); ++ g_cmaHandle = 0; ++ return -EINVAL; ++ } ++ ++ //get an address for it ++ PRINTK(KERN_INFO "trying to map VC memory\n"); ++ ++ if (LockVcMemory(&pBusAddr, g_cmaHandle)) ++ { ++ PRINTK(KERN_ERR "failed to map CMA handle %d, releasing memory\n", g_cmaHandle); ++ ReleaseVcMemory(g_cmaHandle); ++ g_cmaHandle = 0; ++ } ++ ++ PRINTK(KERN_INFO "bus address for CMA memory is %x\n", pBusAddr); ++ return pBusAddr; ++ } ++ case DMA_GET_VERSION: ++ PRINTK(KERN_DEBUG "returning version number, %d\n", VERSION_NUMBER); ++ return VERSION_NUMBER; ++ default: ++ PRINTK(KERN_DEBUG "unknown ioctl: %d\n", cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp) ++{ ++ return -EIO; ++} ++ ++static int Mmap(struct file *pFile, struct vm_area_struct *pVma) ++{ ++ struct PageList *pPages; ++ struct VmaPageList *pVmaList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "MMAP vma %p, length %ld (%s %d)\n", ++ pVma, pVma->vm_end - pVma->vm_start, ++ current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_DEBUG "MMAP %p %d (tracked %d)\n", pVma, current->pid, g_trackedPages); ++ ++ //make a new page list ++ pPages = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); ++ if (!pPages) ++ { ++ PRINTK(KERN_ERR "couldn\'t allocate a new page list (%s %d)\n", ++ current->comm, current->pid); ++ return -ENOMEM; ++ } ++ ++ //clear the page list ++ pPages->m_used = 0; ++ pPages->m_pNext = 0; ++ ++ //insert our vma and new page list somewhere ++ if (!pVma->vm_private_data) ++ { ++ struct VmaPageList *pList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "new vma list, making new one (%s %d)\n", ++ current->comm, current->pid); ++ ++ //make a new vma list ++ pList = (struct VmaPageList *)kmalloc(sizeof(struct VmaPageList), GFP_KERNEL); ++ if (!pList) ++ { ++ PRINTK(KERN_ERR "couldn\'t allocate vma page list (%s %d)\n", ++ current->comm, current->pid); ++ kfree(pPages); ++ return -ENOMEM; ++ } ++ ++ //clear this list ++ pVma->vm_private_data = (void *)pList; ++ pList->m_refCount = 0; ++ } ++ ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ //add it to the vma list ++ pVmaList->m_pPageHead = pPages; ++ pVmaList->m_pPageTail = pPages; ++ ++ pVma->vm_ops = &g_vmOps4k; ++ pVma->vm_flags |= VM_IO; ++ ++ VmaOpen4k(pVma); ++ ++ return 0; ++} ++ ++/****** VMA OPERATIONS ******/ ++ ++static void VmaOpen4k(struct vm_area_struct *pVma) ++{ ++ struct VmaPageList *pVmaList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "vma open %p private %p (%s %d), %d live pages\n", pVma, pVma->vm_private_data, current->comm, current->pid, g_trackedPages); ++ PRINTK_VERBOSE(KERN_DEBUG "OPEN %p %d %ld pages (tracked pages %d)\n", ++ pVma, current->pid, (pVma->vm_end - pVma->vm_start) >> 12, ++ g_trackedPages); ++ ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ if (pVmaList) ++ { ++ pVmaList->m_refCount++; ++ PRINTK_VERBOSE(KERN_DEBUG "ref count is now %d\n", pVmaList->m_refCount); ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "err, open but no vma page list\n"); ++ } ++} ++ ++static void VmaClose4k(struct vm_area_struct *pVma) ++{ ++ struct VmaPageList *pVmaList; ++ int freed = 0; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "vma close %p private %p (%s %d)\n", pVma, pVma->vm_private_data, current->comm, current->pid); ++ ++ //wait for any dmas to finish ++ DmaWaitAll(); ++ ++ //find our vma in the list ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ //may be a fork ++ if (pVmaList) ++ { ++ struct PageList *pPages; ++ ++ pVmaList->m_refCount--; ++ ++ if (pVmaList->m_refCount == 0) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "found vma, freeing pages (%s %d)\n", ++ current->comm, current->pid); ++ ++ pPages = pVmaList->m_pPageHead; ++ ++ if (!pPages) ++ { ++ PRINTK(KERN_ERR "no page list (%s %d)!\n", ++ current->comm, current->pid); ++ return; ++ } ++ ++ while (pPages) ++ { ++ struct PageList *next; ++ int count; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "page list (%s %d)\n", ++ current->comm, current->pid); ++ ++ next = pPages->m_pNext; ++ for (count = 0; count < pPages->m_used; count++) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "freeing page %p (%s %d)\n", ++ pPages->m_pPages[count], ++ current->comm, current->pid); ++ __free_pages(pPages->m_pPages[count], 0); ++ g_trackedPages--; ++ freed++; ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "freeing page list (%s %d)\n", ++ current->comm, current->pid); ++ kfree(pPages); ++ pPages = next; ++ } ++ ++ //remove our vma from the list ++ kfree(pVmaList); ++ pVma->vm_private_data = 0; ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "ref count is %d, not closing\n", pVmaList->m_refCount); ++ } ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_ERR "uh-oh, vma %p not found (%s %d)!\n", pVma, current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_ERR "CLOSE ERR\n"); ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "CLOSE %p %d %d pages (tracked pages %d)", ++ pVma, current->pid, freed, g_trackedPages); ++ ++ PRINTK_VERBOSE(KERN_DEBUG "%d pages open\n", g_trackedPages); ++} ++ ++static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf) ++{ ++ PRINTK_VERBOSE(KERN_DEBUG "vma fault for vma %p private %p at offset %ld (%s %d)\n", pVma, pVma->vm_private_data, pVmf->pgoff, ++ current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_DEBUG "FAULT\n"); ++ pVmf->page = alloc_page(GFP_KERNEL); ++ ++ if (pVmf->page) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "alloc page virtual %p\n", page_address(pVmf->page)); ++ } ++ ++ if (!pVmf->page) ++ { ++ PRINTK(KERN_ERR "vma fault oom (%s %d)\n", current->comm, current->pid); ++ return VM_FAULT_OOM; ++ } ++ else ++ { ++ struct VmaPageList *pVmaList; ++ ++ get_page(pVmf->page); ++ g_trackedPages++; ++ ++ //find our vma in the list ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ if (pVmaList) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "vma found (%s %d)\n", current->comm, current->pid); ++ ++ if (pVmaList->m_pPageTail->m_used == PAGES_PER_LIST) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "making new page list (%s %d)\n", current->comm, current->pid); ++ //making a new page list ++ pVmaList->m_pPageTail->m_pNext = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); ++ if (!pVmaList->m_pPageTail->m_pNext) ++ return -ENOMEM; ++ ++ //update the tail pointer ++ pVmaList->m_pPageTail = pVmaList->m_pPageTail->m_pNext; ++ pVmaList->m_pPageTail->m_used = 0; ++ pVmaList->m_pPageTail->m_pNext = 0; ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "adding page to list (%s %d)\n", current->comm, current->pid); ++ ++ pVmaList->m_pPageTail->m_pPages[pVmaList->m_pPageTail->m_used] = pVmf->page; ++ pVmaList->m_pPageTail->m_used++; ++ } ++ else ++ PRINTK(KERN_ERR "returned page for vma we don\'t know %p (%s %d)\n", pVma, current->comm, current->pid); ++ ++ return 0; ++ } ++} ++ ++/****** GENERIC FUNCTIONS ******/ ++static int __init dmaer_init(void) ++{ ++ int result = alloc_chrdev_region(&g_majorMinor, 0, 1, "dmaer"); ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "unable to get major device number\n"); ++ return result; ++ } ++ else ++ PRINTK(KERN_DEBUG "major device number %d\n", MAJOR(g_majorMinor)); ++ ++ PRINTK(KERN_DEBUG "vma list size %d, page list size %d, page size %ld\n", ++ sizeof(struct VmaPageList), sizeof(struct PageList), PAGE_SIZE); ++ ++ //get a dma channel to work with ++ result = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, (void **)&g_pDmaChanBase, &g_dmaIrq); ++ ++ //uncomment to force to channel 0 ++ //result = 0; ++ //g_pDmaChanBase = 0xce808000; ++ ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "failed to allocate dma channel\n"); ++ cdev_del(&g_cDev); ++ unregister_chrdev_region(g_majorMinor, 1); ++ } ++ ++ //reset the channel ++ PRINTK(KERN_DEBUG "allocated dma channel %d (%p), initial state %08x\n", result, g_pDmaChanBase, *g_pDmaChanBase); ++ *g_pDmaChanBase = 1 << 31; ++ PRINTK(KERN_DEBUG "post-reset %08x\n", *g_pDmaChanBase); ++ ++ g_dmaChan = result; ++ ++ //clear the cache stats ++ g_cacheHit = 0; ++ g_cacheMiss = 0; ++ ++ //register our device - after this we are go go go ++ cdev_init(&g_cDev, &g_fOps); ++ g_cDev.owner = THIS_MODULE; ++ g_cDev.ops = &g_fOps; ++ ++ result = cdev_add(&g_cDev, g_majorMinor, 1); ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "failed to add character device\n"); ++ unregister_chrdev_region(g_majorMinor, 1); ++ bcm_dma_chan_free(g_dmaChan); ++ return result; ++ } ++ ++ return 0; ++} ++ ++static void __exit dmaer_exit(void) ++{ ++ PRINTK(KERN_INFO "closing dmaer device, cache stats: %d hits %d misses\n", g_cacheHit, g_cacheMiss); ++ //unregister the device ++ cdev_del(&g_cDev); ++ unregister_chrdev_region(g_majorMinor, 1); ++ //free the dma channel ++ bcm_dma_chan_free(g_dmaChan); ++} ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Simon Hall"); ++module_init(dmaer_init); ++module_exit(dmaer_exit); +diff --git a/arch/arm/mach-bcm2709/include/mach/arm_control.h b/arch/arm/mach-bcm2709/include/mach/arm_control.h +new file mode 100644 +index 0000000..e346caf +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/arm_control.h +@@ -0,0 +1,493 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/arm_control.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARM_CONTROL_H ++#define __BCM2708_ARM_CONTROL_H ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define ARM_BASE 0x7E00B000 ++ ++/* Basic configuration */ ++#define ARM_CONTROL0 HW_REGISTER_RW(ARM_BASE+0x000) ++#define ARM_C0_SIZ128M 0x00000000 ++#define ARM_C0_SIZ256M 0x00000001 ++#define ARM_C0_SIZ512M 0x00000002 ++#define ARM_C0_SIZ1G 0x00000003 ++#define ARM_C0_BRESP0 0x00000000 ++#define ARM_C0_BRESP1 0x00000004 ++#define ARM_C0_BRESP2 0x00000008 ++#define ARM_C0_BOOTHI 0x00000010 ++#define ARM_C0_UNUSED05 0x00000020 /* free */ ++#define ARM_C0_FULLPERI 0x00000040 ++#define ARM_C0_UNUSED78 0x00000180 /* free */ ++#define ARM_C0_JTAGMASK 0x00000E00 ++#define ARM_C0_JTAGOFF 0x00000000 ++#define ARM_C0_JTAGBASH 0x00000800 /* Debug on GPIO off */ ++#define ARM_C0_JTAGGPIO 0x00000C00 /* Debug on GPIO on */ ++#define ARM_C0_APROTMSK 0x0000F000 ++#define ARM_C0_DBG0SYNC 0x00010000 /* VPU0 halt sync */ ++#define ARM_C0_DBG1SYNC 0x00020000 /* VPU1 halt sync */ ++#define ARM_C0_SWDBGREQ 0x00040000 /* HW debug request */ ++#define ARM_C0_PASSHALT 0x00080000 /* ARM halt passed to debugger */ ++#define ARM_C0_PRIO_PER 0x00F00000 /* per priority mask */ ++#define ARM_C0_PRIO_L2 0x0F000000 ++#define ARM_C0_PRIO_UC 0xF0000000 ++ ++#define ARM_C0_APROTPASS 0x0000A000 /* Translate 1:1 */ ++#define ARM_C0_APROTUSER 0x00000000 /* Only user mode */ ++#define ARM_C0_APROTSYST 0x0000F000 /* Only system mode */ ++ ++ ++#define ARM_CONTROL1 HW_REGISTER_RW(ARM_BASE+0x440) ++#define ARM_C1_TIMER 0x00000001 /* re-route timer IRQ to VC */ ++#define ARM_C1_MAIL 0x00000002 /* re-route Mail IRQ to VC */ ++#define ARM_C1_BELL0 0x00000004 /* re-route Doorbell 0 to VC */ ++#define ARM_C1_BELL1 0x00000008 /* re-route Doorbell 1 to VC */ ++#define ARM_C1_PERSON 0x00000100 /* peripherals on */ ++#define ARM_C1_REQSTOP 0x00000200 /* ASYNC bridge request stop */ ++ ++#define ARM_STATUS HW_REGISTER_RW(ARM_BASE+0x444) ++#define ARM_S_ACKSTOP 0x80000000 /* Bridge stopped */ ++#define ARM_S_READPEND 0x000003FF /* pending reads counter */ ++#define ARM_S_WRITPEND 0x000FFC00 /* pending writes counter */ ++ ++#define ARM_ERRHALT HW_REGISTER_RW(ARM_BASE+0x448) ++#define ARM_EH_PERIBURST 0x00000001 /* Burst write seen on peri bus */ ++#define ARM_EH_ILLADDRS1 0x00000002 /* Address bits 25-27 error */ ++#define ARM_EH_ILLADDRS2 0x00000004 /* Address bits 31-28 error */ ++#define ARM_EH_VPU0HALT 0x00000008 /* VPU0 halted & in debug mode */ ++#define ARM_EH_VPU1HALT 0x00000010 /* VPU1 halted & in debug mode */ ++#define ARM_EH_ARMHALT 0x00000020 /* ARM in halted debug mode */ ++ ++#define ARM_ID_SECURE HW_REGISTER_RW(ARM_BASE+0x00C) ++#define ARM_ID HW_REGISTER_RW(ARM_BASE+0x44C) ++#define ARM_IDVAL 0x364D5241 ++ ++/* Translation memory */ ++#define ARM_TRANSLATE HW_REGISTER_RW(ARM_BASE+0x100) ++/* 32 locations: 0x100.. 0x17F */ ++/* 32 spare means we CAN go to 64 pages.... */ ++ ++ ++/* Interrupts */ ++#define ARM_IRQ_PEND0 HW_REGISTER_RW(ARM_BASE+0x200) /* Top IRQ bits */ ++#define ARM_I0_TIMER 0x00000001 /* timer IRQ */ ++#define ARM_I0_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_I0_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_I0_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_I0_BANK1 0x00000100 /* Bank1 IRQ */ ++#define ARM_I0_BANK2 0x00000200 /* Bank2 IRQ */ ++ ++#define ARM_IRQ_PEND1 HW_REGISTER_RW(ARM_BASE+0x204) /* All bank1 IRQ bits */ ++/* todo: all I1_interrupt sources */ ++#define ARM_IRQ_PEND2 HW_REGISTER_RW(ARM_BASE+0x208) /* All bank2 IRQ bits */ ++/* todo: all I2_interrupt sources */ ++ ++#define ARM_IRQ_FAST HW_REGISTER_RW(ARM_BASE+0x20C) /* FIQ control */ ++#define ARM_IF_INDEX 0x0000007F /* FIQ select */ ++#define ARM_IF_ENABLE 0x00000080 /* FIQ enable */ ++#define ARM_IF_VCMASK 0x0000003F /* FIQ = (index from VC source) */ ++#define ARM_IF_TIMER 0x00000040 /* FIQ = ARM timer */ ++#define ARM_IF_MAIL 0x00000041 /* FIQ = ARM Mail */ ++#define ARM_IF_BELL0 0x00000042 /* FIQ = ARM Doorbell 0 */ ++#define ARM_IF_BELL1 0x00000043 /* FIQ = ARM Doorbell 1 */ ++#define ARM_IF_VP0HALT 0x00000044 /* FIQ = VPU0 Halt seen */ ++#define ARM_IF_VP1HALT 0x00000045 /* FIQ = VPU1 Halt seen */ ++#define ARM_IF_ILLEGAL 0x00000046 /* FIQ = Illegal access seen */ ++ ++#define ARM_IRQ_ENBL1 HW_REGISTER_RW(ARM_BASE+0x210) /* Bank1 enable bits */ ++#define ARM_IRQ_ENBL2 HW_REGISTER_RW(ARM_BASE+0x214) /* Bank2 enable bits */ ++#define ARM_IRQ_ENBL3 HW_REGISTER_RW(ARM_BASE+0x218) /* ARM irqs enable bits */ ++#define ARM_IRQ_DIBL1 HW_REGISTER_RW(ARM_BASE+0x21C) /* Bank1 disable bits */ ++#define ARM_IRQ_DIBL2 HW_REGISTER_RW(ARM_BASE+0x220) /* Bank2 disable bits */ ++#define ARM_IRQ_DIBL3 HW_REGISTER_RW(ARM_BASE+0x224) /* ARM irqs disable bits */ ++#define ARM_IE_TIMER 0x00000001 /* Timer IRQ */ ++#define ARM_IE_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_IE_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_IE_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_IE_VP0HALT 0x00000010 /* VPU0 Halt */ ++#define ARM_IE_VP1HALT 0x00000020 /* VPU1 Halt */ ++#define ARM_IE_ILLEGAL 0x00000040 /* Illegal access seen */ ++ ++/* Timer */ ++/* For reg. fields see sp804 spec. */ ++#define ARM_T_LOAD HW_REGISTER_RW(ARM_BASE+0x400) ++#define ARM_T_VALUE HW_REGISTER_RW(ARM_BASE+0x404) ++#define ARM_T_CONTROL HW_REGISTER_RW(ARM_BASE+0x408) ++#define ARM_T_IRQCNTL HW_REGISTER_RW(ARM_BASE+0x40C) ++#define ARM_T_RAWIRQ HW_REGISTER_RW(ARM_BASE+0x410) ++#define ARM_T_MSKIRQ HW_REGISTER_RW(ARM_BASE+0x414) ++#define ARM_T_RELOAD HW_REGISTER_RW(ARM_BASE+0x418) ++#define ARM_T_PREDIV HW_REGISTER_RW(ARM_BASE+0x41c) ++#define ARM_T_FREECNT HW_REGISTER_RW(ARM_BASE+0x420) ++ ++#define TIMER_CTRL_ONESHOT (1 << 0) ++#define TIMER_CTRL_32BIT (1 << 1) ++#define TIMER_CTRL_DIV1 (0 << 2) ++#define TIMER_CTRL_DIV16 (1 << 2) ++#define TIMER_CTRL_DIV256 (2 << 2) ++#define TIMER_CTRL_IE (1 << 5) ++#define TIMER_CTRL_PERIODIC (1 << 6) ++#define TIMER_CTRL_ENABLE (1 << 7) ++#define TIMER_CTRL_DBGHALT (1 << 8) ++#define TIMER_CTRL_ENAFREE (1 << 9) ++#define TIMER_CTRL_FREEDIV_SHIFT 16) ++#define TIMER_CTRL_FREEDIV_MASK 0xff ++ ++/* Semaphores, Doorbells, Mailboxes */ ++#define ARM_SBM_OWN0 (ARM_BASE+0x800) ++#define ARM_SBM_OWN1 (ARM_BASE+0x900) ++#define ARM_SBM_OWN2 (ARM_BASE+0xA00) ++#define ARM_SBM_OWN3 (ARM_BASE+0xB00) ++ ++/* MAILBOXES ++ * Register flags are common across all ++ * owner registers. See end of this section ++ * ++ * Semaphores, Doorbells, Mailboxes Owner 0 ++ * ++ */ ++ ++#define ARM_0_SEMS HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM0 HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM1 HW_REGISTER_RW(ARM_SBM_OWN0+0x04) ++#define ARM_0_SEM2 HW_REGISTER_RW(ARM_SBM_OWN0+0x08) ++#define ARM_0_SEM3 HW_REGISTER_RW(ARM_SBM_OWN0+0x0C) ++#define ARM_0_SEM4 HW_REGISTER_RW(ARM_SBM_OWN0+0x10) ++#define ARM_0_SEM5 HW_REGISTER_RW(ARM_SBM_OWN0+0x14) ++#define ARM_0_SEM6 HW_REGISTER_RW(ARM_SBM_OWN0+0x18) ++#define ARM_0_SEM7 HW_REGISTER_RW(ARM_SBM_OWN0+0x1C) ++#define ARM_0_BELL0 HW_REGISTER_RW(ARM_SBM_OWN0+0x40) ++#define ARM_0_BELL1 HW_REGISTER_RW(ARM_SBM_OWN0+0x44) ++#define ARM_0_BELL2 HW_REGISTER_RW(ARM_SBM_OWN0+0x48) ++#define ARM_0_BELL3 HW_REGISTER_RW(ARM_SBM_OWN0+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Some addresses should ONLY be used by owner 0 */ ++#define ARM_0_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) */ ++#define ARM_0_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) Normal read */ ++#define ARM_0_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN0+0x90) /* none-pop read */ ++#define ARM_0_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN0+0x94) /* Sender read (only LS 2 bits) */ ++#define ARM_0_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN0+0x98) /* Status read */ ++#define ARM_0_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0x9C) /* Config read/write */ ++/* MAILBOX 1 access in Owner 0 area */ ++/* Owner 0 should only WRITE to this mailbox */ ++#define ARM_0_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_0_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN0+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN0+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_0_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN0+0xB8) /* Status read */ ++/*#define ARM_0_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_0_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE0) /* semaphore clear/debug register */ ++#define ARM_0_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE4) /* Doorbells clear/debug register */ ++#define ARM_0_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xF8) /* ALL interrupts */ ++#define ARM_0_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xFC) /* IRQS pending for owner 0 */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 1 */ ++#define ARM_1_SEMS HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM0 HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM1 HW_REGISTER_RW(ARM_SBM_OWN1+0x04) ++#define ARM_1_SEM2 HW_REGISTER_RW(ARM_SBM_OWN1+0x08) ++#define ARM_1_SEM3 HW_REGISTER_RW(ARM_SBM_OWN1+0x0C) ++#define ARM_1_SEM4 HW_REGISTER_RW(ARM_SBM_OWN1+0x10) ++#define ARM_1_SEM5 HW_REGISTER_RW(ARM_SBM_OWN1+0x14) ++#define ARM_1_SEM6 HW_REGISTER_RW(ARM_SBM_OWN1+0x18) ++#define ARM_1_SEM7 HW_REGISTER_RW(ARM_SBM_OWN1+0x1C) ++#define ARM_1_BELL0 HW_REGISTER_RW(ARM_SBM_OWN1+0x40) ++#define ARM_1_BELL1 HW_REGISTER_RW(ARM_SBM_OWN1+0x44) ++#define ARM_1_BELL2 HW_REGISTER_RW(ARM_SBM_OWN1+0x48) ++#define ARM_1_BELL3 HW_REGISTER_RW(ARM_SBM_OWN1+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Owner 1 should only WRITE to this mailbox */ ++#define ARM_1_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_1_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN1+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN1+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN1+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_1_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN1+0x98) /* Status read */ ++/*#define ARM_1_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 0 area */ ++#define ARM_1_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) */ ++#define ARM_1_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) Normal read */ ++#define ARM_1_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN1+0xB0) /* none-pop read */ ++#define ARM_1_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN1+0xB4) /* Sender read (only LS 2 bits) */ ++#define ARM_1_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN1+0xB8) /* Status read */ ++#define ARM_1_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0xBC) ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_1_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE0) /* semaphore clear/debug register */ ++#define ARM_1_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE4) /* Doorbells clear/debug register */ ++#define ARM_1_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xFC) /* IRQS pending for owner 1 */ ++#define ARM_1_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 2 */ ++#define ARM_2_SEMS HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM0 HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM1 HW_REGISTER_RW(ARM_SBM_OWN2+0x04) ++#define ARM_2_SEM2 HW_REGISTER_RW(ARM_SBM_OWN2+0x08) ++#define ARM_2_SEM3 HW_REGISTER_RW(ARM_SBM_OWN2+0x0C) ++#define ARM_2_SEM4 HW_REGISTER_RW(ARM_SBM_OWN2+0x10) ++#define ARM_2_SEM5 HW_REGISTER_RW(ARM_SBM_OWN2+0x14) ++#define ARM_2_SEM6 HW_REGISTER_RW(ARM_SBM_OWN2+0x18) ++#define ARM_2_SEM7 HW_REGISTER_RW(ARM_SBM_OWN2+0x1C) ++#define ARM_2_BELL0 HW_REGISTER_RW(ARM_SBM_OWN2+0x40) ++#define ARM_2_BELL1 HW_REGISTER_RW(ARM_SBM_OWN2+0x44) ++#define ARM_2_BELL2 HW_REGISTER_RW(ARM_SBM_OWN2+0x48) ++#define ARM_2_BELL3 HW_REGISTER_RW(ARM_SBM_OWN2+0x4C) ++/* MAILBOX 0 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_2_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN2+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN2+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN2+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN2+0x98) /* Status read */ ++/*#define ARM_2_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_2_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN2+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN2+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN2+0xB8) /* Status read */ ++/*#define ARM_2_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_2_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE0) /* semaphore clear/debug register */ ++#define ARM_2_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE4) /* Doorbells clear/debug register */ ++#define ARM_2_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xFC) /* IRQS pending for owner 2 */ ++#define ARM_2_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 3 */ ++#define ARM_3_SEMS HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM0 HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM1 HW_REGISTER_RW(ARM_SBM_OWN3+0x04) ++#define ARM_3_SEM2 HW_REGISTER_RW(ARM_SBM_OWN3+0x08) ++#define ARM_3_SEM3 HW_REGISTER_RW(ARM_SBM_OWN3+0x0C) ++#define ARM_3_SEM4 HW_REGISTER_RW(ARM_SBM_OWN3+0x10) ++#define ARM_3_SEM5 HW_REGISTER_RW(ARM_SBM_OWN3+0x14) ++#define ARM_3_SEM6 HW_REGISTER_RW(ARM_SBM_OWN3+0x18) ++#define ARM_3_SEM7 HW_REGISTER_RW(ARM_SBM_OWN3+0x1C) ++#define ARM_3_BELL0 HW_REGISTER_RW(ARM_SBM_OWN3+0x40) ++#define ARM_3_BELL1 HW_REGISTER_RW(ARM_SBM_OWN3+0x44) ++#define ARM_3_BELL2 HW_REGISTER_RW(ARM_SBM_OWN3+0x48) ++#define ARM_3_BELL3 HW_REGISTER_RW(ARM_SBM_OWN3+0x4C) ++/* MAILBOX 0 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_3_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN3+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN3+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN3+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN3+0x98) /* Status read */ ++/*#define ARM_3_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_3_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN3+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN3+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN3+0xB8) /* Status read */ ++/*#define ARM_3_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_3_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE0) /* semaphore clear/debug register */ ++#define ARM_3_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE4) /* Doorbells clear/debug register */ ++#define ARM_3_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xFC) /* IRQS pending for owner 3 */ ++#define ARM_3_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xF8) /* ALL interrupts */ ++ ++ ++ ++/* Mailbox flags. Valid for all owners */ ++ ++/* Mailbox status register (...0x98) */ ++#define ARM_MS_FULL 0x80000000 ++#define ARM_MS_EMPTY 0x40000000 ++#define ARM_MS_LEVEL 0x400000FF /* Max. value depdnds on mailbox depth parameter */ ++ ++/* MAILBOX config/status register (...0x9C) */ ++/* ANY write to this register clears the error bits! */ ++#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mailbox irq enable: has data */ ++#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mailbox irq enable: has space */ ++#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mailbox irq enable: Opp. is empty */ ++#define ARM_MC_MAIL_CLEAR 0x00000008 /* mailbox clear write 1, then 0 */ ++#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mailbox irq pending: has space */ ++#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mailbox irq pending: Opp. is empty */ ++#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mailbox irq pending */ ++/* Bit 7 is unused */ ++#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ ++#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ ++#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ ++ ++/* Semaphore clear/debug register (...0xE0) */ ++#define ARM_SD_OWN0 0x00000003 /* Owner of sem 0 */ ++#define ARM_SD_OWN1 0x0000000C /* Owner of sem 1 */ ++#define ARM_SD_OWN2 0x00000030 /* Owner of sem 2 */ ++#define ARM_SD_OWN3 0x000000C0 /* Owner of sem 3 */ ++#define ARM_SD_OWN4 0x00000300 /* Owner of sem 4 */ ++#define ARM_SD_OWN5 0x00000C00 /* Owner of sem 5 */ ++#define ARM_SD_OWN6 0x00003000 /* Owner of sem 6 */ ++#define ARM_SD_OWN7 0x0000C000 /* Owner of sem 7 */ ++#define ARM_SD_SEM0 0x00010000 /* Status of sem 0 */ ++#define ARM_SD_SEM1 0x00020000 /* Status of sem 1 */ ++#define ARM_SD_SEM2 0x00040000 /* Status of sem 2 */ ++#define ARM_SD_SEM3 0x00080000 /* Status of sem 3 */ ++#define ARM_SD_SEM4 0x00100000 /* Status of sem 4 */ ++#define ARM_SD_SEM5 0x00200000 /* Status of sem 5 */ ++#define ARM_SD_SEM6 0x00400000 /* Status of sem 6 */ ++#define ARM_SD_SEM7 0x00800000 /* Status of sem 7 */ ++ ++/* Doorbells clear/debug register (...0xE4) */ ++#define ARM_BD_OWN0 0x00000003 /* Owner of doorbell 0 */ ++#define ARM_BD_OWN1 0x0000000C /* Owner of doorbell 1 */ ++#define ARM_BD_OWN2 0x00000030 /* Owner of doorbell 2 */ ++#define ARM_BD_OWN3 0x000000C0 /* Owner of doorbell 3 */ ++#define ARM_BD_BELL0 0x00000100 /* Status of doorbell 0 */ ++#define ARM_BD_BELL1 0x00000200 /* Status of doorbell 1 */ ++#define ARM_BD_BELL2 0x00000400 /* Status of doorbell 2 */ ++#define ARM_BD_BELL3 0x00000800 /* Status of doorbell 3 */ ++ ++/* MY IRQS register (...0xF8) */ ++#define ARM_MYIRQ_BELL 0x00000001 /* This owner has a doorbell IRQ */ ++#define ARM_MYIRQ_MAIL 0x00000002 /* This owner has a mailbox IRQ */ ++ ++/* ALL IRQS register (...0xF8) */ ++#define ARM_AIS_BELL0 0x00000001 /* Doorbell 0 IRQ pending */ ++#define ARM_AIS_BELL1 0x00000002 /* Doorbell 1 IRQ pending */ ++#define ARM_AIS_BELL2 0x00000004 /* Doorbell 2 IRQ pending */ ++#define ARM_AIS_BELL3 0x00000008 /* Doorbell 3 IRQ pending */ ++#define ARM_AIS0_HAVEDATA 0x00000010 /* MAIL 0 has data IRQ pending */ ++#define ARM_AIS0_HAVESPAC 0x00000020 /* MAIL 0 has space IRQ pending */ ++#define ARM_AIS0_OPPEMPTY 0x00000040 /* MAIL 0 opposite is empty IRQ */ ++#define ARM_AIS1_HAVEDATA 0x00000080 /* MAIL 1 has data IRQ pending */ ++#define ARM_AIS1_HAVESPAC 0x00000100 /* MAIL 1 has space IRQ pending */ ++#define ARM_AIS1_OPPEMPTY 0x00000200 /* MAIL 1 opposite is empty IRQ */ ++/* Note that bell-0, bell-1 and MAIL0 IRQ go only to the ARM */ ++/* Whilst that bell-2, bell-3 and MAIL1 IRQ go only to the VC */ ++/* */ ++/* ARM JTAG BASH */ ++/* */ ++#define AJB_BASE 0x7e2000c0 ++ ++#define AJBCONF HW_REGISTER_RW(AJB_BASE+0x00) ++#define AJB_BITS0 0x000000 ++#define AJB_BITS4 0x000004 ++#define AJB_BITS8 0x000008 ++#define AJB_BITS12 0x00000C ++#define AJB_BITS16 0x000010 ++#define AJB_BITS20 0x000014 ++#define AJB_BITS24 0x000018 ++#define AJB_BITS28 0x00001C ++#define AJB_BITS32 0x000020 ++#define AJB_BITS34 0x000022 ++#define AJB_OUT_MS 0x000040 ++#define AJB_OUT_LS 0x000000 ++#define AJB_INV_CLK 0x000080 ++#define AJB_D0_RISE 0x000100 ++#define AJB_D0_FALL 0x000000 ++#define AJB_D1_RISE 0x000200 ++#define AJB_D1_FALL 0x000000 ++#define AJB_IN_RISE 0x000400 ++#define AJB_IN_FALL 0x000000 ++#define AJB_ENABLE 0x000800 ++#define AJB_HOLD0 0x000000 ++#define AJB_HOLD1 0x001000 ++#define AJB_HOLD2 0x002000 ++#define AJB_HOLD3 0x003000 ++#define AJB_RESETN 0x004000 ++#define AJB_CLKSHFT 16 ++#define AJB_BUSY 0x80000000 ++#define AJBTMS HW_REGISTER_RW(AJB_BASE+0x04) ++#define AJBTDI HW_REGISTER_RW(AJB_BASE+0x08) ++#define AJBTDO HW_REGISTER_RW(AJB_BASE+0x0c) ++ ++#define ARM_LOCAL_BASE 0x40000000 ++#define ARM_LOCAL_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x000) ++#define ARM_LOCAL_PRESCALER HW_REGISTER_RW(ARM_LOCAL_BASE+0x008) ++#define ARM_LOCAL_GPU_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x00C) ++#define ARM_LOCAL_PM_ROUTING_SET HW_REGISTER_RW(ARM_LOCAL_BASE+0x010) ++#define ARM_LOCAL_PM_ROUTING_CLR HW_REGISTER_RW(ARM_LOCAL_BASE+0x014) ++#define ARM_LOCAL_TIMER_LS HW_REGISTER_RW(ARM_LOCAL_BASE+0x01C) ++#define ARM_LOCAL_TIMER_MS HW_REGISTER_RW(ARM_LOCAL_BASE+0x020) ++#define ARM_LOCAL_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x024) ++#define ARM_LOCAL_AXI_COUNT HW_REGISTER_RW(ARM_LOCAL_BASE+0x02C) ++#define ARM_LOCAL_AXI_IRQ HW_REGISTER_RW(ARM_LOCAL_BASE+0x030) ++#define ARM_LOCAL_TIMER_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x034) ++#define ARM_LOCAL_TIMER_WRITE HW_REGISTER_RW(ARM_LOCAL_BASE+0x038) ++ ++#define ARM_LOCAL_TIMER_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x040) ++#define ARM_LOCAL_TIMER_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x044) ++#define ARM_LOCAL_TIMER_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x048) ++#define ARM_LOCAL_TIMER_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x04C) ++ ++#define ARM_LOCAL_MAILBOX_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x050) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x054) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x058) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x05C) ++ ++#define ARM_LOCAL_IRQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x060) ++#define ARM_LOCAL_IRQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x064) ++#define ARM_LOCAL_IRQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x068) ++#define ARM_LOCAL_IRQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x06C) ++ ++#define ARM_LOCAL_FIQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x070) ++#define ARM_LOCAL_FIQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x074) ++#define ARM_LOCAL_FIQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x078) ++#define ARM_LOCAL_FIQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x07C) ++ ++#define ARM_LOCAL_MAILBOX0_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x080) ++#define ARM_LOCAL_MAILBOX1_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x084) ++#define ARM_LOCAL_MAILBOX2_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x088) ++#define ARM_LOCAL_MAILBOX3_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x08C) ++ ++#define ARM_LOCAL_MAILBOX0_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x090) ++#define ARM_LOCAL_MAILBOX1_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x094) ++#define ARM_LOCAL_MAILBOX2_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x098) ++#define ARM_LOCAL_MAILBOX3_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x09C) ++ ++#define ARM_LOCAL_MAILBOX0_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A0) ++#define ARM_LOCAL_MAILBOX1_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A4) ++#define ARM_LOCAL_MAILBOX2_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A8) ++#define ARM_LOCAL_MAILBOX3_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0AC) ++ ++#define ARM_LOCAL_MAILBOX0_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B0) ++#define ARM_LOCAL_MAILBOX1_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B4) ++#define ARM_LOCAL_MAILBOX2_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B8) ++#define ARM_LOCAL_MAILBOX3_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0BC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C0) ++#define ARM_LOCAL_MAILBOX1_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C4) ++#define ARM_LOCAL_MAILBOX2_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C8) ++#define ARM_LOCAL_MAILBOX3_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0CC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D0) ++#define ARM_LOCAL_MAILBOX1_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D4) ++#define ARM_LOCAL_MAILBOX2_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D8) ++#define ARM_LOCAL_MAILBOX3_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0DC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E0) ++#define ARM_LOCAL_MAILBOX1_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E4) ++#define ARM_LOCAL_MAILBOX2_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E8) ++#define ARM_LOCAL_MAILBOX3_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0EC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F0) ++#define ARM_LOCAL_MAILBOX1_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F4) ++#define ARM_LOCAL_MAILBOX2_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F8) ++#define ARM_LOCAL_MAILBOX3_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0FC) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/arm_power.h b/arch/arm/mach-bcm2709/include/mach/arm_power.h +new file mode 100644 +index 0000000..d3bf245 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/arm_power.h +@@ -0,0 +1,62 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/arm_power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _ARM_POWER_H ++#define _ARM_POWER_H ++ ++/* Use meaningful names on each side */ ++#ifdef __VIDEOCORE__ ++#define PREFIX(x) ARM_##x ++#else ++#define PREFIX(x) BCM_##x ++#endif ++ ++enum { ++ PREFIX(POWER_SDCARD_BIT), ++ PREFIX(POWER_UART_BIT), ++ PREFIX(POWER_MINIUART_BIT), ++ PREFIX(POWER_USB_BIT), ++ PREFIX(POWER_I2C0_BIT), ++ PREFIX(POWER_I2C1_BIT), ++ PREFIX(POWER_I2C2_BIT), ++ PREFIX(POWER_SPI_BIT), ++ PREFIX(POWER_CCP2TX_BIT), ++ PREFIX(POWER_DSI_BIT), ++ ++ PREFIX(POWER_MAX) ++}; ++ ++enum { ++ PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)), ++ PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)), ++ PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)), ++ PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)), ++ PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)), ++ PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)), ++ PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)), ++ PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)), ++ PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)), ++ PREFIX(POWER_DSI) = (1 << PREFIX(POWER_DSI_BIT)), ++ ++ PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1, ++ PREFIX(POWER_NONE) = 0 ++}; ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/barriers.h b/arch/arm/mach-bcm2709/include/mach/barriers.h +new file mode 100644 +index 0000000..723cdad +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/barriers.h +@@ -0,0 +1,3 @@ ++#define mb() dsb() ++#define rmb() dsb() ++#define wmb() mb() +diff --git a/arch/arm/mach-bcm2709/include/mach/clkdev.h b/arch/arm/mach-bcm2709/include/mach/clkdev.h +new file mode 100644 +index 0000000..04b37a8 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/clkdev.h +@@ -0,0 +1,7 @@ ++#ifndef __ASM_MACH_CLKDEV_H ++#define __ASM_MACH_CLKDEV_H ++ ++#define __clk_get(clk) ({ 1; }) ++#define __clk_put(clk) do { } while (0) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/debug-macro.S b/arch/arm/mach-bcm2709/include/mach/debug-macro.S +new file mode 100644 +index 0000000..b24304a +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/debug-macro.S +@@ -0,0 +1,22 @@ ++/* arch/arm/mach-bcm2708/include/mach/debug-macro.S ++ * ++ * Debugging macro include header ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 1994-1999 Russell King ++ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks ++ * ++ * 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 ++ ++ .macro addruart, rp, rv, tmp ++ ldr \rp, =UART0_BASE ++ ldr \rv, =IO_ADDRESS(UART0_BASE) ++ .endm ++ ++#include +diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h +new file mode 100644 +index 0000000..d03e7b5 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/dma.h +@@ -0,0 +1,94 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/dma.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ */ ++ ++ ++#ifndef _MACH_BCM2708_DMA_H ++#define _MACH_BCM2708_DMA_H ++ ++#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" ++ ++/* DMA CS Control and Status bits */ ++#define BCM2708_DMA_ACTIVE (1 << 0) ++#define BCM2708_DMA_INT (1 << 2) ++#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ ++#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ ++#define BCM2708_DMA_ERR (1 << 8) ++#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ ++#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ ++ ++/* DMA control block "info" field bits */ ++#define BCM2708_DMA_INT_EN (1 << 0) ++#define BCM2708_DMA_TDMODE (1 << 1) ++#define BCM2708_DMA_WAIT_RESP (1 << 3) ++#define BCM2708_DMA_D_INC (1 << 4) ++#define BCM2708_DMA_D_WIDTH (1 << 5) ++#define BCM2708_DMA_D_DREQ (1 << 6) ++#define BCM2708_DMA_S_INC (1 << 8) ++#define BCM2708_DMA_S_WIDTH (1 << 9) ++#define BCM2708_DMA_S_DREQ (1 << 10) ++ ++#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) ++#define BCM2708_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) ++ ++#define BCM2708_DMA_DREQ_EMMC 11 ++#define BCM2708_DMA_DREQ_SDHOST 13 ++ ++#define BCM2708_DMA_CS 0x00 /* Control and Status */ ++#define BCM2708_DMA_ADDR 0x04 ++/* the current control block appears in the following registers - read only */ ++#define BCM2708_DMA_INFO 0x08 ++#define BCM2708_DMA_SOURCE_AD 0x0c ++#define BCM2708_DMA_DEST_AD 0x10 ++#define BCM2708_DMA_NEXTCB 0x1C ++#define BCM2708_DMA_DEBUG 0x20 ++ ++#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) ++#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) ++ ++#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) ++ ++struct bcm2708_dma_cb { ++ unsigned long info; ++ unsigned long src; ++ unsigned long dst; ++ unsigned long length; ++ unsigned long stride; ++ unsigned long next; ++ unsigned long pad[2]; ++}; ++struct scatterlist; ++ ++extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); ++extern void bcm_dma_start(void __iomem *dma_chan_base, ++ dma_addr_t control_block); ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); ++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); ++extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); ++ ++/* When listing features we can ask for when allocating DMA channels give ++ those with higher priority smaller ordinal numbers */ ++#define BCM_DMA_FEATURE_FAST_ORD 0 ++#define BCM_DMA_FEATURE_BULK_ORD 1 ++#define BCM_DMA_FEATURE_NORMAL_ORD 2 ++#define BCM_DMA_FEATURE_LITE_ORD 3 ++#define BCM_DMA_FEATURE_FAST (1< ++#include ++ ++ .macro disable_fiq ++ .endm ++ ++ .macro get_irqnr_preamble, base, tmp ++ ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE) ++ .endm ++ ++ .macro arch_ret_to_user, tmp1, tmp2 ++ .endm ++ ++ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ ++ /* get core's local interrupt controller */ ++ ldr \irqstat, = __io_address(ARM_LOCAL_IRQ_PENDING0) @ local interrupt source ++ add \irqstat, \irqstat, \tmp, lsl #2 ++ ldr \tmp, [\irqstat] ++ /* ignore gpu interrupt */ ++ bic \tmp, #0x100 ++ /* ignore mailbox interrupts */ ++ bics \tmp, #0xf0 ++ beq 1005f ++ ++ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) ++ @ N.B. CLZ is an ARM5 instruction. ++ mov \irqnr, #(ARM_IRQ_LOCAL_BASE + 31) ++ sub \irqstat, \tmp, #1 ++ eor \irqstat, \irqstat, \tmp ++ clz \tmp, \irqstat ++ sub \irqnr, \tmp ++ b 1020f ++1005: ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ ++ cmp \tmp, #1 ++ beq 1020f ++ cmp \tmp, #2 ++ beq 1020f ++ cmp \tmp, #3 ++ beq 1020f ++ ++ /* get masked status */ ++ ldr \irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] ++ mov \irqnr, #(ARM_IRQ0_BASE + 31) ++ and \tmp, \irqstat, #0x300 @ save bits 8 and 9 ++ /* clear bits 8 and 9, and test */ ++ bics \irqstat, \irqstat, #0x300 ++ bne 1010f ++ ++ tst \tmp, #0x100 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ1_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<7) | (1<<9) | (1<<10)) ++ bicne \irqstat, #((1<<18) | (1<<19)) ++ bne 1010f ++ ++ tst \tmp, #0x200 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ2_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25)) ++ bicne \irqstat, #((1<<30)) ++ beq 1020f ++ ++1010: ++ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) ++ @ N.B. CLZ is an ARM5 instruction. ++ sub \tmp, \irqstat, #1 ++ eor \irqstat, \irqstat, \tmp ++ clz \tmp, \irqstat ++ sub \irqnr, \tmp ++ ++1020: @ EQ will be set if no irqs pending ++ ++ .endm ++ ++ .macro test_for_ipi, irqnr, irqstat, base, tmp ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ /* get core's mailbox interrupt control */ ++ ldr \irqstat, = __io_address(ARM_LOCAL_MAILBOX0_CLR0) @ mbox_clr ++ add \irqstat, \irqstat, \tmp, lsl #4 ++ ldr \tmp, [\irqstat] ++ cmp \tmp, #0 ++ beq 1030f ++ clz \tmp, \tmp ++ rsb \irqnr, \tmp, #31 ++ mov \tmp, #1 ++ lsl \tmp, \irqnr ++ str \tmp, [\irqstat] @ clear interrupt source ++ dsb ++1030: @ EQ will be set if no irqs pending ++ .endm +diff --git a/arch/arm/mach-bcm2709/include/mach/frc.h b/arch/arm/mach-bcm2709/include/mach/frc.h +new file mode 100644 +index 0000000..dd51e07 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/frc.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 free running counter (timer) ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _MACH_FRC_H ++#define _MACH_FRC_H ++ ++#define FRC_TICK_RATE (1000000) ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ (slightly faster than frc_clock_ticks63() ++ */ ++extern unsigned long frc_clock_ticks32(void); ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ * Note - top bit should be ignored (see cnt32_to_63) ++ */ ++extern unsigned long long frc_clock_ticks63(void); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/gpio.h b/arch/arm/mach-bcm2709/include/mach/gpio.h +new file mode 100644 +index 0000000..7965a97 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/gpio.h +@@ -0,0 +1,17 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/gpio.h ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#ifndef __ASM_ARCH_GPIO_H ++#define __ASM_ARCH_GPIO_H ++ ++#define BCM2708_NR_GPIOS 54 // number of gpio lines ++ ++#define gpio_to_irq(x) ((x) + GPIO_IRQ_START) ++#define irq_to_gpio(x) ((x) - GPIO_IRQ_START) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/hardware.h b/arch/arm/mach-bcm2709/include/mach/hardware.h +new file mode 100644 +index 0000000..c2954e8 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/hardware.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/hardware.h ++ * ++ * This file contains the hardware definitions of the BCM2708 devices. ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_HARDWARE_H ++#define __ASM_ARCH_HARDWARE_H ++ ++#include ++#include ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/io.h b/arch/arm/mach-bcm2709/include/mach/io.h +new file mode 100644 +index 0000000..e6eb84d +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/io.h +@@ -0,0 +1,27 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/io.h ++ * ++ * Copyright (C) 2003 ARM Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARM_ARCH_IO_H ++#define __ASM_ARM_ARCH_IO_H ++ ++#define IO_SPACE_LIMIT 0xffffffff ++ ++#define __io(a) __typesafe_io(a) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/irqs.h b/arch/arm/mach-bcm2709/include/mach/irqs.h +new file mode 100644 +index 0000000..d301f06c +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/irqs.h +@@ -0,0 +1,225 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/irqs.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_IRQS_H_ ++#define _BCM2708_IRQS_H_ ++ ++#include ++ ++/* ++ * IRQ interrupts definitions are the same as the INT definitions ++ * held within platform.h ++ */ ++#define IRQ_ARMCTRL_START 0 ++#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) ++#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) ++#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) ++#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) ++#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) ++#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) ++#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) ++#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) ++#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) ++#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) ++#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) ++#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) ++#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) ++#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) ++#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) ++#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) ++#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) ++#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) ++#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) ++#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) ++#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) ++#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) ++#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) ++#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) ++#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) ++#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) ++#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) ++#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) ++#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) ++#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) ++#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) ++#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) ++#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) ++#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) ++#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) ++#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) ++#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) ++#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) ++#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) ++#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) ++#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) ++#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) ++#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) ++#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) ++#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) ++#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) ++#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) ++#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) ++#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) ++#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) ++#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) ++#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) ++#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) ++#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) ++#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) ++#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) ++#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) ++#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) ++#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) ++#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) ++#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) ++#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) ++#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) ++#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) ++ ++#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) ++#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) ++#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) ++#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) ++#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) ++#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) ++#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) ++#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) ++#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) ++#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) ++ ++#define IRQ_ARM_LOCAL_CNTPSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) ++#define IRQ_ARM_LOCAL_CNTPNSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) ++#define IRQ_ARM_LOCAL_CNTHPIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) ++#define IRQ_ARM_LOCAL_CNTVIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) ++#define IRQ_ARM_LOCAL_MAILBOX0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) ++#define IRQ_ARM_LOCAL_MAILBOX1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) ++#define IRQ_ARM_LOCAL_MAILBOX2 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) ++#define IRQ_ARM_LOCAL_MAILBOX3 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) ++#define IRQ_ARM_LOCAL_GPU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) ++#define IRQ_ARM_LOCAL_PMU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) ++#define IRQ_ARM_LOCAL_ZERO (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) ++#define IRQ_ARM_LOCAL_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) ++ ++#define FIQ_START HARD_IRQS ++ ++/* ++ * FIQ interrupts definitions are the same as the INT definitions. ++ */ ++#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) ++#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) ++#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) ++#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) ++#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) ++#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) ++#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) ++#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) ++#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) ++#define FIQ_USB (FIQ_START+INTERRUPT_USB) ++#define FIQ_3D (FIQ_START+INTERRUPT_3D) ++#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) ++#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) ++#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) ++#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) ++#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) ++#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) ++#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) ++#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) ++#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) ++#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) ++#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) ++#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) ++#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) ++#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) ++#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) ++#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) ++#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) ++#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) ++#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) ++#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) ++#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) ++#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) ++#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) ++#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) ++#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) ++#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) ++#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) ++#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) ++#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) ++#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) ++#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) ++#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) ++#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) ++#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) ++#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) ++#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) ++#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) ++#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) ++#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) ++#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) ++#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) ++#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) ++#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) ++#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) ++#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) ++#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) ++#define FIQ_UART (FIQ_START+INTERRUPT_UART) ++#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) ++#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) ++#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) ++#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) ++#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) ++#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) ++ ++#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) ++#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) ++#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) ++#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) ++#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) ++#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) ++#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) ++#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) ++#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) ++#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) ++ ++#define FIQ_ARM_LOCAL_CNTPSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) ++#define FIQ_ARM_LOCAL_CNTPNSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) ++#define FIQ_ARM_LOCAL_CNTHPIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) ++#define FIQ_ARM_LOCAL_CNTVIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) ++#define FIQ_ARM_LOCAL_MAILBOX0 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) ++#define FIQ_ARM_LOCAL_MAILBOX1 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) ++#define FIQ_ARM_LOCAL_MAILBOX2 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) ++#define FIQ_ARM_LOCAL_MAILBOX3 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) ++#define FIQ_ARM_LOCAL_GPU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) ++#define FIQ_ARM_LOCAL_PMU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) ++#define FIQ_ARM_LOCAL_ZERO (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) ++#define FIQ_ARM_LOCAL_TIMER (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) ++ ++#define HARD_IRQS (128) ++#define FIQ_IRQS (128) ++#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) ++#define GPIO_IRQS (32*5) ++#define SPARE_ALLOC_IRQS 64 ++#define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) ++#define FREE_IRQS 128 ++#define NR_IRQS (BCM2708_ALLOC_IRQS+FREE_IRQS) ++ ++#endif /* _BCM2708_IRQS_H_ */ +diff --git a/arch/arm/mach-bcm2709/include/mach/memory.h b/arch/arm/mach-bcm2709/include/mach/memory.h +new file mode 100644 +index 0000000..7548a52 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/memory.h +@@ -0,0 +1,57 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/memory.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_MEMORY_H ++#define __ASM_ARCH_MEMORY_H ++ ++/* Memory overview: ++ ++ [ARMcore] <--virtual addr--> ++ [ARMmmu] <--physical addr--> ++ [GERTmap] <--bus add--> ++ [VCperiph] ++ ++*/ ++ ++/* ++ * Physical DRAM offset. ++ */ ++#define BCM_PLAT_PHYS_OFFSET UL(0x00000000) ++#define VC_ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ ++ ++#ifdef CONFIG_BCM2708_NOL2CACHE ++ #define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ ++#else ++ #define _REAL_BUS_OFFSET UL(0x40000000) /* use L2 cache */ ++#endif ++ ++/* We're using the memory at 64M in the VideoCore for Linux - this adjustment ++ * will provide the offset into this area as well as setting the bits that ++ * stop the L1 and L2 cache from being used ++ * ++ * WARNING: this only works because the ARM is given memory at a fixed location ++ * (ARMMEM_OFFSET) ++ */ ++#define BUS_OFFSET (VC_ARMMEM_OFFSET + _REAL_BUS_OFFSET) ++#define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) ++#define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) ++#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) ++#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/platform.h b/arch/arm/mach-bcm2709/include/mach/platform.h +new file mode 100644 +index 0000000..7157f38 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/platform.h +@@ -0,0 +1,225 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/platform.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_PLATFORM_H ++#define _BCM2708_PLATFORM_H ++ ++ ++/* macros to get at IO space when running virtually */ ++#define IO_ADDRESS(x) (((x) & 0x00ffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) ++ ++#define __io_address(n) IOMEM(IO_ADDRESS(n)) ++ ++ ++/* ++ * SDRAM ++ */ ++#define BCM2708_SDRAM_BASE 0x00000000 ++ ++/* ++ * Logic expansion modules ++ * ++ */ ++ ++ ++/* ------------------------------------------------------------------------ ++ * BCM2708 ARMCTRL Registers ++ * ------------------------------------------------------------------------ ++ */ ++ ++#define HW_REGISTER_RW(addr) (addr) ++#define HW_REGISTER_RO(addr) (addr) ++ ++#include "arm_control.h" ++#undef ARM_BASE ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define BCM2708_PERI_BASE 0x3F000000 ++#define IC0_BASE (BCM2708_PERI_BASE + 0x2000) ++#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ ++#define MPHI_BASE (BCM2708_PERI_BASE + 0x6000) /* Message -based Parallel Host Interface */ ++#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ ++#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ ++#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ ++#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ ++#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ ++#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ ++#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ ++#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ ++#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */ ++#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */ ++#define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */ ++#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ ++#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ ++#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ ++#define BSC1_BASE (BCM2708_PERI_BASE + 0x804000) /* BSC1 I2C/TWI */ ++#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ ++#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device (actually the multicore sync block*/ ++ ++#define ARMCTRL_BASE (ARM_BASE + 0x000) ++#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ ++#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ ++#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ ++ ++ ++/* ++ * Interrupt assignments ++ */ ++ ++#define ARM_IRQ1_BASE 0 ++#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) ++#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) ++#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) ++#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) ++#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) ++#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) ++#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) ++#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) ++#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) ++#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) ++#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) ++#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) ++#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) ++#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) ++#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) ++#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) ++#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) ++#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) ++#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) ++#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) ++#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) ++#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) ++#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) ++#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) ++#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) ++#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) ++#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) ++#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) ++#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) ++#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) ++#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) ++#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) ++ ++#define ARM_IRQ2_BASE 32 ++#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) ++#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) ++#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) ++#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) ++#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) ++#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) ++#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) ++#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) ++#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) ++#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) ++#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) ++#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) ++#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) ++#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) ++#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) ++#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) ++#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) ++#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) ++#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) ++#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) ++#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) ++#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) ++#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) ++#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) ++#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) ++#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) ++#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) ++#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) ++#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) ++#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) ++#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) ++#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) ++ ++#define ARM_IRQ0_BASE 64 ++#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) ++#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) ++#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) ++#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) ++#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) ++#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) ++#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) ++#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) ++#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) ++#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) ++#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) ++#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) ++#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) ++#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) ++#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) ++#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) ++#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) ++#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) ++#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) ++#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) ++#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) ++ ++#define ARM_IRQ_LOCAL_BASE 96 ++#define INTERRUPT_ARM_LOCAL_CNTPSIRQ (ARM_IRQ_LOCAL_BASE + 0) ++#define INTERRUPT_ARM_LOCAL_CNTPNSIRQ (ARM_IRQ_LOCAL_BASE + 1) ++#define INTERRUPT_ARM_LOCAL_CNTHPIRQ (ARM_IRQ_LOCAL_BASE + 2) ++#define INTERRUPT_ARM_LOCAL_CNTVIRQ (ARM_IRQ_LOCAL_BASE + 3) ++#define INTERRUPT_ARM_LOCAL_MAILBOX0 (ARM_IRQ_LOCAL_BASE + 4) ++#define INTERRUPT_ARM_LOCAL_MAILBOX1 (ARM_IRQ_LOCAL_BASE + 5) ++#define INTERRUPT_ARM_LOCAL_MAILBOX2 (ARM_IRQ_LOCAL_BASE + 6) ++#define INTERRUPT_ARM_LOCAL_MAILBOX3 (ARM_IRQ_LOCAL_BASE + 7) ++#define INTERRUPT_ARM_LOCAL_GPU_FAST (ARM_IRQ_LOCAL_BASE + 8) ++#define INTERRUPT_ARM_LOCAL_PMU_FAST (ARM_IRQ_LOCAL_BASE + 9) ++#define INTERRUPT_ARM_LOCAL_ZERO (ARM_IRQ_LOCAL_BASE + 10) ++#define INTERRUPT_ARM_LOCAL_TIMER (ARM_IRQ_LOCAL_BASE + 11) ++ ++/* ++ * Watchdog ++ */ ++#define PM_RSTC (PM_BASE+0x1c) ++#define PM_RSTS (PM_BASE+0x20) ++#define PM_WDOG (PM_BASE+0x24) ++ ++#define PM_WDOG_RESET 0000000000 ++#define PM_PASSWORD 0x5a000000 ++#define PM_WDOG_TIME_SET 0x000fffff ++#define PM_RSTC_WRCFG_CLR 0xffffffcf ++#define PM_RSTC_WRCFG_SET 0x00000030 ++#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 ++#define PM_RSTC_RESET 0x00000102 ++ ++#define PM_RSTS_HADPOR_SET 0x00001000 ++#define PM_RSTS_HADSRH_SET 0x00000400 ++#define PM_RSTS_HADSRF_SET 0x00000200 ++#define PM_RSTS_HADSRQ_SET 0x00000100 ++#define PM_RSTS_HADWRH_SET 0x00000040 ++#define PM_RSTS_HADWRF_SET 0x00000020 ++#define PM_RSTS_HADWRQ_SET 0x00000010 ++#define PM_RSTS_HADDRH_SET 0x00000004 ++#define PM_RSTS_HADDRF_SET 0x00000002 ++#define PM_RSTS_HADDRQ_SET 0x00000001 ++ ++#define UART0_CLOCK 3000000 ++ ++#endif ++ ++/* END */ +diff --git a/arch/arm/mach-bcm2709/include/mach/power.h b/arch/arm/mach-bcm2709/include/mach/power.h +new file mode 100644 +index 0000000..52b3b02 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/power.h +@@ -0,0 +1,26 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#ifndef _MACH_BCM2708_POWER_H ++#define _MACH_BCM2708_POWER_H ++ ++#include ++#include ++ ++typedef unsigned int BCM_POWER_HANDLE_T; ++ ++extern int bcm_power_open(BCM_POWER_HANDLE_T *handle); ++extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request); ++extern int bcm_power_close(BCM_POWER_HANDLE_T handle); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/system.h b/arch/arm/mach-bcm2709/include/mach/system.h +new file mode 100644 +index 0000000..2d0b821 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/system.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/system.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_SYSTEM_H ++#define __ASM_ARCH_SYSTEM_H ++ ++#include ++#include ++#include ++ ++static inline void arch_idle(void) ++{ ++ /* ++ * This should do all the clock switching ++ * and wait for interrupt tricks ++ */ ++ cpu_do_idle(); ++} ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/timex.h b/arch/arm/mach-bcm2709/include/mach/timex.h +new file mode 100644 +index 0000000..64a660c +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/timex.h +@@ -0,0 +1,23 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 sysem clock frequency ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#define CLOCK_TICK_RATE (1000000) +diff --git a/arch/arm/mach-bcm2709/include/mach/uncompress.h b/arch/arm/mach-bcm2709/include/mach/uncompress.h +new file mode 100644 +index 0000000..d634813 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/uncompress.h +@@ -0,0 +1,84 @@ ++/* ++ * arch/arm/mach-bcn2708/include/mach/uncompress.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++#define UART_BAUD 115200 ++ ++#define BCM2708_UART_DR __io(UART0_BASE + UART01x_DR) ++#define BCM2708_UART_FR __io(UART0_BASE + UART01x_FR) ++#define BCM2708_UART_IBRD __io(UART0_BASE + UART011_IBRD) ++#define BCM2708_UART_FBRD __io(UART0_BASE + UART011_FBRD) ++#define BCM2708_UART_LCRH __io(UART0_BASE + UART011_LCRH) ++#define BCM2708_UART_CR __io(UART0_BASE + UART011_CR) ++ ++/* ++ * This does not append a newline ++ */ ++static inline void putc(int c) ++{ ++ while (__raw_readl(BCM2708_UART_FR) & UART01x_FR_TXFF) ++ barrier(); ++ ++ __raw_writel(c, BCM2708_UART_DR); ++} ++ ++static inline void flush(void) ++{ ++ int fr; ++ ++ do { ++ fr = __raw_readl(BCM2708_UART_FR); ++ barrier(); ++ } while ((fr & (UART011_FR_TXFE | UART01x_FR_BUSY)) != UART011_FR_TXFE); ++} ++ ++static inline void arch_decomp_setup(void) ++{ ++ int temp, div, rem, frac; ++ ++ temp = 16 * UART_BAUD; ++ div = UART0_CLOCK / temp; ++ rem = UART0_CLOCK % temp; ++ temp = (8 * rem) / UART_BAUD; ++ frac = (temp >> 1) + (temp & 1); ++ ++ /* Make sure the UART is disabled before we start */ ++ __raw_writel(0, BCM2708_UART_CR); ++ ++ /* Set the baud rate */ ++ __raw_writel(div, BCM2708_UART_IBRD); ++ __raw_writel(frac, BCM2708_UART_FBRD); ++ ++ /* Set the UART to 8n1, FIFO enabled */ ++ __raw_writel(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, BCM2708_UART_LCRH); ++ ++ /* Enable the UART */ ++ __raw_writel(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, ++ BCM2708_UART_CR); ++} ++ ++/* ++ * nothing to do ++ */ ++#define arch_decomp_wdog() +diff --git a/arch/arm/mach-bcm2709/include/mach/vc_mem.h b/arch/arm/mach-bcm2709/include/mach/vc_mem.h +new file mode 100644 +index 0000000..4a4a338 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vc_mem.h +@@ -0,0 +1,35 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#if !defined( VC_MEM_H ) ++#define VC_MEM_H ++ ++#include ++ ++#define VC_MEM_IOC_MAGIC 'v' ++ ++#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) ++#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) ++#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int ) ++#define VC_MEM_IOC_MEM_LOAD _IOR( VC_MEM_IOC_MAGIC, 3, unsigned int ) ++ ++#if defined( __KERNEL__ ) ++#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF ++ ++extern unsigned long mm_vc_mem_phys_addr; ++extern unsigned int mm_vc_mem_size; ++extern int vc_mem_get_current_size( void ); ++#endif ++ ++#endif /* VC_MEM_H */ +diff --git a/arch/arm/mach-bcm2709/include/mach/vc_support.h b/arch/arm/mach-bcm2709/include/mach/vc_support.h +new file mode 100755 +index 0000000..70e809f +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vc_support.h +@@ -0,0 +1,69 @@ ++#ifndef _VC_SUPPORT_H_ ++#define _VC_SUPPORT_H_ ++ ++/* ++ * vc_support.h ++ * ++ * Created on: 25 Nov 2012 ++ * Author: Simon ++ */ ++ ++enum { ++/* ++ If a MEM_HANDLE_T is discardable, the memory manager may resize it to size ++ 0 at any time when it is not locked or retained. ++ */ ++ MEM_FLAG_DISCARDABLE = 1 << 0, ++ ++ /* ++ If a MEM_HANDLE_T is allocating (or normal), its block of memory will be ++ accessed in an allocating fashion through the cache. ++ */ ++ MEM_FLAG_NORMAL = 0 << 2, ++ MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, ++ ++ /* ++ If a MEM_HANDLE_T is direct, its block of memory will be accessed ++ directly, bypassing the cache. ++ */ ++ MEM_FLAG_DIRECT = 1 << 2, ++ ++ /* ++ If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a ++ non-allocating fashion through the cache. ++ */ ++ MEM_FLAG_COHERENT = 2 << 2, ++ ++ /* ++ If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by ++ the VPU in a fashion which is allocating in L2, but only coherent in L1. ++ */ ++ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), ++ ++ /* ++ If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than ++ MEM_HANDLE_INVALID on allocation and resize up. ++ */ ++ MEM_FLAG_ZERO = 1 << 4, ++ ++ /* ++ If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value ++ (either zero, or all 1's) on allocation. ++ */ ++ MEM_FLAG_NO_INIT = 1 << 5, ++ ++ /* ++ Hints. ++ */ ++ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ ++}; ++ ++unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags); ++unsigned int ReleaseVcMemory(unsigned int handle); ++unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle); ++unsigned int UnlockVcMemory(unsigned int handle); ++ ++unsigned int ExecuteVcCode(unsigned int code, ++ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/vcio.h b/arch/arm/mach-bcm2709/include/mach/vcio.h +new file mode 100644 +index 0000000..8e11d67e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vcio.h +@@ -0,0 +1,165 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vcio.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _MACH_BCM2708_VCIO_H ++#define _MACH_BCM2708_VCIO_H ++ ++/* Routines to handle I/O via the VideoCore "ARM control" registers ++ * (semaphores, doorbells, mailboxes) ++ */ ++ ++#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio" ++ ++/* Constants shared with the ARM identifying separate mailbox channels */ ++#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ ++#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ ++#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ ++#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ ++#define MBOX_CHAN_COUNT 9 ++ ++enum { ++ VCMSG_PROCESS_REQUEST = 0x00000000 ++}; ++enum { ++ VCMSG_REQUEST_SUCCESSFUL = 0x80000000, ++ VCMSG_REQUEST_FAILED = 0x80000001 ++}; ++/* Mailbox property tags */ ++enum { ++ VCMSG_PROPERTY_END = 0x00000000, ++ VCMSG_GET_FIRMWARE_REVISION = 0x00000001, ++ VCMSG_GET_BOARD_MODEL = 0x00010001, ++ VCMSG_GET_BOARD_REVISION = 0x00010002, ++ VCMSG_GET_BOARD_MAC_ADDRESS = 0x00010003, ++ VCMSG_GET_BOARD_SERIAL = 0x00010004, ++ VCMSG_GET_ARM_MEMORY = 0x00010005, ++ VCMSG_GET_VC_MEMORY = 0x00010006, ++ VCMSG_GET_CLOCKS = 0x00010007, ++ VCMSG_GET_COMMAND_LINE = 0x00050001, ++ VCMSG_GET_DMA_CHANNELS = 0x00060001, ++ VCMSG_GET_POWER_STATE = 0x00020001, ++ VCMSG_GET_TIMING = 0x00020002, ++ VCMSG_SET_POWER_STATE = 0x00028001, ++ VCMSG_GET_CLOCK_STATE = 0x00030001, ++ VCMSG_SET_CLOCK_STATE = 0x00038001, ++ VCMSG_GET_CLOCK_RATE = 0x00030002, ++ VCMSG_SET_CLOCK_RATE = 0x00038002, ++ VCMSG_GET_VOLTAGE = 0x00030003, ++ VCMSG_SET_VOLTAGE = 0x00038003, ++ VCMSG_GET_MAX_CLOCK = 0x00030004, ++ VCMSG_GET_MAX_VOLTAGE = 0x00030005, ++ VCMSG_GET_TEMPERATURE = 0x00030006, ++ VCMSG_GET_MIN_CLOCK = 0x00030007, ++ VCMSG_GET_MIN_VOLTAGE = 0x00030008, ++ VCMSG_GET_TURBO = 0x00030009, ++ VCMSG_GET_MAX_TEMPERATURE = 0x0003000a, ++ VCMSG_GET_STC = 0x0003000b, ++ VCMSG_SET_TURBO = 0x00038009, ++ VCMSG_SET_ALLOCATE_MEM = 0x0003000c, ++ VCMSG_SET_LOCK_MEM = 0x0003000d, ++ VCMSG_SET_UNLOCK_MEM = 0x0003000e, ++ VCMSG_SET_RELEASE_MEM = 0x0003000f, ++ VCMSG_SET_EXECUTE_CODE = 0x00030010, ++ VCMSG_SET_EXECUTE_QPU = 0x00030011, ++ VCMSG_SET_ENABLE_QPU = 0x00030012, ++ VCMSG_GET_RESOURCE_HANDLE = 0x00030014, ++ VCMSG_GET_EDID_BLOCK = 0x00030020, ++ VCMSG_GET_CUSTOMER_OTP = 0x00030021, ++ VCMSG_SET_CUSTOMER_OTP = 0x00038021, ++ VCMSG_SET_ALLOCATE_BUFFER = 0x00040001, ++ VCMSG_SET_RELEASE_BUFFER = 0x00048001, ++ VCMSG_SET_BLANK_SCREEN = 0x00040002, ++ VCMSG_TST_BLANK_SCREEN = 0x00044002, ++ VCMSG_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, ++ VCMSG_TST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, ++ VCMSG_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, ++ VCMSG_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, ++ VCMSG_TST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, ++ VCMSG_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, ++ VCMSG_GET_DEPTH = 0x00040005, ++ VCMSG_TST_DEPTH = 0x00044005, ++ VCMSG_SET_DEPTH = 0x00048005, ++ VCMSG_GET_PIXEL_ORDER = 0x00040006, ++ VCMSG_TST_PIXEL_ORDER = 0x00044006, ++ VCMSG_SET_PIXEL_ORDER = 0x00048006, ++ VCMSG_GET_ALPHA_MODE = 0x00040007, ++ VCMSG_TST_ALPHA_MODE = 0x00044007, ++ VCMSG_SET_ALPHA_MODE = 0x00048007, ++ VCMSG_GET_PITCH = 0x00040008, ++ VCMSG_TST_PITCH = 0x00044008, ++ VCMSG_SET_PITCH = 0x00048008, ++ VCMSG_GET_VIRTUAL_OFFSET = 0x00040009, ++ VCMSG_TST_VIRTUAL_OFFSET = 0x00044009, ++ VCMSG_SET_VIRTUAL_OFFSET = 0x00048009, ++ VCMSG_GET_OVERSCAN = 0x0004000a, ++ VCMSG_TST_OVERSCAN = 0x0004400a, ++ VCMSG_SET_OVERSCAN = 0x0004800a, ++ VCMSG_GET_PALETTE = 0x0004000b, ++ VCMSG_TST_PALETTE = 0x0004400b, ++ VCMSG_SET_PALETTE = 0x0004800b, ++ VCMSG_GET_LAYER = 0x0004000c, ++ VCMSG_TST_LAYER = 0x0004400c, ++ VCMSG_SET_LAYER = 0x0004800c, ++ VCMSG_GET_TRANSFORM = 0x0004000d, ++ VCMSG_TST_TRANSFORM = 0x0004400d, ++ VCMSG_SET_TRANSFORM = 0x0004800d, ++ VCMSG_TST_VSYNC = 0x0004400e, ++ VCMSG_SET_VSYNC = 0x0004800e, ++ VCMSG_SET_CURSOR_INFO = 0x00008010, ++ VCMSG_SET_CURSOR_STATE = 0x00008011, ++}; ++ ++extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28); ++extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28); ++extern int /*rc*/ bcm_mailbox_property(void *data, int size); ++ ++#include ++ ++/* ++ * The major device number. We can't rely on dynamic ++ * registration any more, because ioctls need to know ++ * it. ++ */ ++#define MAJOR_NUM 100 ++ ++/* ++ * Set the message of the device driver ++ */ ++#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) ++/* ++ * _IOWR means that we're creating an ioctl command ++ * number for passing information from a user process ++ * to the kernel module and from the kernel module to user process ++ * ++ * The first arguments, MAJOR_NUM, is the major device ++ * number we're using. ++ * ++ * The second argument is the number of the command ++ * (there could be several with different meanings). ++ * ++ * The third argument is the type we want to get from ++ * the process to the kernel. ++ */ ++ ++/* ++ * The name of the device file ++ */ ++#define DEVICE_FILE_NAME "vcio" ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/vmalloc.h b/arch/arm/mach-bcm2709/include/mach/vmalloc.h +new file mode 100644 +index 0000000..6aa6826 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vmalloc.h +@@ -0,0 +1,20 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vmalloc.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#define VMALLOC_END (0xff000000) +diff --git a/arch/arm/mach-bcm2709/power.c b/arch/arm/mach-bcm2709/power.c +new file mode 100644 +index 0000000..3421057 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/power.c +@@ -0,0 +1,195 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "bcm2708_power" ++ ++#define BCM_POWER_MAXCLIENTS 4 ++#define BCM_POWER_NOCLIENT (1<<31) ++ ++/* Some drivers expect there devices to be permanently powered */ ++#ifdef CONFIG_USB ++#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB) ++#endif ++ ++#if 1 ++#define DPRINTK printk ++#else ++#define DPRINTK if (0) printk ++#endif ++ ++struct state_struct { ++ uint32_t global_request; ++ uint32_t client_request[BCM_POWER_MAXCLIENTS]; ++ struct semaphore client_mutex; ++ struct semaphore mutex; ++} g_state; ++ ++int bcm_power_open(BCM_POWER_HANDLE_T *handle) ++{ ++ BCM_POWER_HANDLE_T i; ++ int ret = -EBUSY; ++ ++ down(&g_state.client_mutex); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (g_state.client_request[i] == BCM_POWER_NOCLIENT) { ++ g_state.client_request[i] = BCM_POWER_NONE; ++ *handle = i; ++ ret = 0; ++ break; ++ } ++ } ++ ++ up(&g_state.client_mutex); ++ ++ DPRINTK("bcm_power_open() -> %d\n", *handle); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(bcm_power_open); ++ ++int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request) ++{ ++ int rc = 0; ++ ++ DPRINTK("bcm_power_request(%d, %x)\n", handle, request); ++ ++ if ((handle < BCM_POWER_MAXCLIENTS) && ++ (g_state.client_request[handle] != BCM_POWER_NOCLIENT)) { ++ if (down_interruptible(&g_state.mutex) != 0) { ++ DPRINTK("bcm_power_request -> interrupted\n"); ++ return -EINTR; ++ } ++ ++ if (request != g_state.client_request[handle]) { ++ uint32_t others_request = 0; ++ uint32_t global_request; ++ BCM_POWER_HANDLE_T i; ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (i != handle) ++ others_request |= ++ g_state.client_request[i]; ++ } ++ others_request &= ~BCM_POWER_NOCLIENT; ++ ++ global_request = request | others_request; ++ if (global_request != g_state.global_request) { ++ uint32_t actual; ++ ++ /* Send a request to VideoCore */ ++ bcm_mailbox_write(MBOX_CHAN_POWER, ++ global_request << 4); ++ ++ /* Wait for a response during power-up */ ++ if (global_request & ~g_state.global_request) { ++ rc = bcm_mailbox_read(MBOX_CHAN_POWER, ++ &actual); ++ DPRINTK ++ ("bcm_mailbox_read -> %08x, %d\n", ++ actual, rc); ++ actual >>= 4; ++ } else { ++ rc = 0; ++ actual = global_request; ++ } ++ ++ if (rc == 0) { ++ if (actual != global_request) { ++ printk(KERN_ERR ++ "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", ++ __func__, ++ g_state.global_request, ++ global_request, actual, request, others_request); ++ /* A failure */ ++ BUG_ON((others_request & actual) ++ != others_request); ++ request &= actual; ++ rc = -EIO; ++ } ++ ++ g_state.global_request = actual; ++ g_state.client_request[handle] = ++ request; ++ } ++ } ++ } ++ up(&g_state.mutex); ++ } else { ++ rc = -EINVAL; ++ } ++ DPRINTK("bcm_power_request -> %d\n", rc); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_request); ++ ++int bcm_power_close(BCM_POWER_HANDLE_T handle) ++{ ++ int rc; ++ ++ DPRINTK("bcm_power_close(%d)\n", handle); ++ ++ rc = bcm_power_request(handle, BCM_POWER_NONE); ++ if (rc == 0) ++ g_state.client_request[handle] = BCM_POWER_NOCLIENT; ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_close); ++ ++static int __init bcm_power_init(void) ++{ ++#if defined(BCM_POWER_ALWAYS_ON) ++ BCM_POWER_HANDLE_T always_on_handle; ++#endif ++ int rc = 0; ++ int i; ++ ++ printk(KERN_INFO "bcm_power: Broadcom power driver\n"); ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) ++ g_state.client_request[i] = BCM_POWER_NOCLIENT; ++ ++ sema_init(&g_state.client_mutex, 1); ++ sema_init(&g_state.mutex, 1); ++ ++ g_state.global_request = 0; ++#if defined(BCM_POWER_ALWAYS_ON) ++ if (BCM_POWER_ALWAYS_ON) { ++ bcm_power_open(&always_on_handle); ++ bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON); ++ } ++#endif ++ ++ return rc; ++} ++ ++static void __exit bcm_power_exit(void) ++{ ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); ++} ++ ++arch_initcall(bcm_power_init); /* Initialize early */ ++module_exit(bcm_power_exit); ++ ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_DESCRIPTION("Interface to BCM2708 power management"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-bcm2709/vc_mem.c b/arch/arm/mach-bcm2709/vc_mem.c +new file mode 100644 +index 0000000..ac578db +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vc_mem.c +@@ -0,0 +1,431 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_ARCH_KONA ++#include ++#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) ++#else ++#include ++#endif ++ ++#include "mach/vc_mem.h" ++#include ++ ++#define DRIVER_NAME "vc-mem" ++ ++// Device (/dev) related variables ++static dev_t vc_mem_devnum = 0; ++static struct class *vc_mem_class = NULL; ++static struct cdev vc_mem_cdev; ++static int vc_mem_inited = 0; ++ ++#ifdef CONFIG_DEBUG_FS ++static struct dentry *vc_mem_debugfs_entry; ++#endif ++ ++/* ++ * Videocore memory addresses and size ++ * ++ * Drivers that wish to know the videocore memory addresses and sizes should ++ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in ++ * headers. This allows the other drivers to not be tied down to a a certain ++ * address/size at compile time. ++ * ++ * In the future, the goal is to have the videocore memory virtual address and ++ * size be calculated at boot time rather than at compile time. The decision of ++ * where the videocore memory resides and its size would be in the hands of the ++ * bootloader (and/or kernel). When that happens, the values of these variables ++ * would be calculated and assigned in the init function. ++ */ ++// in the 2835 VC in mapped above ARM, but ARM has full access to VC space ++unsigned long mm_vc_mem_phys_addr = 0x00000000; ++unsigned int mm_vc_mem_size = 0; ++unsigned int mm_vc_mem_base = 0; ++ ++EXPORT_SYMBOL(mm_vc_mem_phys_addr); ++EXPORT_SYMBOL(mm_vc_mem_size); ++EXPORT_SYMBOL(mm_vc_mem_base); ++ ++static uint phys_addr = 0; ++static uint mem_size = 0; ++static uint mem_base = 0; ++ ++ ++/**************************************************************************** ++* ++* vc_mem_open ++* ++***************************************************************************/ ++ ++static int ++vc_mem_open(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_release ++* ++***************************************************************************/ ++ ++static int ++vc_mem_release(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_size ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_size(void) ++{ ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_base ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_base(void) ++{ ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_current_size ++* ++***************************************************************************/ ++ ++int ++vc_mem_get_current_size(void) ++{ ++ return mm_vc_mem_size; ++} ++ ++EXPORT_SYMBOL_GPL(vc_mem_get_current_size); ++ ++/**************************************************************************** ++* ++* vc_mem_ioctl ++* ++***************************************************************************/ ++ ++static long ++vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int rc = 0; ++ ++ (void) cmd; ++ (void) arg; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ switch (cmd) { ++ case VC_MEM_IOC_MEM_PHYS_ADDR: ++ { ++ pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n", ++ __func__, (void *) mm_vc_mem_phys_addr); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr, ++ sizeof (mm_vc_mem_phys_addr)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_SIZE: ++ { ++ // Get the videocore memory size first ++ vc_mem_get_size(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%u\n", __func__, ++ mm_vc_mem_size); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_size, ++ sizeof (mm_vc_mem_size)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_BASE: ++ { ++ // Get the videocore memory base ++ vc_mem_get_base(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_BASE=%u\n", __func__, ++ mm_vc_mem_base); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_base, ++ sizeof (mm_vc_mem_base)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_LOAD: ++ { ++ // Get the videocore memory base ++ vc_mem_get_base(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%u\n", __func__, ++ mm_vc_mem_base); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_base, ++ sizeof (mm_vc_mem_base)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ default: ++ { ++ return -ENOTTY; ++ } ++ } ++ pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc); ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_mmap ++* ++***************************************************************************/ ++ ++static int ++vc_mem_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ int rc = 0; ++ unsigned long length = vma->vm_end - vma->vm_start; ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ ++ pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n", ++ __func__, (long) vma->vm_start, (long) vma->vm_end, ++ (long) vma->vm_pgoff); ++ ++ if (offset + length > mm_vc_mem_size) { ++ pr_err("%s: length %ld is too big\n", __func__, length); ++ return -EINVAL; ++ } ++ // Do not cache the memory map ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ rc = remap_pfn_range(vma, vma->vm_start, ++ (mm_vc_mem_phys_addr >> PAGE_SHIFT) + ++ vma->vm_pgoff, length, vma->vm_page_prot); ++ if (rc != 0) { ++ pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc); ++ } ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* File Operations for the driver. ++* ++***************************************************************************/ ++ ++static const struct file_operations vc_mem_fops = { ++ .owner = THIS_MODULE, ++ .open = vc_mem_open, ++ .release = vc_mem_release, ++ .unlocked_ioctl = vc_mem_ioctl, ++ .mmap = vc_mem_mmap, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++static void vc_mem_debugfs_deinit(void) ++{ ++ debugfs_remove_recursive(vc_mem_debugfs_entry); ++ vc_mem_debugfs_entry = NULL; ++} ++ ++ ++static int vc_mem_debugfs_init( ++ struct device *dev) ++{ ++ vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL); ++ if (!vc_mem_debugfs_entry) { ++ dev_warn(dev, "could not create debugfs entry\n"); ++ return -EFAULT; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_phys_addr", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_phys_addr)) { ++ dev_warn(dev, "%s:could not create vc_mem_phys entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_size", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_size)) { ++ dev_warn(dev, "%s:could not create vc_mem_size entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_base", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_base)) { ++ dev_warn(dev, "%s:could not create vc_mem_base entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ vc_mem_debugfs_deinit(); ++ return -EFAULT; ++} ++ ++#endif /* CONFIG_DEBUG_FS */ ++ ++ ++/**************************************************************************** ++* ++* vc_mem_init ++* ++***************************************************************************/ ++ ++static int __init ++vc_mem_init(void) ++{ ++ int rc = -EFAULT; ++ struct device *dev; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ mm_vc_mem_phys_addr = phys_addr; ++ mm_vc_mem_size = mem_size; ++ mm_vc_mem_base = mem_base; ++ ++ vc_mem_get_size(); ++ ++ pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n", ++ mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024)); ++ ++ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) { ++ pr_err("%s: alloc_chrdev_region failed (rc=%d)\n", ++ __func__, rc); ++ goto out_err; ++ } ++ ++ cdev_init(&vc_mem_cdev, &vc_mem_fops); ++ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) { ++ pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc); ++ goto out_unregister; ++ } ++ ++ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME); ++ if (IS_ERR(vc_mem_class)) { ++ rc = PTR_ERR(vc_mem_class); ++ pr_err("%s: class_create failed (rc=%d)\n", __func__, rc); ++ goto out_cdev_del; ++ } ++ ++ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL, ++ DRIVER_NAME); ++ if (IS_ERR(dev)) { ++ rc = PTR_ERR(dev); ++ pr_err("%s: device_create failed (rc=%d)\n", __func__, rc); ++ goto out_class_destroy; ++ } ++ ++#ifdef CONFIG_DEBUG_FS ++ /* don't fail if the debug entries cannot be created */ ++ vc_mem_debugfs_init(dev); ++#endif ++ ++ vc_mem_inited = 1; ++ return 0; ++ ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ ++ out_class_destroy: ++ class_destroy(vc_mem_class); ++ vc_mem_class = NULL; ++ ++ out_cdev_del: ++ cdev_del(&vc_mem_cdev); ++ ++ out_unregister: ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ ++ out_err: ++ return -1; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_exit ++* ++***************************************************************************/ ++ ++static void __exit ++vc_mem_exit(void) ++{ ++ pr_debug("%s: called\n", __func__); ++ ++ if (vc_mem_inited) { ++#if CONFIG_DEBUG_FS ++ vc_mem_debugfs_deinit(); ++#endif ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ class_destroy(vc_mem_class); ++ cdev_del(&vc_mem_cdev); ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ } ++} ++ ++module_init(vc_mem_init); ++module_exit(vc_mem_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); ++ ++module_param(phys_addr, uint, 0644); ++module_param(mem_size, uint, 0644); ++module_param(mem_base, uint, 0644); +diff --git a/arch/arm/mach-bcm2709/vc_support.c b/arch/arm/mach-bcm2709/vc_support.c +new file mode 100755 +index 0000000..0bc41c4 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vc_support.c +@@ -0,0 +1,318 @@ ++/* ++ * vc_support.c ++ * ++ * Created on: 25 Nov 2012 ++ * Author: Simon ++ */ ++ ++#include ++#include ++ ++#ifdef ECLIPSE_IGNORE ++ ++#define __user ++#define __init ++#define __exit ++#define __iomem ++#define KERN_DEBUG ++#define KERN_ERR ++#define KERN_WARNING ++#define KERN_INFO ++#define _IOWR(a, b, c) b ++#define _IOW(a, b, c) b ++#define _IO(a, b) b ++ ++#endif ++ ++/****** VC MAILBOX FUNCTIONALITY ******/ ++unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_size; ++ unsigned int m_handle; ++ }; ++ unsigned int m_alignment; ++ unsigned int m_flags; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the allocation command ++ msg.m_tag.m_tagId = 0x3000c; ++ msg.m_tag.m_sendBufferSize = 12; ++ msg.m_tag.m_sendDataSize = 12; ++ ++ //fill in our args ++ msg.m_tag.m_args.m_size = size; ++ msg.m_tag.m_args.m_alignment = alignment; ++ msg.m_tag.m_args.m_flags = flags; ++ ++ //run the command ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ { ++ *pHandle = msg.m_tag.m_args.m_handle; ++ return 0; ++ } ++ else ++ { ++ printk(KERN_ERR "failed to allocate vc memory: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} ++ ++unsigned int ReleaseVcMemory(unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_error; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the release command ++ msg.m_tag.m_tagId = 0x3000f; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) ++ return 0; ++ else ++ { ++ printk(KERN_ERR "failed to release vc memory: s=%d response=%08x recv data size=%08x error=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); ++ return 1; ++ } ++} ++ ++unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_busAddress; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the lock command ++ msg.m_tag.m_tagId = 0x3000d; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ { ++ //pick out the bus address ++ *pBusAddress = msg.m_tag.m_args.m_busAddress; ++ return 0; ++ } ++ else ++ { ++ printk(KERN_ERR "failed to lock vc memory: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} ++ ++unsigned int UnlockVcMemory(unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_error; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the unlock command ++ msg.m_tag.m_tagId = 0x3000e; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ //check the error code too ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) ++ return 0; ++ else ++ { ++ printk(KERN_ERR "failed to unlock vc memory: s=%d response=%08x recv data size=%08x error%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); ++ return 1; ++ } ++} ++ ++unsigned int ExecuteVcCode(unsigned int code, ++ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_pCode; ++ unsigned int m_return; ++ }; ++ unsigned int m_r0; ++ unsigned int m_r1; ++ unsigned int m_r2; ++ unsigned int m_r3; ++ unsigned int m_r4; ++ unsigned int m_r5; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the unlock command ++ msg.m_tag.m_tagId = 0x30010; ++ msg.m_tag.m_sendBufferSize = 28; ++ msg.m_tag.m_sendDataSize = 28; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_pCode = code; ++ msg.m_tag.m_args.m_r0 = r0; ++ msg.m_tag.m_args.m_r1 = r1; ++ msg.m_tag.m_args.m_r2 = r2; ++ msg.m_tag.m_args.m_r3 = r3; ++ msg.m_tag.m_args.m_r4 = r4; ++ msg.m_tag.m_args.m_r5 = r5; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ //check the error code too ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ return msg.m_tag.m_args.m_return; ++ else ++ { ++ printk(KERN_ERR "failed to execute: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} +diff --git a/arch/arm/mach-bcm2709/vcio.c b/arch/arm/mach-bcm2709/vcio.c +new file mode 100644 +index 0000000..5e43e85 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vcio.c +@@ -0,0 +1,474 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/vcio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for writing to the mailboxes, ++ * semaphores, doorbells etc. that are shared between the ARM and the ++ * VideoCore processor ++ */ ++ ++#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++ ++#define DRIVER_NAME BCM_VCIO_DRIVER_NAME ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox ++ * -------------------------------------------------------------------- */ ++ ++/* offsets from a mail box base address */ ++#define MAIL_WRT 0x00 /* write - and next 4 words */ ++#define MAIL_RD 0x00 /* read - and next 4 words */ ++#define MAIL_POL 0x10 /* read without popping the fifo */ ++#define MAIL_SND 0x14 /* sender ID (bottom two bits) */ ++#define MAIL_STA 0x18 /* status */ ++#define MAIL_CNF 0x1C /* configuration */ ++ ++#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) ++#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) ++#define MBOX_CHAN(msg) ((msg) & 0xf) ++#define MBOX_DATA28(msg) ((msg) & ~0xf) ++#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) ++ ++#define MBOX_MAGIC 0xd0d0c0de ++ ++struct vc_mailbox { ++ struct device *dev; /* parent device */ ++ void __iomem *status; ++ void __iomem *config; ++ void __iomem *read; ++ void __iomem *write; ++ uint32_t msg[MBOX_CHAN_COUNT]; ++ struct semaphore sema[MBOX_CHAN_COUNT]; ++ uint32_t magic; ++}; ++ ++static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev, ++ uint32_t addr_mbox) ++{ ++ int i; ++ ++ mbox_out->dev = dev; ++ mbox_out->status = __io_address(addr_mbox + MAIL_STA); ++ mbox_out->config = __io_address(addr_mbox + MAIL_CNF); ++ mbox_out->read = __io_address(addr_mbox + MAIL_RD); ++ /* Write to the other mailbox */ ++ mbox_out->write = ++ __io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) + ++ MAIL_WRT); ++ ++ for (i = 0; i < MBOX_CHAN_COUNT; i++) { ++ mbox_out->msg[i] = 0; ++ sema_init(&mbox_out->sema[i], 0); ++ } ++ ++ /* Enable the interrupt on data reception */ ++ writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->config); ++ ++ mbox_out->magic = MBOX_MAGIC; ++} ++ ++static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ /* wait for the mailbox FIFO to have some space in it */ ++ while (0 != (readl(mbox->status) & ARM_MS_FULL)) ++ cpu_relax(); ++ ++ writel(MBOX_MSG(chan, data28), mbox->write); ++ rc = 0; ++ } ++ return rc; ++} ++ ++static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ down(&mbox->sema[chan]); ++ *data28 = MBOX_DATA28(mbox->msg[chan]); ++ mbox->msg[chan] = 0; ++ rc = 0; ++ } ++ return rc; ++} ++ ++static irqreturn_t mbox_irq(int irq, void *dev_id) ++{ ++ /* wait for the mailbox FIFO to have some data in it */ ++ struct vc_mailbox *mbox = (struct vc_mailbox *) dev_id; ++ int status = readl(mbox->status); ++ int ret = IRQ_NONE; ++ ++ while (!(status & ARM_MS_EMPTY)) { ++ uint32_t msg = readl(mbox->read); ++ int chan = MBOX_CHAN(msg); ++ if (chan < MBOX_CHAN_COUNT) { ++ if (mbox->msg[chan]) { ++ /* Overflow */ ++ printk(KERN_ERR DRIVER_NAME ++ ": mbox chan %d overflow - drop %08x\n", ++ chan, msg); ++ } else { ++ mbox->msg[chan] = (msg | 0xf); ++ up(&mbox->sema[chan]); ++ } ++ } else { ++ printk(KERN_ERR DRIVER_NAME ++ ": invalid channel selector (msg %08x)\n", msg); ++ } ++ ret = IRQ_HANDLED; ++ status = readl(mbox->status); ++ } ++ return ret; ++} ++ ++static struct irqaction mbox_irqaction = { ++ .name = "ARM Mailbox IRQ", ++ .flags = IRQF_DISABLED | IRQF_IRQPOLL, ++ .handler = mbox_irq, ++}; ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox Methods ++ * -------------------------------------------------------------------- */ ++ ++static struct device *mbox_dev; /* we assume there's only one! */ ++ ++static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_write(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_read(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++extern int bcm_mailbox_write(unsigned chan, uint32_t data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_write(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_write); ++ ++extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_read(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_read); ++ ++static void dev_mbox_register(const char *dev_name, struct device *dev) ++{ ++ mbox_dev = dev; ++} ++ ++static int mbox_copy_from_user(void *dst, const void *src, int size) ++{ ++ if ( (uint32_t)src < TASK_SIZE) ++ { ++ return copy_from_user(dst, src, size); ++ } ++ else ++ { ++ memcpy( dst, src, size ); ++ return 0; ++ } ++} ++ ++static int mbox_copy_to_user(void *dst, const void *src, int size) ++{ ++ if ( (uint32_t)dst < TASK_SIZE) ++ { ++ return copy_to_user(dst, src, size); ++ } ++ else ++ { ++ memcpy( dst, src, size ); ++ return 0; ++ } ++} ++ ++static DEFINE_MUTEX(mailbox_lock); ++extern int bcm_mailbox_property(void *data, int size) ++{ ++ uint32_t success; ++ dma_addr_t mem_bus; /* the memory address accessed from videocore */ ++ void *mem_kern; /* the memory address accessed from driver */ ++ int s = 0; ++ ++ mutex_lock(&mailbox_lock); ++ /* allocate some memory for the messages communicating with GPU */ ++ mem_kern = dma_alloc_coherent(NULL, PAGE_ALIGN(size), &mem_bus, GFP_ATOMIC); ++ if (mem_kern) { ++ /* create the message */ ++ mbox_copy_from_user(mem_kern, data, size); ++ ++ /* send the message */ ++ wmb(); ++ s = bcm_mailbox_write(MBOX_CHAN_PROPERTY, (uint32_t)mem_bus); ++ if (s == 0) { ++ s = bcm_mailbox_read(MBOX_CHAN_PROPERTY, &success); ++ } ++ if (s == 0) { ++ /* copy the response */ ++ rmb(); ++ mbox_copy_to_user(data, mem_kern, size); ++ } ++ dma_free_coherent(NULL, PAGE_ALIGN(size), mem_kern, mem_bus); ++ } else { ++ s = -ENOMEM; ++ } ++ if (s != 0) ++ printk(KERN_ERR DRIVER_NAME ": %s failed (%d)\n", __func__, s); ++ ++ mutex_unlock(&mailbox_lock); ++ return s; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_property); ++ ++/* ---------------------------------------------------------------------- ++ * Platform Device for Mailbox ++ * -------------------------------------------------------------------- */ ++ ++/* ++ * Is the device open right now? Used to prevent ++ * concurent access into the same device ++ */ ++static int Device_Open = 0; ++ ++/* ++ * This is called whenever a process attempts to open the device file ++ */ ++static int device_open(struct inode *inode, struct file *file) ++{ ++ /* ++ * We don't want to talk to two processes at the same time ++ */ ++ if (Device_Open) ++ return -EBUSY; ++ ++ Device_Open++; ++ /* ++ * Initialize the message ++ */ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int device_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * We're now ready for our next caller ++ */ ++ Device_Open--; ++ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++/* ++ * This function is called whenever a process tries to do an ioctl on our ++ * device file. We get two extra parameters (additional to the inode and file ++ * structures, which all device functions get): the number of the ioctl called ++ * and the parameter given to the ioctl function. ++ * ++ * If the ioctl is write or read/write (meaning output is returned to the ++ * calling process), the ioctl call returns the output of this function. ++ * ++ */ ++static long device_ioctl(struct file *file, /* see include/linux/fs.h */ ++ unsigned int ioctl_num, /* number and param for ioctl */ ++ unsigned long ioctl_param) ++{ ++ unsigned size; ++ /* ++ * Switch according to the ioctl called ++ */ ++ switch (ioctl_num) { ++ case IOCTL_MBOX_PROPERTY: ++ /* ++ * Receive a pointer to a message (in user space) and set that ++ * to be the device's message. Get the parameter given to ++ * ioctl by the process. ++ */ ++ mbox_copy_from_user(&size, (void *)ioctl_param, sizeof size); ++ return bcm_mailbox_property((void *)ioctl_param, size); ++ break; ++ default: ++ printk(KERN_ERR DRIVER_NAME "unknown ioctl: %d\n", ioctl_num); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* Module Declarations */ ++ ++/* ++ * This structure will hold the functions to be called ++ * when a process does something to the device we ++ * created. Since a pointer to this structure is kept in ++ * the devices table, it can't be local to ++ * init_module. NULL is for unimplemented functios. ++ */ ++struct file_operations fops = { ++ .unlocked_ioctl = device_ioctl, ++ .open = device_open, ++ .release = device_release, /* a.k.a. close */ ++}; ++ ++static int bcm_vcio_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_mailbox *mailbox; ++ ++ mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); ++ if (NULL == mailbox) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ ret = -ENOMEM; ++ } else { ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ kfree(mailbox); ++ } else { ++ /* should be based on the registers from res really */ ++ mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD); ++ ++ platform_set_drvdata(pdev, mailbox); ++ dev_mbox_register(DRIVER_NAME, &pdev->dev); ++ ++ mbox_irqaction.dev_id = mailbox; ++ setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction); ++ printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n", ++ __io_address(ARM_0_MAIL0_RD)); ++ } ++ } ++ ++ if (ret == 0) { ++ /* ++ * Register the character device ++ */ ++ ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops); ++ ++ /* ++ * Negative values signify an error ++ */ ++ if (ret < 0) { ++ printk(KERN_ERR DRIVER_NAME ++ "Failed registering the character device %d\n", ret); ++ return ret; ++ } ++ } ++ return ret; ++} ++ ++static int bcm_vcio_remove(struct platform_device *pdev) ++{ ++ struct vc_mailbox *mailbox = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ kfree(mailbox); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_mbox_driver = { ++ .probe = bcm_vcio_probe, ++ .remove = bcm_vcio_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init bcm_mbox_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n"); ++ ++ ret = platform_driver_register(&bcm_mbox_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_mbox_exit(void) ++{ ++ platform_driver_unregister(&bcm_mbox_driver); ++} ++ ++arch_initcall(bcm_mbox_init); /* Initialize early */ ++module_exit(bcm_mbox_exit); ++ ++MODULE_AUTHOR("Gray Girling"); ++MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm-mbox"); +diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S +index 8b4ee5e..80fa74b 100644 +--- a/arch/arm/mm/proc-v7.S ++++ b/arch/arm/mm/proc-v7.S +@@ -441,6 +441,7 @@ __v7_setup: + orr r0, r0, r6 @ set them + THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions + ret lr @ return to head.S:__ret ++ .space 256 + ENDPROC(__v7_setup) + + .align 2 +diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types +index c9ddd87..bfc397c 100644 +--- a/arch/arm/tools/mach-types ++++ b/arch/arm/tools/mach-types +@@ -523,6 +523,7 @@ prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103 + paz00 MACH_PAZ00 PAZ00 3128 + acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129 + bcm2708 MACH_BCM2708 BCM2708 3138 ++bcm2709 MACH_BCM2709 BCM2709 3139 + ag5evm MACH_AG5EVM AG5EVM 3189 + ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 + wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 +diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c +index a3025e7..4a2a601 100644 +--- a/drivers/clocksource/arm_arch_timer.c ++++ b/drivers/clocksource/arm_arch_timer.c +@@ -804,3 +804,39 @@ static void __init arch_timer_mem_init(struct device_node *np) + } + CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", + arch_timer_mem_init); ++ ++int __init dc4_arch_timer_init(void) ++{ ++ if (arch_timers_present & ARCH_CP15_TIMER) { ++ pr_warn("arch_timer: multiple nodes in dt, skipping\n"); ++ return -1; ++ } ++ ++ arch_timers_present |= ARCH_CP15_TIMER; ++ ++ /* Try to determine the frequency from the device tree or CNTFRQ */ ++ arch_timer_rate = 19200000; ++ ++ arch_timer_ppi[PHYS_SECURE_PPI] = IRQ_ARM_LOCAL_CNTPSIRQ; ++ arch_timer_ppi[PHYS_NONSECURE_PPI] = IRQ_ARM_LOCAL_CNTPNSIRQ; ++ arch_timer_ppi[VIRT_PPI] = IRQ_ARM_LOCAL_CNTVIRQ; ++ arch_timer_ppi[HYP_PPI] = IRQ_ARM_LOCAL_CNTHPIRQ; ++ ++ /* ++ * If HYP mode is available, we know that the physical timer ++ * has been configured to be accessible from PL1. Use it, so ++ * that a guest can use the virtual timer instead. ++ * ++ * If no interrupt provided for virtual timer, we'll have to ++ * stick to the physical timer. It'd better be accessible... ++ */ ++ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) { ++ arch_timer_use_virtual = false; ++ } ++ ++ arch_timer_c3stop = 0; ++ ++ arch_timer_register(); ++ arch_timer_common_init(); ++ return 0; ++} + +From 711ff7723bd270aff011ad323f9249486512bf6d Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 8 Oct 2014 18:50:05 +0100 -Subject: [PATCH 002/172] Add bcm2708_gpio driver +Subject: [PATCH 003/104] Add bcm2708_gpio driver Signed-off-by: popcornmix @@ -5715,226 +12587,10 @@ index 0000000..fb69624 + +#endif -From 559f0c63be158be62310100cbe3e45d224be9592 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 7 May 2013 22:20:24 +0100 -Subject: [PATCH 003/172] Add quick config. - -This is designed for quick compiling when developing. -No modules are needed and it includes all Pi specific drivers ---- - arch/arm/configs/bcmrpi_quick_defconfig | 197 ++++++++++++++++++++++++++++++++ - 1 file changed, 197 insertions(+) - create mode 100644 arch/arm/configs/bcmrpi_quick_defconfig - -diff --git a/arch/arm/configs/bcmrpi_quick_defconfig b/arch/arm/configs/bcmrpi_quick_defconfig -new file mode 100644 -index 0000000..e5efe75 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_quick_defconfig -@@ -0,0 +1,197 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_USB_USBNET=y -+# CONFIG_USB_NET_AX8817X is not set -+# CONFIG_USB_NET_CDCETHER is not set -+# CONFIG_USB_NET_CDC_NCM is not set -+CONFIG_USB_NET_SMSC95XX=y -+# CONFIG_USB_NET_NET1080 is not set -+# CONFIG_USB_NET_CDC_SUBSET is not set -+# CONFIG_USB_NET_ZAURUS is not set -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=y -+CONFIG_RAW_DRIVER=y -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_SND_BCM2835=y -+# CONFIG_SND_USB is not set -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_DWCOTG=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+CONFIG_DETECT_HUNG_TASK=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y - -From 08e12db8a1dcba12eeabca2b8eca4b4efa30f439 Mon Sep 17 00:00:00 2001 +From 950ea05f0c4da1ae960869be689d21dcef50365b Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:46:17 +0100 -Subject: [PATCH 004/172] Add dwc_otg driver +Subject: [PATCH 004/104] Add dwc_otg driver Signed-off-by: popcornmix @@ -5945,7 +12601,416 @@ Signed-off-by: Kari Suvanto usb: dwc: fix inconsistent lock state Signed-off-by: Kari Suvanto + +Add FIQ patch to dwc_otg driver. Enable with dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. +Thanks to Gordon and Costas + +Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005. + +Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh + +Make sure we wait for the reset to finish + +dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel + memory corruption, escalating to OOPS under high USB load. + +dwc_otg: Fix unsafe access of QTD during URB enqueue + +In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the +transaction could complete almost immediately after the qtd was assigned +to a host channel during URB enqueue, which meant the qtd pointer was no +longer valid having been completed and removed. Usually, this resulted in +an OOPS during URB submission. By predetermining whether transactions +need to be queued or not, this unsafe pointer access is avoided. + +This bug was only evident on the Pi model A where a device was attached +that had no periodic endpoints (e.g. USB pendrive or some wlan devices). + +dwc_otg: Fix incorrect URB allocation error handling + +If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS +because for some reason a member of the *unallocated* struct was set to +zero. Error handling changed to fail correctly. + +dwc_otg: fix potential use-after-free case in interrupt handler + +If a transaction had previously aborted, certain interrupts are +enabled to track error counts and reset where necessary. On IN +endpoints the host generates an ACK interrupt near-simultaneously +with completion of transfer. In the case where this transfer had +previously had an error, this results in a use-after-free on +the QTD memory space with a 1-byte length being overwritten to +0x00. + +dwc_otg: add handling of SPLIT transaction data toggle errors + +Previously a data toggle error on packets from a USB1.1 device behind +a TT would result in the Pi locking up as the driver never handled +the associated interrupt. Patch adds basic retry mechanism and +interrupt acknowledgement to cater for either a chance toggle error or +for devices that have a broken initial toggle state (FT8U232/FT232BM). + +dwc_otg: implement tasklet for returning URBs to usbcore hcd layer + +The dwc_otg driver interrupt handler for transfer completion will spend +a very long time with interrupts disabled when a URB is completed - +this is because usb_hcd_giveback_urb is called from within the handler +which for a USB device driver with complicated processing (e.g. webcam) +will take an exorbitant amount of time to complete. This results in +missed completion interrupts for other USB packets which lead to them +being dropped due to microframe overruns. + +This patch splits returning the URB to the usb hcd layer into a +high-priority tasklet. This will have most benefit for isochronous IN +transfers but will also have incidental benefit where multiple periodic +devices are active at once. + +dwc_otg: fix NAK holdoff and allow on split transactions only + +This corrects a bug where if a single active non-periodic endpoint +had at least one transaction in its qh, on frnum == MAX_FRNUM the qh +would get skipped and never get queued again. This would result in +a silent device until error detection (automatic or otherwise) would +either reset the device or flush and requeue the URBs. + +Additionally the NAK holdoff was enabled for all transactions - this +would potentially stall a HS endpoint for 1ms if a previous error state +enabled this interrupt and the next response was a NAK. Fix so that +only split transactions get held off. + +dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler + +usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it +asynchronously in the tasklet was not safe (regression in +c4564d4a1a0a9b10d4419e48239f5d99e88d2667). + +This change unlinks it from the endpoint prior to queueing it for handling in +the tasklet, and also adds a check to ensure the urb is OK to be unlinked +before doing so. + +NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb +when a USB device was unplugged/replugged during data transfer. This effect +was reproduced using automated USB port power control, hundreds of replug +events were performed during active transfers to confirm that the problem was +eliminated. + +USB fix using a FIQ to implement split transactions + +This commit adds a FIQ implementaion that schedules +the split transactions using a FIQ so we don't get +held off by the interrupt latency of Linux + +dwc_otg: fix device attributes and avoid kernel warnings on boot + +dcw_otg: avoid logging function that can cause panics + +See: https://github.com/raspberrypi/firmware/issues/21 +Thanks to cleverca22 for fix + +dwc_otg: mask correct interrupts after transaction error recovery + +The dwc_otg driver will unmask certain interrupts on a transaction +that previously halted in the error state in order to reset the +QTD error count. The various fine-grained interrupt handlers do not +consider that other interrupts besides themselves were unmasked. + +By disabling the two other interrupts only ever enabled in DMA mode +for this purpose, we can avoid unnecessary function calls in the +IRQ handler. This will also prevent an unneccesary FIQ interrupt +from being generated if the FIQ is enabled. + +dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ + +In the case of a transaction to a device that had previously aborted +due to an error, several interrupts are enabled to reset the error +count when a device responds. This has the side-effect of making the +FIQ thrash because the hardware will generate multiple instances of +a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK +on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the +associated interrupts. + +Additionally, on non-split transactions make sure that only unmasked +interrupts are cleared. This caused a hard-to-trigger but serious +race condition when you had the combination of an endpoint awaiting +error recovery and a transaction completed on an endpoint - due to +the sequencing and timing of interrupts generated by the dwc_otg core, +it was possible to confuse the IRQ handler. + +Fix function tracing + +dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue + +dwc_otg: prevent OOPSes during device disconnects + +The dwc_otg_urb_enqueue function is thread-unsafe. In particular the +access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and +friends does not occur within a critical section and so if a device +was unplugged during activity there was a high chance that the +usbcore hub_thread would try to disable the endpoint with partially- +formed entries in the URB queue. This would result in BUG() or null +pointer dereferences. + +Fix so that access of urb->hcpriv, enqueuing to the hardware and +adding to usbcore endpoint URB lists is contained within a single +critical section. + +dwc_otg: prevent BUG() in TT allocation if hub address is > 16 + +A fixed-size array is used to track TT allocation. This was +previously set to 16 which caused a crash because +dwc_otg_hcd_allocate_port would read past the end of the array. + +This was hit if a hub was plugged in which enumerated as addr > 16, +due to previous device resets or unplugs. + +Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows +to a large size if 128 hub addresses are supported. This field is +for debug only for tracking which frame an allocate happened in. + +dwc_otg: make channel halts with unknown state less damaging + +If the IRQ received a channel halt interrupt through the FIQ +with no other bits set, the IRQ would not release the host +channel and never complete the URB. + +Add catchall handling to treat as a transaction error and retry. + +dwc_otg: fiq_split: use TTs with more granularity + +This fixes certain issues with split transaction scheduling. + +- Isochronous multi-packet OUT transactions now hog the TT until + they are completed - this prevents hubs aborting transactions + if they get a periodic start-split out-of-order +- Don't perform TT allocation on non-periodic endpoints - this + allows simultaneous use of the TT's bulk/control and periodic + transaction buffers + +This commit will mainly affect USB audio playback. + +dwc_otg: fix potential sleep while atomic during urb enqueue + +Fixes a regression introduced with eb1b482a. Kmalloc called from +dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have +the GPF_ATOMIC flag set. Force this flag when inside the larger +critical section. + +dwc_otg: make fiq_split_enable imply fiq_fix_enable + +Failing to set up the FIQ correctly would result in +"IRQ 32: nobody cared" errors in dmesg. + +dwc_otg: prevent crashes on host port disconnects + +Fix several issues resulting in crashes or inconsistent state +if a Model A root port was disconnected. + +- Clean up queue heads properly in kill_urbs_in_qh_list by + removing the empty QHs from the schedule lists +- Set the halt status properly to prevent IRQ handlers from + using freed memory +- Add fiq_split related cleanup for saved registers +- Make microframe scheduling reclaim host channels if + active during a disconnect +- Abort URBs with -ESHUTDOWN status response, informing + device drivers so they respond in a more correct fashion + and don't try to resubmit URBs +- Prevent IRQ handlers from attempting to handle channel + interrupts if the associated URB was dequeued (and the + driver state was cleared) + +dwc_otg: prevent leaking URBs during enqueue + +A dwc_otg_urb would get leaked if the HCD enqueue function +failed for any reason. Free the URB at the appropriate points. + +dwc_otg: Enable NAK holdoff for control split transactions + +Certain low-speed devices take a very long time to complete a +data or status stage of a control transaction, producing NAK +responses until they complete internal processing - the USB2.0 +spec limit is up to 500mS. This causes the same type of interrupt +storm as seen with USB-serial dongles prior to c8edb238. + +In certain circumstances, usually while booting, this interrupt +storm could cause SD card timeouts. + +dwc_otg: Fix for occasional lockup on boot when doing a USB reset + +dwc_otg: Don't issue traffic to LS devices in FS mode + +Issuing low-speed packets when the root port is in full-speed mode +causes the root port to stop responding. Explicitly fail when +enqueuing URBs to a LS endpoint on a FS bus. + +Fix ARM architecture issue with local_irq_restore() + +If local_fiq_enable() is called before a local_irq_restore(flags) where +the flags variable has the F bit set, the FIQ will be erroneously disabled. + +Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR. + +Also fix some of the hacks previously implemented for previous dwc_otg +incarnations. + +dwc_otg: fiq_fsm: Base commit for driver rewrite + +This commit removes the previous FIQ fixes entirely and adds fiq_fsm. + +This rewrite features much more complete support for split transactions +and takes into account several OTG hardware bugs. High-speed +isochronous transactions are also capable of being performed by fiq_fsm. + +All driver options have been removed and replaced with: + - dwc_otg.fiq_enable (bool) + - dwc_otg.fiq_fsm_enable (bool) + - dwc_otg.fiq_fsm_mask (bitmask) + - dwc_otg.nak_holdoff (unsigned int) + +Defaults are specified such that fiq_fsm behaves similarly to the +previously implemented FIQ fixes. + +fiq_fsm: Push error recovery into the FIQ when fiq_fsm is used + +If the transfer associated with a QTD failed due to a bus error, the HCD +would retry the transfer up to 3 times (implementing the USB2.0 +three-strikes retry in software). + +Due to the masking mechanism used by fiq_fsm, it is only possible to pass +a single interrupt through to the HCD per-transfer. + +In this instance host channels would fall off the radar because the error +reset would function, but the subsequent channel halt would be lost. + +Push the error count reset into the FIQ handler. + +fiq_fsm: Implement timeout mechanism + +For full-speed endpoints with a large packet size, interrupt latency +runs the risk of the FIQ starting a transaction too late in a full-speed +frame. If the device is still transmitting data when EOF2 for the +downstream frame occurs, the hub will disable the port. This change is +not reflected in the hub status endpoint and the device becomes +unresponsive. + +Prevent high-bandwidth transactions from being started too late in a +frame. The mechanism is not guaranteed: a combination of bit stuffing +and hub latency may still result in a device overrunning. + +fiq_fsm: fix bounce buffer utilisation for Isochronous OUT + +Multi-packet isochronous OUT transactions were subject to a few bounday +bugs. Fix them. + +Audio playback is now much more robust: however, an issue stands with +devices that have adaptive sinks - ALSA plays samples too fast. + +dwc_otg: Return full-speed frame numbers in HS mode + +The frame counter increments on every *microframe* in high-speed mode. +Most device drivers expect this number to be in full-speed frames - this +caused considerable confusion to e.g. snd_usb_audio which uses the +frame counter to estimate the number of samples played. + +fiq_fsm: save PID on completion of interrupt OUT transfers + +Also add edge case handling for interrupt transports. + +Note that for periodic split IN, data toggles are unimplemented in the +OTG host hardware - it unconditionally accepts any PID. + +fiq_fsm: add missing case for fiq_fsm_tt_in_use() + +Certain combinations of bitrate and endpoint activity could +result in a periodic transaction erroneously getting started +while the previous Isochronous OUT was still active. + +fiq_fsm: clear hcintmsk for aborted transactions + +Prevents the FIQ from erroneously handling interrupts +on a timed out channel. + +fiq_fsm: enable by default + +fiq_fsm: fix dequeues for non-periodic split transactions + +If a dequeue happened between the SSPLIT and CSPLIT phases of the +transaction, the HCD would never receive an interrupt. + +fiq_fsm: Disable by default + +fiq_fsm: Handle HC babble errors + +The HCTSIZ transfer size field raises a babble interrupt if +the counter wraps. Handle the resulting interrupt in this case. + +dwc_otg: fix interrupt registration for fiq_enable=0 + +Additionally make the module parameter conditional for wherever +hcd->fiq_state is touched. + +fiq_fsm: Enable by default + +dwc_otg: Fix various issues with root port and transaction errors + +Process the host port interrupts correctly (and don't trample them). +Root port hotplug now functional again. + +Fix a few thinkos with the transaction error passthrough for fiq_fsm. + +fiq_fsm: Implement hack for Split Interrupt transactions + +Hubs aren't too picky about which endpoint we send Control type split +transactions to. By treating Interrupt transfers as Control, it is +possible to use the non-periodic queue in the OTG core as well as the +non-periodic FIFOs in the hub itself. This massively reduces the +microframe exclusivity/contention that periodic split transactions +otherwise have to enforce. + +It goes without saying that this is a fairly egregious USB specification +violation, but it works. + +Original idea by Hans Petter Selasky @ FreeBSD.org. + +dwc_otg: FIQ support on SMP. Set up FIQ stack and handler on Core 0 only. + +dwc_otg: introduce fiq_fsm_spin(un|)lock() + +SMP safety for the FIQ relies on register read-modify write cycles being +completed in the correct order. Several places in the DWC code modify +registers also touched by the FIQ. Protect these by a bare-bones lock +mechanism. + +This also makes it possible to run the FIQ and IRQ handlers on different +cores. + +fiq_fsm: fix build on bcm2708 and bcm2709 platforms + +dwc_otg: put some barriers back where they should be for UP + +bcm2709/dwc_otg: Setup FIQ on core 1 if >1 core active + +dwc_otg: fixup read-modify-write in critical paths + +Be more careful about read-modify-write on registers that the FIQ +also touches. + +Guard fiq_fsm_spin_lock with fiq_enable check + +fiq_fsm: Falling out of the state machine isn't fatal + +This edge case can be hit if the port is disabled while the FIQ is +in the middle of a transaction. Make the effects less severe. + +Also get rid of the useless return value. --- + arch/arm/Kconfig | 1 + + arch/arm/include/asm/irqflags.h | 16 +- + arch/arm/kernel/fiqasm.S | 4 + + arch/arm/mach-bcm2708/armctrl.c | 19 +- + arch/arm/mach-bcm2708/bcm2708.c | 15 +- + arch/arm/mach-bcm2708/include/mach/irqs.h | 153 +- + arch/arm/mach-bcm2709/armctrl.c | 10 +- drivers/usb/Makefile | 1 + drivers/usb/core/generic.c | 1 + drivers/usb/core/message.c | 79 + @@ -5961,7 +13026,7 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_common_port/dwc_cc.c | 532 ++ drivers/usb/host/dwc_common_port/dwc_cc.h | 224 + drivers/usb/host/dwc_common_port/dwc_common_fbsd.c | 1308 ++++ - .../usb/host/dwc_common_port/dwc_common_linux.c | 1429 ++++ + .../usb/host/dwc_common_port/dwc_common_linux.c | 1434 ++++ drivers/usb/host/dwc_common_port/dwc_common_nbsd.c | 1275 ++++ drivers/usb/host/dwc_common_port/dwc_crypto.c | 308 + drivers/usb/host/dwc_common_port/dwc_crypto.h | 111 + @@ -5973,9 +13038,9 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_common_port/dwc_modpow.h | 34 + drivers/usb/host/dwc_common_port/dwc_notifier.c | 319 + drivers/usb/host/dwc_common_port/dwc_notifier.h | 122 + - drivers/usb/host/dwc_common_port/dwc_os.h | 1274 ++++ + drivers/usb/host/dwc_common_port/dwc_os.h | 1276 ++++ drivers/usb/host/dwc_common_port/usb.h | 946 +++ - drivers/usb/host/dwc_otg/Makefile | 80 + + drivers/usb/host/dwc_otg/Makefile | 82 + drivers/usb/host/dwc_otg/doc/doxygen.cfg | 224 + drivers/usb/host/dwc_otg/dummy_audio.c | 1575 +++++ drivers/usb/host/dwc_otg/dwc_cfi_common.h | 142 + @@ -5985,32 +13050,35 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_otg/dwc_otg_attr.h | 89 + drivers/usb/host/dwc_otg/dwc_otg_cfi.c | 1876 +++++ drivers/usb/host/dwc_otg/dwc_otg_cfi.h | 320 + - drivers/usb/host/dwc_otg/dwc_otg_cil.c | 7151 ++++++++++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_cil.c | 7141 ++++++++++++++++++++ drivers/usb/host/dwc_otg/dwc_otg_cil.h | 1464 ++++ - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 1563 +++++ + drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 1594 +++++ drivers/usb/host/dwc_otg/dwc_otg_core_if.h | 705 ++ - drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 116 + - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1700 +++++ + drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 117 + + drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1749 +++++ drivers/usb/host/dwc_otg/dwc_otg_driver.h | 86 + - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 3479 ++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 824 +++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1133 ++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 412 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2106 ++++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 893 +++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 923 +++ - drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 185 + + drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1346 ++++ + drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 367 + + drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 + + drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 4252 ++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 862 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1132 ++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 417 ++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2713 ++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 994 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 957 +++ + drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 188 + drivers/usb/host/dwc_otg/dwc_otg_pcd.c | 2712 ++++++++ drivers/usb/host/dwc_otg/dwc_otg_pcd.h | 266 + drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h | 360 + drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 5147 ++++++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1358 ++++ + drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1360 ++++ drivers/usb/host/dwc_otg/dwc_otg_regs.h | 2550 +++++++ drivers/usb/host/dwc_otg/test/Makefile | 16 + drivers/usb/host/dwc_otg/test/dwc_otg_test.pm | 337 + drivers/usb/host/dwc_otg/test/test_mod_param.pl | 133 + drivers/usb/host/dwc_otg/test/test_sysfs.pl | 193 + - 64 files changed, 56458 insertions(+), 12 deletions(-) + 74 files changed, 60026 insertions(+), 97 deletions(-) create mode 100644 drivers/usb/gadget/file_storage.c create mode 100644 drivers/usb/host/dwc_common_port/Makefile create mode 100644 drivers/usb/host/dwc_common_port/Makefile.fbsd @@ -6051,6 +13119,9 @@ Signed-off-by: Kari Suvanto create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_dbg.h create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.c create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.c create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.h create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c @@ -6070,18 +13141,336 @@ Signed-off-by: Kari Suvanto create mode 100644 drivers/usb/host/dwc_otg/test/test_mod_param.pl create mode 100644 drivers/usb/host/dwc_otg/test/test_sysfs.pl +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4018fea..ee1bb5e 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -382,6 +382,7 @@ config ARCH_BCM2708 + select ARM_ERRATA_411920 + select MACH_BCM2708 + select VC4 ++ select FIQ + help + This enables support for Broadcom BCM2708 boards. + +diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h +index 3b763d6..5770408 100644 +--- a/arch/arm/include/asm/irqflags.h ++++ b/arch/arm/include/asm/irqflags.h +@@ -145,12 +145,22 @@ static inline unsigned long arch_local_save_flags(void) + } + + /* +- * restore saved IRQ & FIQ state ++ * restore saved IRQ state + */ + static inline void arch_local_irq_restore(unsigned long flags) + { +- asm volatile( +- " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore" ++ unsigned long temp = 0; ++ flags &= ~(1 << 6); ++ asm volatile ( ++ " mrs %0, cpsr" ++ : "=r" (temp) ++ : ++ : "memory", "cc"); ++ /* Preserve FIQ bit */ ++ temp &= (1 << 6); ++ flags = flags | temp; ++ asm volatile ( ++ " msr cpsr_c, %0 @ local_irq_restore" + : + : "r" (flags) + : "memory", "cc"); +diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S +index 8dd26e1..eef4847 100644 +--- a/arch/arm/kernel/fiqasm.S ++++ b/arch/arm/kernel/fiqasm.S +@@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs) + mov r0, r0 @ avoid hazard prior to ARMv4 + ret lr + ENDPROC(__get_fiq_regs) ++ ++ENTRY(__FIQ_Branch) ++ mov pc, r8 ++ENDPROC(__FIQ_Branch) +diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c +index ef1c8d5..96fa9b9 100644 +--- a/arch/arm/mach-bcm2708/armctrl.c ++++ b/arch/arm/mach-bcm2708/armctrl.c +@@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_data *d) + 0 + }; + +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); +- writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ if (d->irq >= FIQ_START) { ++ writel(0, __io_address(ARM_IRQ_FAST)); ++ } else { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ } + } + + static void armctrl_unmask_irq(struct irq_data *d) +@@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct irq_data *d) + 0 + }; + +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); +- writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ if (d->irq >= FIQ_START) { ++ unsigned int data = ++ (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); ++ } else { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ } + } + + #if defined(CONFIG_PM) +@@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, + } + + armctrl_pm_register(base, irq_start, resume_sources); ++ init_FIQ(FIQ_START); + return 0; + } +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 7503649..d9db081 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -291,12 +291,23 @@ static struct resource bcm2708_usb_resources[] = { + .flags = IORESOURCE_MEM, + }, + [1] = { +- .start = IRQ_USB, +- .end = IRQ_USB, ++ .start = MPHI_BASE, ++ .end = MPHI_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = IRQ_HOSTPORT, ++ .end = IRQ_HOSTPORT, + .flags = IORESOURCE_IRQ, + }, ++ [3] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, + }; + ++ + static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_usb_device = { +diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h +index 3a88a1a..45152ed 100644 +--- a/arch/arm/mach-bcm2708/include/mach/irqs.h ++++ b/arch/arm/mach-bcm2708/include/mach/irqs.h +@@ -106,87 +106,90 @@ + #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) + #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) + ++#define FIQ_START HARD_IRQS ++ + /* + * FIQ interrupts definitions are the same as the INT definitions. + */ +-#define FIQ_TIMER0 INT_TIMER0 +-#define FIQ_TIMER1 INT_TIMER1 +-#define FIQ_TIMER2 INT_TIMER2 +-#define FIQ_TIMER3 INT_TIMER3 +-#define FIQ_CODEC0 INT_CODEC0 +-#define FIQ_CODEC1 INT_CODEC1 +-#define FIQ_CODEC2 INT_CODEC2 +-#define FIQ_JPEG INT_JPEG +-#define FIQ_ISP INT_ISP +-#define FIQ_USB INT_USB +-#define FIQ_3D INT_3D +-#define FIQ_TRANSPOSER INT_TRANSPOSER +-#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 +-#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 +-#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 +-#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 +-#define FIQ_DMA0 INT_DMA0 +-#define FIQ_DMA1 INT_DMA1 +-#define FIQ_DMA2 INT_DMA2 +-#define FIQ_DMA3 INT_DMA3 +-#define FIQ_DMA4 INT_DMA4 +-#define FIQ_DMA5 INT_DMA5 +-#define FIQ_DMA6 INT_DMA6 +-#define FIQ_DMA7 INT_DMA7 +-#define FIQ_DMA8 INT_DMA8 +-#define FIQ_DMA9 INT_DMA9 +-#define FIQ_DMA10 INT_DMA10 +-#define FIQ_DMA11 INT_DMA11 +-#define FIQ_DMA12 INT_DMA12 +-#define FIQ_AUX INT_AUX +-#define FIQ_ARM INT_ARM +-#define FIQ_VPUDMA INT_VPUDMA +-#define FIQ_HOSTPORT INT_HOSTPORT +-#define FIQ_VIDEOSCALER INT_VIDEOSCALER +-#define FIQ_CCP2TX INT_CCP2TX +-#define FIQ_SDC INT_SDC +-#define FIQ_DSI0 INT_DSI0 +-#define FIQ_AVE INT_AVE +-#define FIQ_CAM0 INT_CAM0 +-#define FIQ_CAM1 INT_CAM1 +-#define FIQ_HDMI0 INT_HDMI0 +-#define FIQ_HDMI1 INT_HDMI1 +-#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 +-#define FIQ_I2CSPISLV INT_I2CSPISLV +-#define FIQ_DSI1 INT_DSI1 +-#define FIQ_PWA0 INT_PWA0 +-#define FIQ_PWA1 INT_PWA1 +-#define FIQ_CPR INT_CPR +-#define FIQ_SMI INT_SMI +-#define FIQ_GPIO0 INT_GPIO0 +-#define FIQ_GPIO1 INT_GPIO1 +-#define FIQ_GPIO2 INT_GPIO2 +-#define FIQ_GPIO3 INT_GPIO3 +-#define FIQ_I2C INT_I2C +-#define FIQ_SPI INT_SPI +-#define FIQ_I2SPCM INT_I2SPCM +-#define FIQ_SDIO INT_SDIO +-#define FIQ_UART INT_UART +-#define FIQ_SLIMBUS INT_SLIMBUS +-#define FIQ_VEC INT_VEC +-#define FIQ_CPG INT_CPG +-#define FIQ_RNG INT_RNG +-#define FIQ_ARASANSDIO INT_ARASANSDIO +-#define FIQ_AVSPMON INT_AVSPMON ++#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) ++#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) ++#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) ++#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) ++#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) ++#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) ++#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) ++#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) ++#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) ++#define FIQ_USB (FIQ_START+INTERRUPT_USB) ++#define FIQ_3D (FIQ_START+INTERRUPT_3D) ++#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) ++#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) ++#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) ++#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) ++#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) ++#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) ++#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) ++#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) ++#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) ++#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) ++#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) ++#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) ++#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) ++#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) ++#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) ++#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) ++#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) ++#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) ++#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) ++#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) ++#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) ++#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) ++#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) ++#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) ++#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) ++#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) ++#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) ++#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) ++#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) ++#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) ++#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) ++#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) ++#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) ++#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) ++#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) ++#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) ++#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) ++#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) ++#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) ++#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) ++#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) ++#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) ++#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) ++#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) ++#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) ++#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) ++#define FIQ_UART (FIQ_START+INTERRUPT_UART) ++#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) ++#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) ++#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) ++#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) ++#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) ++#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) + +-#define FIQ_ARM_TIMER INT_ARM_TIMER +-#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX +-#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 +-#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 +-#define FIQ_VPU0_HALTED INT_VPU0_HALTED +-#define FIQ_VPU1_HALTED INT_VPU1_HALTED +-#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 +-#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 +-#define FIQ_PENDING1 INT_PENDING1 +-#define FIQ_PENDING2 INT_PENDING2 ++#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) ++#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) ++#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) ++#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) ++#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) ++#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) ++#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) ++#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) ++#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) ++#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) + + #define HARD_IRQS (64 + 21) +-#define GPIO_IRQ_START (HARD_IRQS) ++#define FIQ_IRQS (64 + 21) ++#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) + #define GPIO_IRQS (32*5) + #define SPARE_ALLOC_IRQS 64 + #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) +diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c +index 2815b64..fc6cb8b 100644 +--- a/arch/arm/mach-bcm2709/armctrl.c ++++ b/arch/arm/mach-bcm2709/armctrl.c +@@ -91,7 +91,15 @@ static void armctrl_unmask_irq(struct irq_data *d) + }; + int i; + if (d->irq >= FIQ_START) { +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ unsigned int data; ++ if (num_online_cpus() > 1) { ++ data = readl(__io_address(ARM_LOCAL_GPU_INT_ROUTING)); ++ data &= ~0xc; ++ data |= (1 << 2); ++ writel(data, __io_address(ARM_LOCAL_GPU_INT_ROUTING)); ++ } ++ /* Unmask in ARMCTRL block after routing it properly */ ++ data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; + writel(0x80 | data, __io_address(ARM_IRQ_FAST)); + } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { + #if 1 diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile -index d7be717..f48b630 100644 +index 2f1e2aa..887ca56 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile -@@ -24,6 +24,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/ - obj-$(CONFIG_USB_R8A66597_HCD) += host/ - obj-$(CONFIG_USB_HWA_HCD) += host/ - obj-$(CONFIG_USB_ISP1760_HCD) += host/ +@@ -6,6 +6,7 @@ + + obj-$(CONFIG_USB) += core/ + +obj-$(CONFIG_USB_DWCOTG) += host/ - obj-$(CONFIG_USB_IMX21_HCD) += host/ - obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ - obj-$(CONFIG_USB_FUSBH200_HCD) += host/ + obj-$(CONFIG_USB_DWC3) += dwc3/ + obj-$(CONFIG_USB_DWC2) += dwc2/ + obj-$(CONFIG_USB_ISP1760) += isp1760/ diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 358ca8d..abaac7c 100644 --- a/drivers/usb/core/generic.c @@ -10024,10 +17413,10 @@ index 0000000..a896d73 +} +module_exit(fsg_cleanup); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig -index fafc628..83c34af 100644 +index 5ad60e4..fb5b986 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig -@@ -752,6 +752,19 @@ config USB_HWA_HCD +@@ -738,6 +738,19 @@ config USB_HWA_HCD To compile this driver a module, choose M here: the module will be called "hwa-hc". @@ -10048,12 +17437,12 @@ index fafc628..83c34af 100644 tristate "i.MX21 HCD support" depends on ARM && ARCH_MXC diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile -index d6216a4..c6dfede 100644 +index 65b0b6a..49399f2 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile -@@ -71,6 +71,8 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o +@@ -68,6 +68,8 @@ obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o + obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o - obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o + +obj-$(CONFIG_USB_DWCOTG) += dwc_otg/ dwc_common_port/ @@ -12742,10 +20131,10 @@ index 0000000..6dd04b5 +} diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c new file mode 100644 -index 0000000..1668f10 +index 0000000..b802042 --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -0,0 +1,1429 @@ +@@ -0,0 +1,1434 @@ +#include +#include +#include @@ -13743,6 +21132,11 @@ index 0000000..1668f10 + tasklet_schedule(&task->t); +} + ++void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task) ++{ ++ tasklet_hi_schedule(&task->t); ++} ++ + +/* workqueues + - run in process context (can sleep) @@ -16298,7 +23692,7 @@ index 0000000..25c1cc0 +#endif /* _DWC_DH_H_ */ diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h new file mode 100644 -index 0000000..89cc325 +index 0000000..4ce560d --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_list.h @@ -0,0 +1,594 @@ @@ -16688,17 +24082,17 @@ index 0000000..89cc325 +#define DWC_TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define DWC_TAILQ_EMPTY(head) \ -+ (TAILQ_FIRST(head) == TAILQ_END(head)) ++ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head)) + +#define DWC_TAILQ_FOREACH(var, head, field) \ -+ for((var) = TAILQ_FIRST(head); \ -+ (var) != TAILQ_END(head); \ -+ (var) = TAILQ_NEXT(var, field)) ++ for ((var) = DWC_TAILQ_FIRST(head); \ ++ (var) != DWC_TAILQ_END(head); \ ++ (var) = DWC_TAILQ_NEXT(var, field)) + +#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ -+ for((var) = TAILQ_LAST(head, headname); \ -+ (var) != TAILQ_END(head); \ -+ (var) = TAILQ_PREV(var, headname, field)) ++ for ((var) = DWC_TAILQ_LAST(head, headname); \ ++ (var) != DWC_TAILQ_END(head); \ ++ (var) = DWC_TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. @@ -18284,10 +25678,10 @@ index 0000000..4a8cdfe +#endif /* __DWC_NOTIFIER_H__ */ diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h new file mode 100644 -index 0000000..8117731 +index 0000000..a2bbe23 --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_os.h -@@ -0,0 +1,1274 @@ +@@ -0,0 +1,1276 @@ +/* ========================================================================= + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $ + * $Revision: #14 $ @@ -19272,6 +26666,8 @@ index 0000000..8117731 +extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); +#define dwc_task_schedule DWC_TASK_SCHEDULE + ++extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task); ++#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE + +/** @name Timer + * @@ -20516,10 +27912,10 @@ index 0000000..27bda82 +#endif /* _USB_H_ */ diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile new file mode 100644 -index 0000000..236c47c +index 0000000..e7bdd12 --- /dev/null +++ b/drivers/usb/host/dwc_otg/Makefile -@@ -0,0 +1,80 @@ +@@ -0,0 +1,82 @@ +# +# Makefile for DWC_otg Highspeed USB controller driver +# @@ -20558,6 +27954,8 @@ index 0000000..236c47c +dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o +dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o +dwc_otg-objs += dwc_otg_adp.o ++dwc_otg-objs += dwc_otg_fiq_fsm.o ++dwc_otg-objs += dwc_otg_fiq_stub.o +ifneq ($(CFI),) +dwc_otg-objs += dwc_otg_cfi.o +endif @@ -23507,7 +30905,7 @@ index 0000000..4110b25 +#endif //__DWC_OTG_ADP_H__ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_attr.c b/drivers/usb/host/dwc_otg/dwc_otg_attr.c new file mode 100644 -index 0000000..fab2961 +index 0000000..9da0c92 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c @@ -0,0 +1,1210 @@ @@ -24422,7 +31820,7 @@ index 0000000..fab2961 + return sprintf(buf, "Register Dump\n"); +} + -+DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); ++DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0); + +/** + * Dump global registers and either host or device registers (depending on the @@ -24433,12 +31831,12 @@ index 0000000..fab2961 +{ + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev); + -+ dwc_otg_dump_spram(otg_dev->core_if); ++ //dwc_otg_dump_spram(otg_dev->core_if); + + return sprintf(buf, "SPRAM Dump\n"); +} + -+DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); ++DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0); + +/** + * Dump the current hcd state. @@ -24453,7 +31851,7 @@ index 0000000..fab2961 + return sprintf(buf, "HCD Dump\n"); +} + -+DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); ++DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0); + +/** + * Dump the average frame remaining at SOF. This can be used to @@ -24471,7 +31869,7 @@ index 0000000..fab2961 + return sprintf(buf, "HCD Dump Frame Remaining\n"); +} + -+DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); ++DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0); + +/** + * Displays the time required to read the GNPTXFSIZ register many times (the @@ -24499,7 +31897,7 @@ index 0000000..fab2961 + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); +} + -+DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); ++DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0); + +/** + * Displays the time required to write the GNPTXFSIZ register many times (the @@ -24527,7 +31925,7 @@ index 0000000..fab2961 + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); +} + -+DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); ++DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0); + +#ifdef CONFIG_USB_DWC_OTG_LPM + @@ -27026,10 +34424,10 @@ index 0000000..55fd337 +#endif /* (__DWC_OTG_CFI_H__) */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil.c b/drivers/usb/host/dwc_otg/dwc_otg_cil.c new file mode 100644 -index 0000000..6a32c5c +index 0000000..e40060f --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -@@ -0,0 +1,7151 @@ +@@ -0,0 +1,7141 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ + * $Revision: #191 $ @@ -29276,9 +36674,7 @@ index 0000000..6a32c5c + */ +void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) +{ -+ uint32_t intr_enable; + hcintmsk_data_t hc_intr_mask; -+ gintmsk_data_t gintmsk = {.d32 = 0 }; + hcchar_data_t hcchar; + hcsplt_data_t hcsplt; + @@ -29380,14 +36776,6 @@ index 0000000..6a32c5c + } + DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32); + -+ /* Enable the top level host channel interrupt. */ -+ intr_enable = (1 << hc_num); -+ DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable); -+ -+ /* Make sure host channel interrupts are enabled. */ -+ gintmsk.b.hcintr = 1; -+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ + /* + * Program the HCCHARn register with the endpoint characteristics for + * the current transfer. @@ -35653,10 +43041,10 @@ index 0000000..79dbf83 +#endif diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c new file mode 100644 -index 0000000..59fc862 +index 0000000..96c76e3 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -0,0 +1,1563 @@ +@@ -0,0 +1,1594 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $ + * $Revision: #32 $ @@ -36977,7 +44365,7 @@ index 0000000..59fc862 +/** + * This function returns the Core Interrupt register. + */ -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd) +{ + gahbcfg_data_t gahbcfg = {.d32 = 0 }; + gintsts_data_t gintsts; @@ -36994,26 +44382,45 @@ index 0000000..59fc862 + gintmsk_common.b.lpmtranrcvd = 1; +#endif + gintmsk_common.b.restoredone = 1; -+ /** @todo: The port interrupt occurs while in device -+ * mode. Added code to CIL to clear the interrupt for now! -+ */ -+ gintmsk_common.b.portintr = 1; -+ ++ if(dwc_otg_is_device_mode(core_if)) ++ { ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ } + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ if(fiq_enable) { ++ local_fiq_disable(); ++ /* Pull in the interrupts that the FIQ has masked */ ++ gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); ++ gintmsk.d32 |= gintmsk_common.d32; ++ /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ ++ reenable_gintmsk->d32 = gintmsk.d32; ++ local_fiq_enable(); ++ } ++ + gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); + +#ifdef DEBUG + /* if any common interrupts set */ + if (gintsts.d32 & gintmsk_common.d32) { -+ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n", + gintsts.d32, gintmsk.d32); + } +#endif -+ if (gahbcfg.b.glblintrmsk) ++ if (!fiq_enable){ ++ if (gahbcfg.b.glblintrmsk) ++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); ++ else ++ return 0; ++ } else { ++ /* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface. ++ * Can't trust the global interrupt mask bit in this case. ++ */ + return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -+ else -+ return 0; ++ } + +} + @@ -37045,6 +44452,7 @@ index 0000000..59fc862 +{ + int retval = 0; + gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk_reenable = { .d32 = 0 }; + gpwrdn_data_t gpwrdn = {.d32 = 0 }; + dwc_otg_device_t *otg_dev = dev; + dwc_otg_core_if_t *core_if = otg_dev->core_if; @@ -37066,7 +44474,10 @@ index 0000000..59fc862 + } + + if (core_if->hibernation_suspend <= 0) { -+ gintsts.d32 = dwc_otg_read_common_intr(core_if); ++ /* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end ++ * of this handler - god only knows why it's done like this ++ */ ++ gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd); + + if (gintsts.b.modemismatch) { + retval |= dwc_otg_handle_mode_mismatch_intr(core_if); @@ -37163,8 +44574,17 @@ index 0000000..59fc862 + gintsts.b.portintr = 1; + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); + retval |= 1; ++ gintmsk_reenable.b.portintr = 1; + + } ++ /* Did we actually handle anything? if so, unmask the interrupt */ ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); ++ if (retval && fiq_enable) { ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); ++ } ++ + } else { + DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); + @@ -37217,7 +44637,6 @@ index 0000000..59fc862 + } + if (core_if->lock) + DWC_SPINUNLOCK(core_if->lock); -+ + return retval; +} diff --git a/drivers/usb/host/dwc_otg/dwc_otg_core_if.h b/drivers/usb/host/dwc_otg/dwc_otg_core_if.h @@ -37933,10 +45352,10 @@ index 0000000..4138fd1 +#endif /* __DWC_CORE_IF_H__ */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h new file mode 100644 -index 0000000..8900318 +index 0000000..ccc24e0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -@@ -0,0 +1,116 @@ +@@ -0,0 +1,117 @@ +/* ========================================================================== + * + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, @@ -37988,6 +45407,7 @@ index 0000000..8900318 + return old; +} + ++#define DBG_USER (0x1) +/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ +#define DBG_CIL (0x2) +/** When debug level has the DBG_CILV bit set, display CIL Verbose debug @@ -38055,10 +45475,10 @@ index 0000000..8900318 +#endif diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c new file mode 100644 -index 0000000..ac2c846 +index 0000000..dc7cd32 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -0,0 +1,1700 @@ +@@ -0,0 +1,1749 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $ + * $Revision: #92 $ @@ -38117,6 +45537,7 @@ index 0000000..ac2c846 +#include "dwc_otg_core_if.h" +#include "dwc_otg_pcd_if.h" +#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_fiq_fsm.h" + +#define DWC_DRIVER_VERSION "3.00a 10-AUG-2012" +#define DWC_DRIVER_DESC "HS OTG USB Controller driver" @@ -38125,6 +45546,7 @@ index 0000000..ac2c846 + +static const char dwc_driver_name[] = "dwc_otg"; + ++ +extern int pcd_init( +#ifdef LM_INTERFACE + struct lm_device *_dev @@ -38299,6 +45721,15 @@ index 0000000..ac2c846 + .adp_enable = -1, +}; + ++//Global variable to switch the fiq fix on or off ++bool fiq_enable = 1; ++// Global variable to enable the split transaction fix ++bool fiq_fsm_enable = true; ++//Bulk split-transaction NAK holdoff in microframes ++uint16_t nak_holdoff = 8; ++ ++unsigned short fiq_fsm_mask = 0x07; ++ +/** + * This function shows the Driver Version. + */ @@ -38840,17 +46271,32 @@ index 0000000..ac2c846 + _dev->resource->start, + _dev->resource->end - _dev->resource->start + 1); +#if 1 -+ if (!request_mem_region(_dev->resource->start, -+ _dev->resource->end - _dev->resource->start + 1, ++ if (!request_mem_region(_dev->resource[0].start, ++ _dev->resource[0].end - _dev->resource[0].start + 1, + "dwc_otg")) { + dev_dbg(&_dev->dev, "error reserving mapped memory\n"); + retval = -EFAULT; + goto fail; + } + -+ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start, -+ _dev->resource->end - -+ _dev->resource->start+1); ++ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, ++ _dev->resource[0].end - ++ _dev->resource[0].start+1); ++ if (fiq_enable) ++ { ++ if (!request_mem_region(_dev->resource[1].start, ++ _dev->resource[1].end - _dev->resource[1].start + 1, ++ "dwc_otg")) { ++ dev_dbg(&_dev->dev, "error reserving mapped memory\n"); ++ retval = -EFAULT; ++ goto fail; ++ } ++ ++ dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, ++ _dev->resource[1].end - ++ _dev->resource[1].start + 1); ++ } ++ +#else + { + struct map_desc desc = { @@ -38937,9 +46383,9 @@ index 0000000..ac2c846 + */ + +#if defined(PLATFORM_INTERFACE) -+ devirq = platform_get_irq(_dev, 0); ++ devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1); +#else -+ devirq = _dev->irq; ++ devirq = _dev->irq; +#endif + DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", + devirq); @@ -39105,6 +46551,12 @@ index 0000000..ac2c846 + int retval = 0; + int error; + struct device_driver *drv; ++ ++ if(fiq_fsm_enable && !fiq_enable) { ++ printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n"); ++ fiq_enable = 1; ++ } ++ + printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, + DWC_DRIVER_VERSION, +#ifdef LM_INTERFACE @@ -39124,6 +46576,9 @@ index 0000000..ac2c846 + printk(KERN_ERR "%s retval=%d\n", __func__, retval); + return retval; + } ++ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled"); ++ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled"); ++ printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled"); + + error = driver_create_file(drv, &driver_attr_version); +#ifdef DEBUG @@ -39404,6 +46859,20 @@ index 0000000..ac2c846 +module_param(microframe_schedule, bool, 0444); +MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); + ++module_param(fiq_enable, bool, 0444); ++MODULE_PARM_DESC(fiq_enable, "Enable the FIQ"); ++module_param(nak_holdoff, ushort, 0644); ++MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8"); ++module_param(fiq_fsm_enable, bool, 0444); ++MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask"); ++module_param(fiq_fsm_mask, ushort, 0444); ++MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n" ++ "Bit 0 : Non-periodic split transactions\n" ++ "Bit 1 : Periodic split transactions\n" ++ "Bit 2 : High-speed multi-transfer isochronous\n" ++ "All other bits should be set 0."); ++ ++ +/** @page "Module Parameters" + * + * The following parameters may be specified when starting the module. @@ -39851,12 +47320,1823 @@ index 0000000..6a8be63 +#endif + +#endif +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c +new file mode 100644 +index 0000000..276ad0c7 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c +@@ -0,0 +1,1346 @@ ++/* ++ * dwc_otg_fiq_fsm.c - The finite state machine FIQ ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This FIQ implements functionality that performs split transactions on ++ * the dwc_otg hardware without any outside intervention. A split transaction ++ * is "queued" by nominating a specific host channel to perform the entirety ++ * of a split transaction. This FIQ will then perform the microframe-precise ++ * scheduling required in each phase of the transaction until completion. ++ * ++ * The FIQ functionality is glued into the Synopsys driver via the entry point ++ * in the FSM enqueue function, and at the exit point in handling a HC interrupt ++ * for a FSM-enabled channel. ++ * ++ * NB: Large parts of this implementation have architecture-specific code. ++ * For porting this functionality to other ARM machines, the minimum is required: ++ * - An interrupt controller allowing the top-level dwc USB interrupt to be routed ++ * to the FIQ ++ * - A method of forcing a software generated interrupt from FIQ mode that then ++ * triggers an IRQ entry (with the dwc USB handler called by this IRQ number) ++ * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same ++ * processor core - there is no locking between the FIQ and IRQ (aside from ++ * local_fiq_disable) ++ * ++ */ ++ ++#include "dwc_otg_fiq_fsm.h" ++ ++ ++char buffer[1000*16]; ++int wptr; ++void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...) ++{ ++ enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR; ++ va_list args; ++ char text[17]; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) }; ++ ++ if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR) ++ { ++ snprintf(text, 9, " %4d:%1u ", hfnum.b.frnum/8, hfnum.b.frnum & 7); ++ va_start(args, fmt); ++ vsnprintf(text+8, 9, fmt, args); ++ va_end(args); ++ ++ memcpy(buffer + wptr, text, 16); ++ wptr = (wptr + 16) % sizeof(buffer); ++ } ++} ++ ++/** ++ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock ++ * Must be called with local interrupts and FIQ disabled. ++ */ ++#ifdef CONFIG_ARCH_BCM2709 ++inline void fiq_fsm_spin_lock(fiq_lock_t *lock) ++{ ++ unsigned long tmp; ++ uint32_t newval; ++ fiq_lock_t lockval; ++ smp_mb__before_spinlock(); ++ /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable ++ * will be sufficient. If we are on a different CPU, then the lock protects us. */ ++ prefetchw(&lock->slock); ++ asm volatile ( ++ "1: ldrex %0, [%3]\n" ++ " add %1, %0, %4\n" ++ " strex %2, %1, [%3]\n" ++ " teq %2, #0\n" ++ " bne 1b" ++ : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) ++ : "r" (&lock->slock), "I" (1 << 16) ++ : "cc"); ++ ++ while (lockval.tickets.next != lockval.tickets.owner) { ++ wfe(); ++ lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); ++ } ++ smp_mb(); ++} ++#else ++inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { } ++#endif ++ ++/** ++ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock ++ */ ++#ifdef CONFIG_ARCH_BCM2709 ++inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) ++{ ++ smp_mb(); ++ lock->tickets.owner++; ++ dsb_sev(); ++} ++#else ++inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { } ++#endif ++ ++/** ++ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction ++ * @channel: channel to re-enable ++ */ ++static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force) ++{ ++ hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) }; ++ ++ hcchar.b.chen = 0; ++ if (st->channel[n].hcchar_copy.b.eptype & 0x1) { ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ /* Hardware bug workaround: update the ssplit index */ ++ if (st->channel[n].hcsplt_copy.b.spltena) ++ st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; ++ ++ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++ } ++ ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); ++ hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ hcchar.b.chen = 1; ++ ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); ++ fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force); ++} ++ ++/** ++ * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage ++ * @st: Pointer to the channel's state ++ * @n : channel number ++ * ++ * Change host channel registers to perform a complete-split transaction. Being mindful of the ++ * endpoint direction, set control regs up correctly. ++ */ ++static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n) ++{ ++ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) }; ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ ++ hcsplt.b.compsplt = 1; ++ if (st->channel[n].hcchar_copy.b.epdir == 1) { ++ // If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket. ++ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; ++ } else { ++ // If OUT, the CSPLIT result contains handshake only. ++ hctsiz.b.xfersize = 0; ++ } ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++ mb(); ++} ++ ++static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n) ++{ ++ /* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */ ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ ++ if (st->channel[n].hcchar_copy.b.epdir == 0) { ++ return st->channel[n].hctsiz_copy.b.xfersize; ++ } else { ++ return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize; ++ } ++ ++} ++ ++ ++/** ++ * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT ++ * ++ * Of use only for IN periodic transfers. ++ */ ++static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n) ++{ ++ hcdma_data_t hcdma; ++ int i = st->channel[n].dma_info.index; ++ int len; ++ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; ++ ++ len = fiq_get_xfer_len(st, n); ++ fiq_print(FIQDBG_INT, st, "LEN: %03d", len); ++ st->channel[n].dma_info.slot_len[i] = len; ++ i++; ++ if (i > 6) ++ BUG(); ++ ++ hcdma.d32 = (dma_addr_t) &blob->channel[n].index[i].buf[0]; ++ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ st->channel[n].dma_info.index = i; ++ return 0; ++} ++ ++/** ++ * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ ++ */ ++static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n) ++{ ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; ++ hctsiz.b.pktcnt = 1; ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++} ++ ++/** ++ * fiq_iso_out_advance() - update DMA address and split position bits ++ * for isochronous OUT transactions. ++ * ++ * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and ++ * Split-BEGIN states are not handled - this is done when the transaction was queued. ++ * ++ * This function must only be called from the FIQ_ISO_OUT_ACTIVE state. ++ */ ++static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n) ++{ ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ hcdma_data_t hcdma; ++ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; ++ int last = 0; ++ int i = st->channel[n].dma_info.index; ++ ++ fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i); ++ i++; ++ if (i == 4) ++ last = 1; ++ if (st->channel[n].dma_info.slot_len[i+1] == 255) ++ last = 1; ++ ++ /* New DMA address - address of bounce buffer referred to in index */ ++ hcdma.d32 = (uint32_t) &blob->channel[n].index[i].buf[0]; ++ //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n)); ++ //hcdma.d32 += st->channel[n].dma_info.slot_len[i]; ++ fiq_print(FIQDBG_INT, st, "LAST: %01d ", last); ++ fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]); ++ hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT); ++ hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ); ++ hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID; ++ /* Set up new packet length */ ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i]; ++ fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32); ++ ++ st->channel[n].dma_info.index++; ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ return last; ++} ++ ++/** ++ * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT ++ * ++ * Despite the limitations of the DWC core, we can force a microframe pipeline of ++ * isochronous OUT start-split transactions while waiting for a corresponding other-type ++ * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it ++ * is very unlikely that filling the start-split FIFO will cause data loss. ++ * This allows much better interleaving of transactions in an order-independent way- ++ * there is no requirement to prioritise isochronous, just a state-space search has ++ * to be performed on each periodic start-split complete interrupt. ++ */ ++static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n) ++{ ++ int hub_addr = st->channel[n].hub_addr; ++ int port_addr = st->channel[n].port_addr; ++ int i, poked = 0; ++ for (i = 0; i < num_channels; i++) { ++ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ if (st->channel[i].hub_addr == hub_addr && ++ st->channel[i].port_addr == port_addr) { ++ switch (st->channel[i].fsm) { ++ case FIQ_PER_ISO_OUT_PENDING: ++ if (st->channel[i].nrpackets == 1) { ++ st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ fiq_fsm_restart_channel(st, i, 0); ++ poked = 1; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ if (poked) ++ break; ++ } ++ return poked; ++} ++ ++/** ++ * fiq_fsm_tt_in_use() - search for host channels using this TT ++ * @n: Channel to use as reference ++ * ++ */ ++int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n) ++{ ++ int hub_addr = st->channel[n].hub_addr; ++ int port_addr = st->channel[n].port_addr; ++ int i, in_use = 0; ++ for (i = 0; i < num_channels; i++) { ++ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ switch (st->channel[i].fsm) { ++ /* TT is reserved for channels that are in the middle of a periodic ++ * split transaction. ++ */ ++ case FIQ_PER_SSPLIT_STARTED: ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ //case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ case FIQ_PER_ISO_OUT_LAST: ++ if (st->channel[i].hub_addr == hub_addr && ++ st->channel[i].port_addr == port_addr) { ++ in_use = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ if (in_use) ++ break; ++ } ++ return in_use; ++} ++ ++/** ++ * fiq_fsm_more_csplits() - determine whether additional CSPLITs need ++ * to be issued for this IN transaction. ++ * ++ * We cannot tell the inbound PID of a data packet due to hardware limitations. ++ * we need to make an educated guess as to whether we need to queue another CSPLIT ++ * or not. A no-brainer is when we have received enough data to fill the endpoint ++ * size, but for endpoints that give variable-length data then we have to resort ++ * to heuristics. ++ * ++ * We also return whether this is the last CSPLIT to be queued, again based on ++ * heuristics. This is to allow a 1-uframe overlap of periodic split transactions. ++ * Note: requires at least 1 CSPLIT to have been performed prior to being called. ++ */ ++ ++/* ++ * We need some way of guaranteeing if a returned periodic packet of size X ++ * has a DATA0 PID. ++ * The heuristic value of 144 bytes assumes that the received data has maximal ++ * bit-stuffing and the clock frequency of the transmitting device is at the lowest ++ * permissible limit. If the transfer length results in a final packet size ++ * 144 < p <= 188, then an erroneous CSPLIT will be issued. ++ * Also used to ensure that an endpoint will nominally only return a single ++ * complete-split worth of data. ++ */ ++#define DATA0_PID_HEURISTIC 144 ++ ++static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last) ++{ ++ ++ int i; ++ int total_len = 0; ++ int more_needed = 1; ++ struct fiq_channel_state *st = &state->channel[n]; ++ ++ for (i = 0; i < st->dma_info.index; i++) { ++ total_len += st->dma_info.slot_len[i]; ++ } ++ ++ *probably_last = 0; ++ ++ if (st->hcchar_copy.b.eptype == 0x3) { ++ /* ++ * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data ++ * then this is definitely the last CSPLIT. ++ */ ++ *probably_last = 1; ++ } else { ++ /* Isoc IN. This is a bit risky if we are the first transaction: ++ * we may have been held off slightly. */ ++ if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) { ++ more_needed = 0; ++ } ++ /* If in the next uframe we will receive enough data to fill the endpoint, ++ * then only issue 1 more csplit. ++ */ ++ if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC) ++ *probably_last = 1; ++ } ++ ++ if (total_len >= st->hctsiz_copy.b.xfersize || ++ i == 6 || total_len == 0) ++ /* Note: due to bit stuffing it is possible to have > 6 CSPLITs for ++ * a single endpoint. Accepting more would completely break our scheduling mechanism though ++ * - in these extreme cases we will pass through a truncated packet. ++ */ ++ more_needed = 0; ++ ++ return more_needed; ++} ++ ++/** ++ * fiq_fsm_too_late() - Test transaction for lateness ++ * ++ * If a SSPLIT for a large IN transaction is issued too late in a frame, ++ * the hub will disable the port to the device and respond with ERR handshakes. ++ * The hub status endpoint will not reflect this change. ++ * Returns 1 if we will issue a SSPLIT that will result in a device babble. ++ */ ++int notrace fiq_fsm_too_late(struct fiq_state *st, int n) ++{ ++ int uframe; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ uframe = hfnum.b.frnum & 0x7; ++ if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++ ++/** ++ * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline ++ * ++ * Search pending transactions in the start-split pending state and queue them. ++ * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4). ++ * Note: we specifically don't do isochronous OUT transactions first because better ++ * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT. ++ */ ++static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels) ++{ ++ int n; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ if ((hfnum.b.frnum & 0x7) == 5) ++ return; ++ for (n = 0; n < num_channels; n++) { ++ if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) { ++ /* Check to see if any other transactions are using this TT */ ++ if(!fiq_fsm_tt_in_use(st, num_channels, n)) { ++ if (!fiq_fsm_too_late(st, n)) { ++ st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; ++ fiq_print(FIQDBG_INT, st, "NEXTPER "); ++ fiq_fsm_restart_channel(st, n, 0); ++ } else { ++ st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ } ++ break; ++ } ++ } ++ } ++ for (n = 0; n < num_channels; n++) { ++ if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) { ++ if (!fiq_fsm_tt_in_use(st, num_channels, n)) { ++ fiq_print(FIQDBG_INT, st, "NEXTISO "); ++ st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ fiq_fsm_restart_channel(st, n, 0); ++ break; ++ } ++ } ++ } ++} ++ ++/** ++ * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data ++ * @state: Pointer to fiq_state ++ * @n: Channel transaction is active on ++ * @hcint: Copy of host channel interrupt register ++ * ++ * Returns 0 if there are no more transactions for this HC to do, 1 ++ * otherwise. ++ */ ++static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint) ++{ ++ struct fiq_channel_state *st = &state->channel[n]; ++ int xfer_len = 0, nrpackets = 0; ++ hcdma_data_t hcdma; ++ fiq_print(FIQDBG_INT, state, "HSISO %02d", n); ++ ++ xfer_len = fiq_get_xfer_len(state, n); ++ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len; ++ ++ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32; ++ ++ st->hs_isoc_info.index++; ++ if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) { ++ return 0; ++ } ++ ++ /* grab the next DMA address offset from the array */ ++ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset; ++ FIQ_WRITE(state->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ ++ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as ++ * the core needs to be told to send the correct number. Caution: for IN transfers, ++ * this is always set to the maximum size of the endpoint. */ ++ xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length; ++ /* Integer divide in a FIQ: fun. FIXME: make this not suck */ ++ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; ++ if (nrpackets == 0) ++ nrpackets = 1; ++ st->hcchar_copy.b.multicnt = nrpackets; ++ st->hctsiz_copy.b.pktcnt = nrpackets; ++ ++ /* Initial PID also needs to be set */ ++ if (st->hcchar_copy.b.epdir == 0) { ++ st->hctsiz_copy.b.xfersize = xfer_len; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_MDATA; ++ break; ++ } ++ ++ } else { ++ switch (st->hcchar_copy.b.multicnt) { ++ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA1; ++ break; ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA2; ++ break; ++ } ++ } ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32); ++ /* Channel is enabled on hcint handler exit */ ++ fiq_print(FIQDBG_INT, state, "HSISOOUT"); ++ return 1; ++} ++ ++ ++/** ++ * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler ++ * @state: Pointer to the state struct passed from banked FIQ mode registers. ++ * @num_channels: set according to the DWC hardware configuration ++ * ++ * The SOF handler in FSM mode has two functions ++ * 1. Hold off SOF from causing schedule advancement in IRQ context if there's ++ * nothing to do ++ * 2. Advance certain FSM states that require either a microframe delay, or a microframe ++ * of holdoff. ++ * ++ * The second part is architecture-specific to mach-bcm2835 - ++ * a sane interrupt controller would have a mask register for ARM interrupt sources ++ * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt ++ * number (USB) can be enabled. This means that certain parts of the USB specification ++ * that require "wait a little while, then issue another packet" cannot be fulfilled with ++ * the timing granularity required to achieve optimal throughout. The workaround is to use ++ * the SOF "timer" (125uS) to perform this task. ++ */ ++static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels) ++{ ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) }; ++ int n; ++ int kick_irq = 0; ++ ++ if ((hfnum.b.frnum & 0x7) == 1) { ++ /* We cannot issue csplits for transactions in the last frame past (n+1).1 ++ * Check to see if there are any transactions that are stale. ++ * Boot them out. ++ */ ++ for (n = 0; n < num_channels; n++) { ++ switch (state->channel[n].fsm) { ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_CSPLIT_LAST: ++ /* Check if we are no longer in the same full-speed frame. */ ++ if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) < ++ (hfnum.b.frnum & ~0x7)) ++ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++ for (n = 0; n < num_channels; n++) { ++ switch (state->channel[n].fsm) { ++ ++ case FIQ_NP_SSPLIT_RETRY: ++ case FIQ_NP_IN_CSPLIT_RETRY: ++ case FIQ_NP_OUT_CSPLIT_RETRY: ++ fiq_fsm_restart_channel(state, n, 0); ++ break; ++ ++ case FIQ_HS_ISOC_SLEEPING: ++ state->channel[n].fsm = FIQ_HS_ISOC_TURBO; ++ fiq_fsm_restart_channel(state, n, 0); ++ break; ++ ++ case FIQ_PER_SSPLIT_QUEUED: ++ if ((hfnum.b.frnum & 0x7) == 5) ++ break; ++ if(!fiq_fsm_tt_in_use(state, num_channels, n)) { ++ if (!fiq_fsm_too_late(state, n)) { ++ fiq_print(FIQDBG_INT, st, "SOF GO %01d", n); ++ fiq_fsm_restart_channel(state, n, 0); ++ state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ /* Transaction cannot be started without risking a device babble error */ ++ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ state->haintmsk_saved.b2.chint &= ~(1 << n); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); ++ kick_irq |= 1; ++ } ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_PENDING: ++ /* Ordinarily, this should be poked after the SSPLIT ++ * complete interrupt for a competing transfer on the same ++ * TT. Doesn't happen for aborted transactions though. ++ */ ++ if ((hfnum.b.frnum & 0x7) >= 5) ++ break; ++ if (!fiq_fsm_tt_in_use(state, num_channels, n)) { ++ /* Hardware bug. SOF can sometimes occur after the channel halt interrupt ++ * that caused this. ++ */ ++ fiq_fsm_restart_channel(state, n, 0); ++ fiq_print(FIQDBG_INT, state, "SOF ISOC"); ++ if (state->channel[n].nrpackets == 1) { ++ state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_WAIT: ++ /* we are guaranteed to be in this state if and only if the SSPLIT interrupt ++ * occurred when the bus transaction occurred. The SOF interrupt reversal bug ++ * will utterly bugger this up though. ++ */ ++ if (hfnum.b.frnum != state->channel[n].expected_uframe) { ++ fiq_print(FIQDBG_INT, state, "SOFCS %d ", n); ++ state->channel[n].fsm = FIQ_PER_CSPLIT_POLL; ++ fiq_fsm_restart_channel(state, n, 0); ++ fiq_fsm_start_next_periodic(state, num_channels); ++ ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ case FIQ_DEQUEUE_ISSUED: ++ /* Ugly: we have to force a HCD interrupt. ++ * Poke the mask for the channel in question. ++ * We will take a fake SOF because of this, but ++ * that's OK. ++ */ ++ state->haintmsk_saved.b2.chint &= ~(1 << n); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); ++ kick_irq |= 1; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ if (state->kick_np_queues || ++ dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum)) ++ kick_irq |= 1; ++ ++ return !kick_irq; ++} ++ ++ ++/** ++ * fiq_fsm_do_hcintr() - FSM host channel interrupt handler ++ * @state: Pointer to the FIQ state struct ++ * @num_channels: Number of channels as per hardware config ++ * @n: channel for which HAINT(i) was raised ++ * ++ * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well. ++ */ ++static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcint_data_t hcint_probe; ++ hcchar_data_t hcchar; ++ int handled = 0; ++ int restart = 0; ++ int last_csplit = 0; ++ int start_next_periodic = 0; ++ struct fiq_channel_state *st = &state->channel[n]; ++ hfnum_data_t hfnum; ++ ++ hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT); ++ hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK); ++ hcint_probe.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) { ++ fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm); ++ fiq_print(FIQDBG_INT, state, "%08x", hcint.d32); ++ } ++ ++ switch (st->fsm) { ++ ++ case FIQ_PASSTHROUGH: ++ case FIQ_DEQUEUE_ISSUED: ++ /* doesn't belong to us, kick it upstairs */ ++ break; ++ ++ case FIQ_PASSTHROUGH_ERRORSTATE: ++ /* We are here to emulate the error recovery mechanism of the dwc HCD. ++ * Several interrupts are unmasked if a previous transaction failed - it's ++ * death for the FIQ to attempt to handle them as the channel isn't halted. ++ * Emulate what the HCD does in this situation: mask and continue. ++ * The FSM has no other state setup so this has to be handled out-of-band. ++ */ ++ fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); ++ if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { ++ fiq_print(FIQDBG_ERR, state, "RESET %02d", n); ++ /* In some random cases we can get a NAK interrupt coincident with a Xacterr ++ * interrupt, after the device has disappeared. ++ */ ++ if (!hcint.b.xacterr) ++ st->nr_errors = 0; ++ hcintmsk.b.nak = 0; ++ hcintmsk.b.ack = 0; ++ hcintmsk.b.datatglerr = 0; ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32); ++ return 1; ++ } ++ if (hcint_probe.b.chhltd) { ++ fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n); ++ fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32); ++ return 0; ++ } ++ break; ++ ++ /* Non-periodic state groups */ ++ case FIQ_NP_SSPLIT_STARTED: ++ case FIQ_NP_SSPLIT_RETRY: ++ /* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */ ++ if (hcint.b.ack) { ++ /* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction ++ * will start shortly. SOF needs to kick the transaction to prevent a NYET flood. ++ */ ++ if(st->hcchar_copy.b.epdir == 1) ++ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; ++ else ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ st->nr_errors = 0; ++ handled = 1; ++ fiq_fsm_setup_csplit(state, n); ++ } else if (hcint.b.nak) { ++ // No buffer space in TT. Retry on a uframe boundary. ++ st->fsm = FIQ_NP_SSPLIT_RETRY; ++ handled = 1; ++ } else if (hcint.b.xacterr) { ++ // The only other one we care about is xacterr. This implies HS bus error - retry. ++ st->nr_errors++; ++ st->fsm = FIQ_NP_SSPLIT_RETRY; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else { ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ handled = 0; ++ restart = 0; ++ } ++ break; ++ ++ case FIQ_NP_IN_CSPLIT_RETRY: ++ /* Received a CSPLIT done interrupt. ++ * Expected Data/NAK/STALL/NYET for IN. ++ */ ++ if (hcint.b.xfercomp) { ++ /* For IN, data is present. */ ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nak) { ++ /* no endpoint data. Punt it upstairs */ ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nyet) { ++ /* CSPLIT NYET - retry on a uframe boundary. */ ++ handled = 1; ++ st->nr_errors = 0; ++ } else if (hcint.b.datatglerr) { ++ /* data toggle errors do not set the xfercomp bit. */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else if (hcint.b.xacterr) { ++ /* HS error. Retry immediate */ ++ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else if (hcint.b.stall || hcint.b.bblerr) { ++ /* A STALL implies either a LS bus error or a genuine STALL. */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else { ++ /* Hardware bug. It's possible in some cases to ++ * get a channel halt with nothing else set when ++ * the response was a NYET. Treat as local 3-strikes retry. ++ */ ++ hcint_data_t hcint_test = hcint; ++ hcint_test.b.chhltd = 0; ++ if (!hcint_test.d32) { ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ } ++ } else { ++ /* Bail out if something unexpected happened */ ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } ++ } ++ break; ++ ++ case FIQ_NP_OUT_CSPLIT_RETRY: ++ /* Received a CSPLIT done interrupt. ++ * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/ ++ if (hcint.b.xfercomp) { ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nak) { ++ // The HCD will implement the holdoff on frame boundaries. ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nyet) { ++ // Hub still processing. ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ handled = 1; ++ st->nr_errors = 0; ++ //restart = 1; ++ } else if (hcint.b.xacterr) { ++ /* HS error. retry immediate */ ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else if (hcint.b.stall) { ++ /* LS bus error or genuine stall */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else { ++ /* ++ * Hardware bug. It's possible in some cases to get a ++ * channel halt with nothing else set when the response was a NYET. ++ * Treat as local 3-strikes retry. ++ */ ++ hcint_data_t hcint_test = hcint; ++ hcint_test.b.chhltd = 0; ++ if (!hcint_test.d32) { ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ } ++ } else { ++ // Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it. ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } ++ } ++ break; ++ ++ /* Periodic split states (except isoc out) */ ++ case FIQ_PER_SSPLIT_STARTED: ++ /* Expect an ACK or failure for SSPLIT */ ++ if (hcint.b.ack) { ++ /* ++ * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs. ++ * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1 ++ * point for microframe n-3, the packet will not appear on the bus until microframe n. ++ * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus ++ * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1 ++ * coincident with SOF for n+1. ++ * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place. ++ * These appear to be caused by timing/clock crossing bugs within the core itself. ++ * State machine workaround. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ fiq_fsm_setup_csplit(state, n); ++ /* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct ++ * time. If not, then we're in the next SOF. ++ */ ++ if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) { ++ fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n); ++ st->expected_uframe = hfnum.b.frnum; ++ st->fsm = FIQ_PER_CSPLIT_WAIT; ++ } else { ++ fiq_print(FIQDBG_INT, state, "CSPOL %01d", n); ++ /* For isochronous IN endpoints, ++ * we need to hold off if we are expecting a lot of data */ ++ if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) { ++ start_next_periodic = 1; ++ } ++ /* Danger will robinson: we are in a broken state. If our first interrupt after ++ * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable ++ * lag. Unmask the NYET interrupt. ++ */ ++ st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; ++ st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1; ++ restart = 1; ++ } ++ handled = 1; ++ } else if (hcint.b.xacterr) { ++ /* 3-strikes retry is enabled, we have hit our max nr_errors */ ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ start_next_periodic = 1; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ start_next_periodic = 1; ++ } ++ /* We can now queue the next isochronous OUT transaction, if one is pending. */ ++ if(fiq_fsm_tt_next_isoc(state, num_channels, n)) { ++ fiq_print(FIQDBG_INT, state, "NEXTISO "); ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_NYET1: ++ /* First CSPLIT attempt was a NYET. If we get a subsequent NYET, ++ * we are too late and the TT has dropped its CSPLIT fifo. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ start_next_periodic = 1; ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ st->fsm = FIQ_PER_CSPLIT_POLL; ++ st->nr_errors = 0; ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ handled = 1; ++ restart = 1; ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ /* Doh. Data lost. */ ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_BROKEN_NYET1: ++ /* ++ * we got here because our host channel is in the delayed-interrupt ++ * state and we cannot take a NYET interrupt any later than when it ++ * occurred. Disable then re-enable the channel if this happens to force ++ * CSPLITs to occur at the right time. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ fiq_print(FIQDBG_INT, state, "BROK: %01d ", n); ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ start_next_periodic = 1; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ st->fsm = FIQ_PER_CSPLIT_POLL; ++ handled = 1; ++ restart = 1; ++ start_next_periodic = 1; ++ /* Reload HCTSIZ for the next transfer */ ++ fiq_fsm_reload_hctsiz(state, n); ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ start_next_periodic = 1; ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ /* Local 3-strikes retry is handled by the core. This is a ERR response.*/ ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_POLL: ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ start_next_periodic = 1; ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ handled = 1; ++ restart = 1; ++ /* Reload HCTSIZ for the next transfer */ ++ fiq_fsm_reload_hctsiz(state, n); ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ /* Are we a NYET after the first data packet? */ ++ if (st->nrpackets == 0) { ++ st->fsm = FIQ_PER_CSPLIT_NYET1; ++ handled = 1; ++ restart = 1; ++ } else { ++ /* We got a NYET when polling CSPLITs. Can happen ++ * if our heuristic fails, or if someone disables us ++ * for any significant length of time. ++ */ ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ /* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/ ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_HS_ISOC_TURBO: ++ if (fiq_fsm_update_hs_isoc(state, n, hcint)) { ++ /* more transactions to come */ ++ handled = 1; ++ restart = 1; ++ fiq_print(FIQDBG_INT, state, "HSISO M "); ++ } else { ++ st->fsm = FIQ_HS_ISOC_DONE; ++ fiq_print(FIQDBG_INT, state, "HSISO F "); ++ } ++ break; ++ ++ case FIQ_HS_ISOC_ABORTED: ++ /* This abort is called by the driver rewriting the state mid-transaction ++ * which allows the dequeue mechanism to work more effectively. ++ */ ++ break; ++ ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ if (hcint.b.ack) { ++ if(fiq_iso_out_advance(state, num_channels, n)) { ++ /* last OUT transfer */ ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ /* ++ * Assuming the periodic FIFO in the dwc core ++ * actually does its job properly, we can queue ++ * the next ssplit now and in theory, the wire ++ * transactions will be in-order. ++ */ ++ // No it doesn't. It appears to process requests in host channel order. ++ //start_next_periodic = 1; ++ } ++ handled = 1; ++ restart = 1; ++ } else { ++ /* ++ * Isochronous transactions carry on regardless. Log the error ++ * and continue. ++ */ ++ //explode += 1; ++ st->nr_errors++; ++ if(fiq_iso_out_advance(state, num_channels, n)) { ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ //start_next_periodic = 1; ++ } ++ handled = 1; ++ restart = 1; ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_LAST: ++ if (hcint.b.ack) { ++ /* All done here */ ++ st->fsm = FIQ_PER_ISO_OUT_DONE; ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_DONE; ++ st->nr_errors++; ++ } ++ start_next_periodic = 1; ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ /* SOF kicked us because we overran. */ ++ start_next_periodic = 1; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (handled) { ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32); ++ } else { ++ /* Copy the regs into the state so the IRQ knows what to do */ ++ st->hcint_copy.d32 = hcint.d32; ++ } ++ ++ if (restart) { ++ /* Restart always implies handled. */ ++ if (restart == 2) { ++ /* For complete-split INs, the show must go on. ++ * Force a channel restart */ ++ fiq_fsm_restart_channel(state, n, 1); ++ } else { ++ fiq_fsm_restart_channel(state, n, 0); ++ } ++ } ++ if (start_next_periodic) { ++ fiq_fsm_start_next_periodic(state, num_channels); ++ } ++ if (st->fsm != FIQ_PASSTHROUGH) ++ fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm); ++ ++ return handled; ++} ++ ++ ++/** ++ * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ ++ * @state: pointer to state struct passed from the banked FIQ mode registers. ++ * @num_channels: set according to the DWC hardware configuration ++ * @dma: pointer to DMA bounce buffers for split transaction slots ++ * ++ * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode ++ * inside an EHCI or similar host controller regarding split transactions. The DWC core ++ * interrupts each and every time a split transaction packet is received or sent successfully. ++ * This results in either an interrupt storm when everything is working "properly", or ++ * the interrupt latency of the system in general breaks time-sensitive periodic split ++ * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ ++ * solves these problems. ++ * ++ * Return: void ++ */ ++void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) ++{ ++ gintsts_data_t gintsts, gintsts_handled; ++ gintmsk_data_t gintmsk; ++ //hfnum_data_t hfnum; ++ haint_data_t haint, haint_handled; ++ haintmsk_data_t haintmsk; ++ int kick_irq = 0; ++ ++ gintsts_handled.d32 = 0; ++ haint_handled.d32 = 0; ++ ++ fiq_fsm_spin_lock(&state->lock); ++ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); ++ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); ++ gintsts.d32 &= gintmsk.d32; ++ ++ if (gintsts.b.sofintr) { ++ /* For FSM mode, SOF is required to keep the state machine advance for ++ * certain stages of the periodic pipeline. It's death to mask this ++ * interrupt in that case. ++ */ ++ ++ if (!fiq_fsm_do_sof(state, num_channels)) { ++ /* Kick IRQ once. Queue advancement means that all pending transactions ++ * will get serviced when the IRQ finally executes. ++ */ ++ if (state->gintmsk_saved.b.sofintr == 1) ++ kick_irq |= 1; ++ state->gintmsk_saved.b.sofintr = 0; ++ } ++ gintsts_handled.b.sofintr = 1; ++ } ++ ++ if (gintsts.b.hcintr) { ++ int i; ++ haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT); ++ haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK); ++ haint.d32 &= haintmsk.d32; ++ haint_handled.d32 = 0; ++ for (i=0; ihaintmsk_saved.b2.chint &= ~(1 << i); ++ } else { ++ /* do_hcintr cleaned up after itself, but clear haint */ ++ haint_handled.b2.chint |= (1 << i); ++ } ++ } ++ } ++ ++ if (haint_handled.b2.chint) { ++ FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32); ++ } ++ ++ if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) { ++ /* ++ * This is necessary to avoid multiple retriggers of the MPHI in the case ++ * where interrupts are held off and HCINTs start to pile up. ++ * Only wake up the IRQ if a new interrupt came in, was not handled and was ++ * masked. ++ */ ++ haintmsk.d32 &= state->haintmsk_saved.d32; ++ FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32); ++ kick_irq |= 1; ++ } ++ /* Top-Level interrupt - always handled because it's level-sensitive */ ++ gintsts_handled.b.hcintr = 1; ++ } ++ ++ ++ /* Clear the bits in the saved register that were not handled but were triggered. */ ++ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); ++ ++ /* FIQ didn't handle something - mask has changed - write new mask */ ++ if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) { ++ gintmsk.d32 &= state->gintmsk_saved.d32; ++ gintmsk.b.sofintr = 1; ++ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); ++// fiq_print(FIQDBG_INT, state, "KICKGINT"); ++// fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32); ++// fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32); ++ kick_irq |= 1; ++ } ++ ++ if (gintsts_handled.d32) { ++ /* Only applies to edge-sensitive bits in GINTSTS */ ++ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); ++ } ++ ++ /* We got an interrupt, didn't handle it. */ ++ if (kick_irq) { ++ state->mphi_int_count++; ++ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); ++ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); ++ ++ } ++ state->fiq_done++; ++ mb(); ++ fiq_fsm_spin_unlock(&state->lock); ++} ++ ++ ++/** ++ * dwc_otg_fiq_nop() - FIQ "lite" ++ * @state: pointer to state struct passed from the banked FIQ mode registers. ++ * ++ * The "nop" handler does not intervene on any interrupts other than SOF. ++ * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals ++ * with non-periodic/periodic queues) needs to be kicked. ++ * ++ * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second. ++ * ++ * Return: void ++ */ ++void notrace dwc_otg_fiq_nop(struct fiq_state *state) ++{ ++ gintsts_data_t gintsts, gintsts_handled; ++ gintmsk_data_t gintmsk; ++ hfnum_data_t hfnum; ++ ++ fiq_fsm_spin_lock(&state->lock); ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); ++ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); ++ gintsts.d32 &= gintmsk.d32; ++ gintsts_handled.d32 = 0; ++ ++ if (gintsts.b.sofintr) { ++ if (!state->kick_np_queues && ++ dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) { ++ /* SOF handled, no work to do, just ACK interrupt */ ++ gintsts_handled.b.sofintr = 1; ++ } else { ++ /* Kick IRQ */ ++ state->gintmsk_saved.b.sofintr = 0; ++ } ++ } ++ ++ /* Reset handled interrupts */ ++ if(gintsts_handled.d32) { ++ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); ++ } ++ ++ /* Clear the bits in the saved register that were not handled but were triggered. */ ++ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); ++ ++ /* We got an interrupt, didn't handle it and want to mask it */ ++ if (~(state->gintmsk_saved.d32)) { ++ state->mphi_int_count++; ++ gintmsk.d32 &= state->gintmsk_saved.d32; ++ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); ++ /* Force a clear before another dummy send */ ++ FIQ_WRITE(state->mphi_regs.intstat, (1<<29)); ++ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); ++ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); ++ ++ } ++ state->fiq_done++; ++ mb(); ++ fiq_fsm_spin_unlock(&state->lock); ++} +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h +new file mode 100644 +index 0000000..8455324 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h +@@ -0,0 +1,367 @@ ++/* ++ * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This FIQ implements functionality that performs split transactions on ++ * the dwc_otg hardware without any outside intervention. A split transaction ++ * is "queued" by nominating a specific host channel to perform the entirety ++ * of a split transaction. This FIQ will then perform the microframe-precise ++ * scheduling required in each phase of the transaction until completion. ++ * ++ * The FIQ functionality has been surgically implanted into the Synopsys ++ * vendor-provided driver. ++ * ++ */ ++ ++#ifndef DWC_OTG_FIQ_FSM_H_ ++#define DWC_OTG_FIQ_FSM_H_ ++ ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_hcd.h" ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define FLAME_ON(x) \ ++do { \ ++ int gpioreg; \ ++ \ ++ gpioreg = readl(__io_address(0x20200000+0x8)); \ ++ gpioreg &= ~(7 << (x-20)*3); \ ++ gpioreg |= 0x1 << (x-20)*3; \ ++ writel(gpioreg, __io_address(0x20200000+0x8)); \ ++ \ ++ writel(1< 1, SOF wakes up the isochronous FSM */ ++ FIQ_HS_ISOC_SLEEPING = 24, ++ FIQ_HS_ISOC_DONE = 25, ++ FIQ_HS_ISOC_ABORTED = 26, ++ FIQ_DEQUEUE_ISSUED = 30, ++ FIQ_TEST = 32, ++}; ++ ++struct fiq_stack { ++ int magic1; ++ uint8_t stack[2048]; ++ int magic2; ++}; ++ ++ ++/** ++ * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel) ++ * @index: Number of slots reported used for IN transactions / number of slots ++ * transmitted for an OUT transaction ++ * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused) ++ * ++ * Split transaction transfers can have variable length depending on other bus ++ * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore ++ * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers ++ * can happen per-frame. ++ */ ++struct fiq_dma_info { ++ u8 index; ++ u8 slot_len[6]; ++}; ++ ++struct __attribute__((packed)) fiq_split_dma_slot { ++ u8 buf[188]; ++}; ++ ++struct fiq_dma_channel { ++ struct __attribute__((packed)) fiq_split_dma_slot index[6]; ++}; ++ ++struct fiq_dma_blob { ++ struct __attribute__((packed)) fiq_dma_channel channel[0]; ++}; ++ ++/** ++ * struct fiq_hs_isoc_info - USB2.0 isochronous data ++ * @iso_frame: Pointer to the array of OTG URB iso_frame_descs. ++ * @nrframes: Total length of iso_frame_desc array ++ * @index: Current index (FIQ-maintained) ++ * ++ */ ++struct fiq_hs_isoc_info { ++ struct dwc_otg_hcd_iso_packet_desc *iso_desc; ++ unsigned int nrframes; ++ unsigned int index; ++}; ++ ++/** ++ * struct fiq_channel_state - FIQ state machine storage ++ * @fsm: Current state of the channel as understood by the FIQ ++ * @nr_errors: Number of transaction errors on this split-transaction ++ * @hub_addr: SSPLIT/CSPLIT destination hub ++ * @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub ++ * @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For ++ * split-IN, number of CSPLIT data packets that were received. ++ * @hcchar_copy: ++ * @hcsplt_copy: ++ * @hcintmsk_copy: ++ * @hctsiz_copy: Copies of the host channel registers. ++ * For use as scratch, or for returning state. ++ * ++ * The fiq_channel_state is state storage between interrupts for a host channel. The ++ * FSM state is stored here. Members of this structure must only be set up by the ++ * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ ++ * has updated the state to either a COMPLETE state group or ABORT state group. ++ */ ++ ++struct fiq_channel_state { ++ enum fiq_fsm_state fsm; ++ unsigned int nr_errors; ++ unsigned int hub_addr; ++ unsigned int port_addr; ++ /* Hardware bug workaround: sometimes channel halt interrupts are ++ * delayed until the next SOF. Keep track of when we expected to get interrupted. */ ++ unsigned int expected_uframe; ++ /* in/out for communicating number of dma buffers used, or number of ISOC to do */ ++ unsigned int nrpackets; ++ struct fiq_dma_info dma_info; ++ struct fiq_hs_isoc_info hs_isoc_info; ++ /* Copies of HC registers - in/out communication from/to IRQ handler ++ * and for ease of channel setup. A bit of mungeing is performed - for ++ * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint. ++ */ ++ hcchar_data_t hcchar_copy; ++ hcsplt_data_t hcsplt_copy; ++ hcint_data_t hcint_copy; ++ hcintmsk_data_t hcintmsk_copy; ++ hctsiz_data_t hctsiz_copy; ++ hcdma_data_t hcdma_copy; ++}; ++ ++/** ++ * struct fiq_state - top-level FIQ state machine storage ++ * @mphi_regs: virtual address of the MPHI peripheral register file ++ * @dwc_regs_base: virtual address of the base of the DWC core register file ++ * @dma_base: physical address for the base of the DMA bounce buffers ++ * @dummy_send: Scratch area for sending a fake message to the MPHI peripheral ++ * @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled. ++ * Used for determining which interrupts fired to set off the IRQ handler. ++ * @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally. ++ * @np_count: Non-periodic transactions in the active queue ++ * @np_sent: Count of non-periodic transactions that have completed ++ * @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism, ++ * this is the next frame on which a SOF interrupt is required. Used to hold off ++ * passing SOF through to the driver until necessary. ++ * @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host ++ * channels configured into the core logic. ++ * ++ * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub. ++ * It contains top-level state information. ++ */ ++struct fiq_state { ++ fiq_lock_t lock; ++ mphi_regs_t mphi_regs; ++ void *dwc_regs_base; ++ dma_addr_t dma_base; ++ struct fiq_dma_blob *fiq_dmab; ++ void *dummy_send; ++ gintmsk_data_t gintmsk_saved; ++ haintmsk_data_t haintmsk_saved; ++ int mphi_int_count; ++ unsigned int fiq_done; ++ unsigned int kick_np_queues; ++ unsigned int next_sched_frame; ++#ifdef FIQ_DEBUG ++ char * buffer; ++ unsigned int bufsiz; ++#endif ++ struct fiq_channel_state channel[0]; ++}; ++ ++extern void fiq_fsm_spin_lock(fiq_lock_t *lock); ++ ++extern void fiq_fsm_spin_unlock(fiq_lock_t *lock); ++ ++extern int fiq_fsm_too_late(struct fiq_state *st, int n); ++ ++extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); ++ ++extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels); ++ ++extern void dwc_otg_fiq_nop(struct fiq_state *state); ++ ++#endif /* DWC_OTG_FIQ_FSM_H_ */ +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S +new file mode 100644 +index 0000000..ffa8d21 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S +@@ -0,0 +1,80 @@ ++/* ++ * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++ ++#include ++#include ++ ++ ++.text ++ ++.global _dwc_otg_fiq_stub_end; ++ ++/** ++ * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow ++ * a C-style function call with arguments from the FIQ banked registers. ++ * r0 = &hcd->fiq_state ++ * r1 = &hcd->num_channels ++ * r2 = &hcd->dma_buffers ++ * Tramples: r0, r1, r2, r4, fp, ip ++ */ ++ ++ENTRY(_dwc_otg_fiq_stub) ++ /* Stash unbanked regs - SP will have been set up for us */ ++ mov ip, sp; ++ stmdb sp!, {r0-r12, lr}; ++#ifdef FIQ_DEBUG ++ // Cycle profiling - read cycle counter at start ++ mrc p15, 0, r5, c15, c12, 1; ++#endif ++ /* r11 = fp, don't trample it */ ++ mov r4, fp; ++ /* set EABI frame size */ ++ sub fp, ip, #512; ++ ++ /* for fiq NOP mode - just need state */ ++ mov r0, r8; ++ /* r9 = num_channels */ ++ mov r1, r9; ++ /* r10 = struct *dma_bufs */ ++// mov r2, r10; ++ ++ /* r4 = &fiq_c_function */ ++ blx r4; ++#ifdef FIQ_DEBUG ++ mrc p15, 0, r4, c15, c12, 1; ++ subs r5, r5, r4; ++ // r5 is now the cycle count time for executing the FIQ. Store it somewhere? ++#endif ++ ldmia sp!, {r0-r12, lr}; ++ subs pc, lr, #4; ++_dwc_otg_fiq_stub_end: ++END(_dwc_otg_fiq_stub) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c new file mode 100644 -index 0000000..1e89549 +index 0000000..6c53d2c --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -0,0 +1,3479 @@ +@@ -0,0 +1,4252 @@ + +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $ @@ -39899,10 +49179,15 @@ index 0000000..1e89549 + * header file. + */ + ++#include ++#include ++ +#include "dwc_otg_hcd.h" +#include "dwc_otg_regs.h" ++#include "dwc_otg_fiq_fsm.h" + +extern bool microframe_schedule; ++extern uint16_t fiq_fsm_mask, nak_holdoff; + +//#define DEBUG_HOST_CHANNELS +#ifdef DEBUG_HOST_CHANNELS @@ -39912,6 +49197,7 @@ index 0000000..1e89549 +static int last_sel_trans_num_avail_hc_at_end = 0; +#endif /* DEBUG_HOST_CHANNELS */ + ++ +dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) +{ + return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); @@ -40021,31 +49307,43 @@ index 0000000..1e89549 + +/** + * Processes all the URBs in a single list of QHs. Completes them with -+ * -ETIMEDOUT and frees the QTD. ++ * -ESHUTDOWN and frees the QTD. + */ +static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) +{ -+ dwc_list_link_t *qh_item; ++ dwc_list_link_t *qh_item, *qh_tmp; + dwc_otg_qh_t *qh; + dwc_otg_qtd_t *qtd, *qtd_tmp; + -+ DWC_LIST_FOREACH(qh_item, qh_list) { ++ DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) { + qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, + &qh->qtd_list, qtd_list_entry) { + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); + if (qtd->urb != NULL) { + hcd->fops->complete(hcd, qtd->urb->priv, -+ qtd->urb, -DWC_E_TIMEOUT); ++ qtd->urb, -DWC_E_SHUTDOWN); + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); + } + + } ++ if(qh->channel) { ++ /* Using hcchar.chen == 1 is not a reliable test. ++ * It is possible that the channel has already halted ++ * but not yet been through the IRQ handler. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ if(microframe_schedule) ++ hcd->available_host_channels++; ++ qh->channel = NULL; ++ } ++ dwc_otg_hcd_qh_remove(hcd, qh); + } +} + +/** -+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic + * and periodic schedules. The QTD associated with each URB is removed from + * the schedule and freed. This function may be called when a disconnect is + * detected or when the HCD is being stopped. @@ -40131,7 +49429,8 @@ index 0000000..1e89549 + */ + dwc_otg_hcd->flags.b.port_connect_status_change = 1; + dwc_otg_hcd->flags.b.port_connect_status = 0; -+ ++ if(fiq_enable) ++ local_fiq_disable(); + /* + * Shutdown any transfers in process by clearing the Tx FIFO Empty + * interrupt mask and status bits and disabling subsequent host @@ -40227,8 +49526,17 @@ index 0000000..1e89549 + channel->qh = NULL; + } + } ++ if(fiq_fsm_enable) { ++ for(i=0; i < 128; i++) { ++ dwc_otg_hcd->hub_port[i] = 0; ++ } ++ } ++ + } + ++ if(fiq_enable) ++ local_fiq_enable(); ++ + if (dwc_otg_hcd->fops->disconnect) { + dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); + } @@ -40266,6 +49574,7 @@ index 0000000..1e89549 +} +#endif + ++ +/** + * HCD Callback function for Remote Wakeup. + * @@ -40316,10 +49625,12 @@ index 0000000..1e89549 + dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle, + int atomic_alloc) +{ -+ dwc_irqflags_t flags; + int retval = 0; ++ uint8_t needs_scheduling = 0; ++ dwc_otg_transaction_type_e tr_type; + dwc_otg_qtd_t *qtd; + gintmsk_data_t intr_mask = {.d32 = 0 }; ++ hprt0_data_t hprt0 = { .d32 = 0 }; + +#ifdef DEBUG /* integrity checks (Broadcom) */ + if (NULL == hcd->core_if) { @@ -40334,6 +49645,16 @@ index 0000000..1e89549 + return -DWC_E_NO_DEVICE; + } + ++ /* Some core configurations cannot support LS traffic on a FS root port */ ++ if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) && ++ (hcd->core_if->hwcfg2.b.fs_phy_type == 1) && ++ (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) { ++ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) { ++ return -DWC_E_NO_DEVICE; ++ } ++ } ++ + qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc); + if (qtd == NULL) { + DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); @@ -40349,32 +49670,27 @@ index 0000000..1e89549 + return -DWC_E_NO_MEMORY; + } +#endif -+ retval = -+ dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); ++ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); ++ if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1; ++ if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) ++ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ ++ needs_scheduling = 0; ++ ++ retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); + // creates a new queue in ep_handle if it doesn't exist already + if (retval < 0) { + DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " + "Error status %d\n", retval); + dwc_otg_hcd_qtd_free(qtd); -+ } else { -+ qtd->qh = *ep_handle; ++ return retval; + } -+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -+ if (!intr_mask.b.sofintr && retval == 0) { -+ dwc_otg_transaction_type_e tr_type; -+ if ((qtd->qh->ep_type == UE_BULK) -+ && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { -+ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -+ return 0; -+ } -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if(needs_scheduling) { + tr_type = dwc_otg_hcd_select_transactions(hcd); + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); + } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); + } -+ + return retval; +} + @@ -40383,6 +49699,8 @@ index 0000000..1e89549 +{ + dwc_otg_qh_t *qh; + dwc_otg_qtd_t *urb_qtd; ++ BUG_ON(!hcd); ++ BUG_ON(!dwc_otg_urb); + +#ifdef DEBUG /* integrity checks (Broadcom) */ + @@ -40399,14 +49717,17 @@ index 0000000..1e89549 + return -DWC_E_INVALID; + } + urb_qtd = dwc_otg_urb->qtd; ++ BUG_ON(!urb_qtd); + if (urb_qtd->qh == NULL) { + DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); + return -DWC_E_INVALID; + } +#else + urb_qtd = dwc_otg_urb->qtd; ++ BUG_ON(!urb_qtd); +#endif + qh = urb_qtd->qh; ++ BUG_ON(!qh); + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { + if (urb_qtd->in_process) { + dump_channel_info(hcd, qh); @@ -40421,6 +49742,7 @@ index 0000000..1e89549 + if (urb_qtd->in_process && qh->channel) { + /* The QTD is in process (it has been assigned to a channel). */ + if (hcd->flags.b.port_connect_status) { ++ int n = qh->channel->hc_num; + /* + * If still connected (i.e. in host mode), halt the + * channel so it can be used for other transfers. If @@ -40428,8 +49750,16 @@ index 0000000..1e89549 + * written to halt the channel since the core is in + * device mode. + */ -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ /* In FIQ FSM mode, we need to shut down carefully. ++ * The FIQ may attempt to restart a disabled channel */ ++ if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) { ++ qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE; ++ qh->channel->halt_pending = 1; ++ hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED; ++ } else { ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } + } + } + @@ -40546,6 +49876,32 @@ index 0000000..1e89549 + dwc_otg_hcd->flags.b.port_reset_change = 1; +} + ++static void completion_tasklet_func(void *ptr) ++{ ++ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr; ++ struct urb *urb; ++ urb_tq_entry_t *item; ++ dwc_irqflags_t flags; ++ ++ /* This could just be spin_lock_irq */ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) { ++ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list); ++ urb = item->urb; ++ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item, ++ urb_tq_entries); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ DWC_FREE(item); ++ ++ usb_hcd_giveback_urb(hcd->priv, urb, urb->status); ++ ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ return; ++} ++ +static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) +{ + dwc_list_link_t *item; @@ -40633,6 +49989,34 @@ index 0000000..1e89549 + cil_hcd_start(core_if); +} + ++void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num) ++{ ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ struct fiq_dma_blob *blob = hcd->fiq_dmab; ++ int i; ++ ++ st->fsm = FIQ_PASSTHROUGH; ++ st->hcchar_copy.d32 = 0; ++ st->hcsplt_copy.d32 = 0; ++ st->hcint_copy.d32 = 0; ++ st->hcintmsk_copy.d32 = 0; ++ st->hctsiz_copy.d32 = 0; ++ st->hcdma_copy.d32 = 0; ++ st->nr_errors = 0; ++ st->hub_addr = 0; ++ st->port_addr = 0; ++ st->expected_uframe = 0; ++ st->nrpackets = 0; ++ st->dma_info.index = 0; ++ for (i = 0; i < 6; i++) ++ st->dma_info.slot_len[i] = 255; ++ st->hs_isoc_info.index = 0; ++ st->hs_isoc_info.iso_desc = NULL; ++ st->hs_isoc_info.nrframes = 0; ++ ++ DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128); ++} ++ +/** + * Frees secondary storage associated with the dwc_otg_hcd structure contained + * in the struct usb_hcd field. @@ -40678,12 +50062,15 @@ index 0000000..1e89549 + } else if (dwc_otg_hcd->status_buf != NULL) { + DWC_FREE(dwc_otg_hcd->status_buf); + } ++ DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock); + DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); + /* Set core_if's lock pointer to NULL */ + dwc_otg_hcd->core_if->lock = NULL; + + DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); + DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); ++ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); ++ DWC_FREE(dwc_otg_hcd->fiq_state); + +#ifdef DWC_DEV_SRPCAP + if (dwc_otg_hcd->core_if->power_down == 2 && @@ -40733,7 +50120,7 @@ index 0000000..1e89549 + DWC_LIST_INIT(&hcd->periodic_sched_ready); + DWC_LIST_INIT(&hcd->periodic_sched_assigned); + DWC_LIST_INIT(&hcd->periodic_sched_queued); -+ ++ DWC_TAILQ_INIT(&hcd->completed_urb_list); + /* + * Create a host channel descriptor for each host channel implemented + * in the controller. Initialize the channel descriptor array. @@ -40761,6 +50148,60 @@ index 0000000..1e89549 + channel); + } + ++ if (fiq_enable) { ++ hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)); ++ if (!hcd->fiq_state) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels))); ++ ++ for (i = 0; i < num_channels; i++) { ++ hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH; ++ } ++ hcd->fiq_state->dummy_send = DWC_ALLOC_ATOMIC(16); ++ ++ hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack)); ++ if (!hcd->fiq_stack) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ hcd->fiq_stack->magic1 = 0xDEADBEEF; ++ hcd->fiq_stack->magic2 = 0xD00DFEED; ++ hcd->fiq_state->gintmsk_saved.d32 = ~0; ++ hcd->fiq_state->haintmsk_saved.b2.chint = ~0; ++ ++ /* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools ++ * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels) ++ * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some ++ * moderately readable array casts. ++ */ ++ hcd->fiq_dmab = DWC_DMA_ALLOC((sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base); ++ DWC_WARN("FIQ DMA bounce buffers: virt = 0x%08x dma = 0x%08x len=%d", ++ (unsigned int)hcd->fiq_dmab, (unsigned int)hcd->fiq_state->dma_base, ++ sizeof(struct fiq_dma_channel) * num_channels); ++ ++ DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024); ++ ++ /* pointer for debug in fiq_print */ ++ hcd->fiq_state->fiq_dmab = hcd->fiq_dmab; ++ if (fiq_fsm_enable) { ++ int i; ++ for (i=0; i < hcd->core_if->core_params->host_channels; i++) { ++ dwc_otg_cleanup_fiq_channel(hcd, i); ++ } ++ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s%s", ++ (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", ++ (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", ++ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "", ++ (fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : ""); ++ } ++ } ++ + /* Initialize the Connection timeout timer. */ + hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", + dwc_otg_hcd_connect_timeout, 0); @@ -40771,6 +50212,9 @@ index 0000000..1e89549 + + /* Initialize reset tasklet. */ + hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); ++ ++ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet", ++ completion_tasklet_func, hcd); +#ifdef DWC_DEV_SRPCAP + if (hcd->core_if->power_down == 2) { + /* Initialize Power on timer for Host power up in case hibernation */ @@ -40803,6 +50247,12 @@ index 0000000..1e89549 + hcd->frame_list = NULL; + hcd->frame_list_dma = 0; + hcd->periodic_qh_count = 0; ++ ++ DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port)); ++#ifdef FIQ_DEBUG ++ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc)); ++#endif ++ +out: + return retval; +} @@ -40873,6 +50323,9 @@ index 0000000..1e89549 + dwc_otg_qtd_t *qtd; + dwc_otg_hcd_urb_t *urb; + void* ptr = NULL; ++ uint32_t intr_enable; ++ unsigned long flags; ++ gintmsk_data_t gintmsk = { .d32 = 0, }; + + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); + @@ -40948,7 +50401,13 @@ index 0000000..1e89549 + uint32_t hub_addr, port_addr; + hc->do_split = 1; + hc->xact_pos = qtd->isoc_split_pos; -+ hc->complete_split = qtd->complete_split; ++ /* We don't need to do complete splits anymore */ ++// if(fiq_fsm_enable) ++ if (0) ++ hc->complete_split = qtd->complete_split = 0; ++ else ++ hc->complete_split = qtd->complete_split; ++ + hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); + hc->hub_addr = (uint8_t) hub_addr; + hc->port_addr = (uint8_t) port_addr; @@ -41092,9 +50551,537 @@ index 0000000..1e89549 + hc->desc_list_addr = qh->desc_list_dma; + + dwc_otg_hc_init(hcd->core_if, hc); ++ ++ local_irq_save(flags); ++ ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ } ++ ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc->hc_num); ++ DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ if (fiq_enable) { ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } ++ ++ local_irq_restore(flags); + hc->qh = qh; +} + ++ ++/** ++ * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ ++ * @qh: pointer to the endpoint's queue head ++ * ++ * Transaction start/end control flow is grafted onto the existing dwc_otg ++ * mechanisms, to avoid spaghettifying the functions more than they already are. ++ * This function's eligibility check is altered by debug parameter. ++ * ++ * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction. ++ */ ++ ++int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh) ++{ ++ if (qh->do_split) { ++ switch (qh->ep_type) { ++ case UE_CONTROL: ++ case UE_BULK: ++ if (fiq_fsm_mask & (1 << 0)) ++ return 1; ++ break; ++ case UE_INTERRUPT: ++ case UE_ISOCHRONOUS: ++ if (fiq_fsm_mask & (1 << 1)) ++ return 1; ++ break; ++ default: ++ break; ++ } ++ } else if (qh->ep_type == UE_ISOCHRONOUS) { ++ if (fiq_fsm_mask & (1 << 2)) { ++ /* HS ISOCH support. We test for compatibility: ++ * - DWORD aligned buffers ++ * - Must be at least 2 transfers (otherwise pointless to use the FIQ) ++ * If yes, then the fsm enqueue function will handle the state machine setup. ++ */ ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ struct dwc_otg_hcd_iso_packet_desc (*iso_descs)[0] = &urb->iso_descs; ++ int nr_iso_frames = urb->packet_count; ++ int i; ++ uint32_t ptr; ++ ++ if (nr_iso_frames < 2) ++ return 0; ++ for (i = 0; i < nr_iso_frames; i++) { ++ ptr = urb->dma + iso_descs[i]->offset; ++ if (ptr & 0x3) { ++ printk_ratelimited("%s: Non-Dword aligned isochronous frame offset." ++ " Cannot queue FIQ-accelerated transfer to device %d endpoint %d\n", ++ __FUNCTION__, qh->channel->dev_addr, qh->channel->ep_num); ++ return 0; ++ } ++ } ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers ++ * @hcd: Pointer to the dwc_otg_hcd struct ++ * @qh: Pointer to the endpoint's queue head ++ * ++ * Periodic split transactions are transmitted modulo 188 bytes. ++ * This necessitates slicing data up into buckets for isochronous out ++ * and fixing up the DMA address for all IN transfers. ++ * ++ * Returns 1 if the DMA bounce buffers have been used, 0 if the default ++ * HC buffer has been used. ++ */ ++int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh) ++ { ++ int frame_length, i = 0; ++ uint8_t *ptr = NULL; ++ dwc_hc_t *hc = qh->channel; ++ struct fiq_dma_blob *blob; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ for (i = 0; i < 6; i++) { ++ st->dma_info.slot_len[i] = 255; ++ } ++ st->dma_info.index = 0; ++ i = 0; ++ if (hc->ep_is_in) { ++ /* ++ * Set dma_regs to bounce buffer. FIQ will update the ++ * state depending on transaction progress. ++ */ ++ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; ++ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; ++ /* Calculate the max number of CSPLITS such that the FIQ can time out ++ * a transaction if it fails. ++ */ ++ frame_length = st->hcchar_copy.b.mps; ++ do { ++ i++; ++ frame_length -= 188; ++ } while (frame_length >= 0); ++ st->nrpackets = i; ++ return 1; ++ } else { ++ if (qh->ep_type == UE_ISOCHRONOUS) { ++ ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ frame_length = frame_desc->length; ++ ++ /* Virtual address for bounce buffers */ ++ blob = hcd->fiq_dmab; ++ ++ ptr = qtd->urb->buf + frame_desc->offset; ++ if (frame_length == 0) { ++ /* ++ * for isochronous transactions, we must still transmit a packet ++ * even if the length is zero. ++ */ ++ st->dma_info.slot_len[0] = 0; ++ st->nrpackets = 1; ++ } else { ++ do { ++ if (frame_length <= 188) { ++ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length); ++ st->dma_info.slot_len[i] = frame_length; ++ ptr += frame_length; ++ } else { ++ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188); ++ st->dma_info.slot_len[i] = 188; ++ ptr += 188; ++ } ++ i++; ++ frame_length -= 188; ++ } while (frame_length > 0); ++ st->nrpackets = i; ++ } ++ ptr = qtd->urb->buf + frame_desc->offset; ++ /* Point the HC at the DMA address of the bounce buffers */ ++ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; ++ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; ++ ++ /* fixup xfersize to the actual packet size */ ++ st->hctsiz_copy.b.pid = 0; ++ st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0]; ++ return 1; ++ } else { ++ /* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */ ++ return 0; ++ } ++ } ++} ++ ++/* ++ * Pushing a periodic request into the queue near the EOF1 point ++ * in a microframe causes erroneous behaviour (frmovrun) interrupt. ++ * Usually, the request goes out on the bus causing a transfer but ++ * the core does not transfer the data to memory. ++ * This guard interval (in number of 60MHz clocks) is required which ++ * must cater for CPU latency between reading the value and enabling ++ * the channel. ++ */ ++#define PERIODIC_FRREM_BACKOFF 1000 ++ ++int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ dwc_hc_t *hc = qh->channel; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ int frame; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; ++ int xfer_len, nrpackets; ++ hcdma_data_t hcdma; ++ hfnum_data_t hfnum; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) ++ return 0; ++ ++ st->nr_errors = 0; ++ ++ st->hcchar_copy.d32 = 0; ++ st->hcchar_copy.b.mps = hc->max_packet; ++ st->hcchar_copy.b.epdir = hc->ep_is_in; ++ st->hcchar_copy.b.devaddr = hc->dev_addr; ++ st->hcchar_copy.b.epnum = hc->ep_num; ++ st->hcchar_copy.b.eptype = hc->ep_type; ++ ++ st->hcintmsk_copy.b.chhltd = 1; ++ ++ frame = dwc_otg_hcd_get_frame_number(hcd); ++ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; ++ ++ st->hcchar_copy.b.lspddev = 0; ++ /* Enable the channel later as a final register write. */ ++ ++ st->hcsplt_copy.d32 = 0; ++ ++ st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs; ++ st->hs_isoc_info.nrframes = qtd->urb->packet_count; ++ /* grab the next DMA address offset from the array */ ++ st->hcdma_copy.d32 = qtd->urb->dma; ++ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset; ++ ++ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as ++ * the core needs to be told to send the correct number. Caution: for IN transfers, ++ * this is always set to the maximum size of the endpoint. */ ++ xfer_len = st->hs_isoc_info.iso_desc[0].length; ++ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; ++ if (nrpackets == 0) ++ nrpackets = 1; ++ st->hcchar_copy.b.multicnt = nrpackets; ++ st->hctsiz_copy.b.pktcnt = nrpackets; ++ ++ /* Initial PID also needs to be set */ ++ if (st->hcchar_copy.b.epdir == 0) { ++ st->hctsiz_copy.b.xfersize = xfer_len; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_MDATA; ++ break; ++ } ++ ++ } else { ++ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA1; ++ break; ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA2; ++ break; ++ } ++ } ++ ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); ++ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); ++ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { ++ /* Prevent queueing near EOF1. Bad things happen if a periodic ++ * split transaction is queued very close to EOF. ++ */ ++ st->fsm = FIQ_HS_ISOC_SLEEPING; ++ } else { ++ st->fsm = FIQ_HS_ISOC_TURBO; ++ st->hcchar_copy.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ } ++ mb(); ++ st->hcchar_copy.b.chen = 0; ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ return 0; ++} ++ ++ ++/** ++ * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state ++ * @hcd: Pointer to the dwc_otg_hcd struct ++ * @qh: Pointer to the endpoint's queue head ++ * ++ * This overrides the dwc_otg driver's normal method of queueing a transaction. ++ * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup ++ * for the nominated host channel. ++ * ++ * For periodic transfers, it also peeks at the FIQ state to see if an immediate ++ * start is possible. If not, then the FIQ is left to start the transfer. ++ */ ++int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ int start_immediate = 1, i; ++ hfnum_data_t hfnum; ++ dwc_hc_t *hc = qh->channel; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ /* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */ ++ int hub_addr, port_addr, frame, uframe; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) ++ return 0; ++ st->nr_errors = 0; ++ ++ st->hcchar_copy.d32 = 0; ++ st->hcchar_copy.b.mps = hc->max_packet; ++ st->hcchar_copy.b.epdir = hc->ep_is_in; ++ st->hcchar_copy.b.devaddr = hc->dev_addr; ++ st->hcchar_copy.b.epnum = hc->ep_num; ++ st->hcchar_copy.b.eptype = hc->ep_type; ++ if (hc->ep_type & 0x1) { ++ if (hc->ep_is_in) ++ st->hcchar_copy.b.multicnt = 3; ++ else ++ /* Docs say set this to 1, but driver sets to 0! */ ++ st->hcchar_copy.b.multicnt = 0; ++ } else { ++ st->hcchar_copy.b.multicnt = 1; ++ st->hcchar_copy.b.oddfrm = 0; ++ } ++ st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0; ++ /* Enable the channel later as a final register write. */ ++ ++ st->hcsplt_copy.d32 = 0; ++ if(qh->do_split) { ++ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); ++ st->hcsplt_copy.b.compsplt = 0; ++ st->hcsplt_copy.b.spltena = 1; ++ // XACTPOS is for isoc-out only but needs initialising anyway. ++ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL; ++ if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) { ++ /* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL. ++ * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ ++ * will update as necessary. ++ */ ++ if (hc->xfer_len > 188) { ++ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN; ++ } ++ } ++ st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr; ++ st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr; ++ st->hub_addr = hub_addr; ++ st->port_addr = port_addr; ++ } ++ ++ st->hctsiz_copy.d32 = 0; ++ st->hctsiz_copy.b.dopng = 0; ++ st->hctsiz_copy.b.pid = hc->data_pid_start; ++ ++ if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { ++ hc->xfer_len = hc->max_packet; ++ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { ++ hc->xfer_len = 188; ++ } ++ st->hctsiz_copy.b.xfersize = hc->xfer_len; ++ ++ st->hctsiz_copy.b.pktcnt = 1; ++ ++ if (hc->ep_type & 0x1) { ++ /* ++ * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers, ++ * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array. ++ * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt ++ * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set ++ * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ ++ * must not touch internal driver state. ++ */ ++ if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) { ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ } else { ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ /* The FIQ depends upon no other interrupts being enabled except channel halt. ++ * Fixup channel interrupt mask. */ ++ st->hcintmsk_copy.d32 = 0; ++ st->hcintmsk_copy.b.chhltd = 1; ++ st->hcintmsk_copy.b.ahberr = 1; ++ ++ /* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions ++ * as Control puts the transfer into the non-periodic request queue and the ++ * non-periodic handler in the hub. Makes things lots easier. ++ */ ++ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) { ++ st->hcchar_copy.b.multicnt = 0; ++ st->hcchar_copy.b.oddfrm = 0; ++ st->hcchar_copy.b.eptype = UE_CONTROL; ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); ++ ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ ++ if (hc->ep_type & 0x1) { ++ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ frame = (hfnum.b.frnum & ~0x7) >> 3; ++ uframe = hfnum.b.frnum & 0x7; ++ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { ++ /* Prevent queueing near EOF1. Bad things happen if a periodic ++ * split transaction is queued very close to EOF. ++ */ ++ start_immediate = 0; ++ } else if (uframe == 5) { ++ start_immediate = 0; ++ } else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) { ++ start_immediate = 0; ++ } else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) { ++ start_immediate = 0; ++ } else { ++ /* Search through all host channels to determine if a transaction ++ * is currently in progress */ ++ for (i = 0; i < hcd->core_if->core_params->host_channels; i++) { ++ if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ switch (hcd->fiq_state->channel[i].fsm) { ++ /* TT is reserved for channels that are in the middle of a periodic ++ * split transaction. ++ */ ++ case FIQ_PER_SSPLIT_STARTED: ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ case FIQ_PER_ISO_OUT_LAST: ++ if (hcd->fiq_state->channel[i].hub_addr == hub_addr && ++ hcd->fiq_state->channel[i].port_addr == port_addr) { ++ start_immediate = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ if (!start_immediate) ++ break; ++ } ++ } ++ } ++ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) ++ start_immediate = 1; ++ ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); ++ switch (hc->ep_type) { ++ case UE_CONTROL: ++ case UE_BULK: ++ st->fsm = FIQ_NP_SSPLIT_STARTED; ++ break; ++ case UE_ISOCHRONOUS: ++ if (hc->ep_is_in) { ++ if (start_immediate) { ++ st->fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ } ++ } else { ++ if (start_immediate) { ++ /* Single-isoc OUT packets don't require FIQ involvement */ ++ if (st->nrpackets == 1) { ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_PENDING; ++ } ++ } ++ break; ++ case UE_INTERRUPT: ++ if (fiq_fsm_mask & 0x8) { ++ st->fsm = FIQ_NP_SSPLIT_STARTED; ++ } else if (start_immediate) { ++ st->fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ } ++ default: ++ break; ++ } ++ if (start_immediate) { ++ /* Set the oddfrm bit as close as possible to actual queueing */ ++ frame = dwc_otg_hcd_get_frame_number(hcd); ++ st->expected_uframe = (frame + 1) & 0x3FFF; ++ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; ++ st->hcchar_copy.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ } ++ mb(); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ return 0; ++} ++ ++ +/** + * This function selects transactions from the HCD transfer schedule and + * assigns them to available host channels. It is called from HCD interrupt @@ -41110,13 +51097,9 @@ index 0000000..1e89549 + dwc_otg_qh_t *qh; + int num_channels; + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; + dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; + -+#ifdef DEBUG_SOF -+ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); -+#endif -+ +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_per_scheduled = 0; + last_sel_trans_num_nonper_scheduled = 0; @@ -41128,6 +51111,9 @@ index 0000000..1e89549 + + while (qh_ptr != &hcd->periodic_sched_ready && + !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ + if (microframe_schedule) { + // Make sure we leave one channel for non periodic transactions. + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); @@ -41153,8 +51139,6 @@ index 0000000..1e89549 + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, + &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -+ -+ ret_val = DWC_OTG_TRANSACTION_PERIODIC; + } + + /* @@ -41169,6 +51153,28 @@ index 0000000..1e89549 + num_channels - hcd->periodic_channels) && + !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { + ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ /* ++ * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission ++ * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed ++ * cheeky devices that just hold off using NAKs ++ */ ++ if (nak_holdoff && qh->do_split) { ++ if (qh->nak_frame != 0xffff) { ++ uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8); ++ uint16_t frame = dwc_otg_hcd_get_frame_number(hcd); ++ if (dwc_frame_num_le(frame, next_frame)) { ++ if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) { ++ hcd->fiq_state->next_sched_frame = next_frame; ++ } ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ continue; ++ } else { ++ qh->nak_frame = 0xFFFF; ++ } ++ } ++ } ++ + if (microframe_schedule) { + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + if (hcd->available_host_channels < 1) { @@ -41181,7 +51187,6 @@ index 0000000..1e89549 + last_sel_trans_num_nonper_scheduled++; +#endif /* DEBUG_HOST_CHANNELS */ + } -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); + + assign_and_init_hc(hcd, qh); + @@ -41195,21 +51200,41 @@ index 0000000..1e89549 + &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + -+ if (ret_val == DWC_OTG_TRANSACTION_NONE) { -+ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; -+ } else { -+ ret_val = DWC_OTG_TRANSACTION_ALL; -+ } + + if (!microframe_schedule) + hcd->non_periodic_channels++; + } ++ /* we moved a non-periodic QH to the active schedule. If the inactive queue is empty, ++ * stop the FIQ from kicking us. We could potentially still have elements here if we ++ * ran out of host channels. ++ */ ++ if (fiq_enable) { ++ if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) { ++ hcd->fiq_state->kick_np_queues = 0; ++ } else { ++ /* For each entry remaining in the NP inactive queue, ++ * if this a NAK'd retransmit then don't set the kick flag. ++ */ ++ if(nak_holdoff) { ++ DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) { ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ if (qh->nak_frame == 0xFFFF) { ++ hcd->fiq_state->kick_np_queues = 1; ++ } ++ } ++ } ++ } ++ } ++ if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) ++ ret_val |= DWC_OTG_TRANSACTION_PERIODIC; ++ ++ if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) ++ ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC; ++ + +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels; +#endif /* DEBUG_HOST_CHANNELS */ -+ -+ DWC_SPINLOCK_FREE(channel_lock); + return ret_val; +} + @@ -41244,6 +51269,12 @@ index 0000000..1e89549 + hc->qh->ping_state = 0; + } + } else if (!hc->xfer_started) { ++ if (fiq_fsm_enable && hc->error_state) { ++ hcd->fiq_state->channel[hc->hc_num].nr_errors = ++ DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count; ++ hcd->fiq_state->channel[hc->hc_num].fsm = ++ FIQ_PASSTHROUGH_ERRORSTATE; ++ } + dwc_otg_hc_start_transfer(hcd->core_if, hc); + hc->qh->ping_state = 0; + } @@ -41296,7 +51327,7 @@ index 0000000..1e89549 + hptxsts_data_t tx_status; + dwc_list_link_t *qh_ptr; + dwc_otg_qh_t *qh; -+ int status; ++ int status = 0; + int no_queue_space = 0; + int no_fifo_space = 0; + @@ -41323,20 +51354,36 @@ index 0000000..1e89549 + + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); + -+ /* -+ * Set a flag if we're queuing high-bandwidth in slave mode. -+ * The flag prevents any halts to get into the request queue in -+ * the middle of multiple high-bandwidth packets getting queued. -+ */ -+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -+ hcd->core_if->queuing_high_bandwidth = 1; ++ // Do not send a split start transaction any later than frame .6 ++ // Note, we have to schedule a periodic in .5 to make it go in .6 ++ if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) ++ { ++ qh_ptr = qh_ptr->next; ++ hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; ++ continue; + } -+ status = -+ queue_transaction(hcd, qh->channel, -+ tx_status.b.ptxfspcavail); -+ if (status < 0) { -+ no_fifo_space = 1; -+ break; ++ ++ if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { ++ if (qh->do_split) ++ fiq_fsm_queue_split_transaction(hcd, qh); ++ else ++ fiq_fsm_queue_isoc_transaction(hcd, qh); ++ } else { ++ ++ /* ++ * Set a flag if we're queueing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { ++ hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ status = queue_transaction(hcd, qh->channel, ++ tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } + } + + /* @@ -41452,17 +51499,20 @@ index 0000000..1e89549 + + qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, + qh_list_entry); -+ status = -+ queue_transaction(hcd, qh->channel, -+ tx_status.b.nptxfspcavail); + -+ if (status > 0) { -+ more_to_do = 1; -+ } else if (status < 0) { -+ no_fifo_space = 1; -+ break; ++ if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { ++ fiq_fsm_queue_split_transaction(hcd, qh); ++ } else { ++ status = queue_transaction(hcd, qh->channel, ++ tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } + } -+ + /* Advance to next QH, skipping start-of-list entry. */ + hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; + if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { @@ -41544,9 +51594,16 @@ index 0000000..1e89549 + */ + gintmsk_data_t gintmsk = {.d32 = 0 }; + gintmsk.b.nptxfempty = 1; -+ DWC_MODIFY_REG32(&hcd->core_if-> -+ core_global_regs->gintmsk, gintmsk.d32, -+ 0); ++ ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ } + } + } +} @@ -42977,17 +53034,13 @@ index 0000000..1e89549 + else + dwc_otg_urb = DWC_ALLOC(size); + -+ if (NULL != dwc_otg_urb) -+ dwc_otg_urb->packet_count = iso_desc_count; ++ if (dwc_otg_urb) ++ dwc_otg_urb->packet_count = iso_desc_count; + else { -+ dwc_otg_urb->packet_count = 0; -+ if (size != 0) { -+ DWC_ERROR("**** DWC OTG HCD URB alloc - " -+ "%salloc of %db failed\n", -+ atomic_alloc?"atomic ":"", size); -+ } -+ } -+ ++ DWC_ERROR("**** DWC OTG HCD URB alloc - " ++ "%salloc of %db failed\n", ++ atomic_alloc?"atomic ":"", size); ++ } + return dwc_otg_urb; +} + @@ -43338,10 +53391,10 @@ index 0000000..1e89549 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h new file mode 100644 -index 0000000..bb4f67a +index 0000000..43dbed9 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -0,0 +1,824 @@ +@@ -0,0 +1,862 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $ + * $Revision: #58 $ @@ -43384,6 +53437,8 @@ index 0000000..bb4f67a +#include "dwc_otg_core_if.h" +#include "dwc_list.h" +#include "dwc_otg_cil.h" ++#include "dwc_otg_fiq_fsm.h" ++ + +/** + * @file @@ -43512,10 +53567,10 @@ index 0000000..bb4f67a + +/** Transaction types. */ +typedef enum dwc_otg_transaction_type { -+ DWC_OTG_TRANSACTION_NONE, -+ DWC_OTG_TRANSACTION_PERIODIC, -+ DWC_OTG_TRANSACTION_NON_PERIODIC, -+ DWC_OTG_TRANSACTION_ALL ++ DWC_OTG_TRANSACTION_NONE = 0, ++ DWC_OTG_TRANSACTION_PERIODIC = 1, ++ DWC_OTG_TRANSACTION_NON_PERIODIC = 2, ++ DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC +} dwc_otg_transaction_type_e; + +struct dwc_otg_qh; @@ -43665,6 +53720,11 @@ index 0000000..bb4f67a + */ + uint16_t sched_frame; + ++ /* ++ ** Frame a NAK was received on this queue head, used to minimise NAK retransmission ++ */ ++ uint16_t nak_frame; ++ + /** (micro)frame at which last start split was initialized. */ + uint16_t start_split_frame; + @@ -43709,10 +53769,19 @@ index 0000000..bb4f67a + + uint16_t speed; + uint16_t frame_usecs[8]; ++ ++ uint32_t skip_count; +} dwc_otg_qh_t; + +DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); + ++typedef struct urb_tq_entry { ++ struct urb *urb; ++ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries; ++} urb_tq_entry_t; ++ ++DWC_TAILQ_HEAD(urb_list, urb_tq_entry); ++ +/** + * This structure holds the state of the HCD, including the non-periodic and + * periodic schedules. @@ -43890,9 +53959,12 @@ index 0000000..bb4f67a + /* Tasket to do a reset */ + dwc_tasklet_t *reset_tasklet; + ++ dwc_tasklet_t *completion_tasklet; ++ struct urb_list completed_urb_list; ++ + /* */ + dwc_spinlock_t *lock; -+ ++ dwc_spinlock_t *channel_lock; + /** + * Private data that could be used by OS wrapper. + */ @@ -43903,9 +53975,21 @@ index 0000000..bb4f67a + /** Frame List */ + uint32_t *frame_list; + ++ /** Hub - Port assignment */ ++ int hub_port[128]; ++#ifdef FIQ_DEBUG ++ int hub_port_alloc[2048]; ++#endif ++ + /** Frame List DMA address */ + dma_addr_t frame_list_dma; + ++ struct fiq_stack *fiq_stack; ++ struct fiq_state *fiq_state; ++ ++ /** Virtual address for split transaction DMA bounce buffers */ ++ struct fiq_dma_blob *fiq_dmab; ++ +#ifdef DEBUG + uint32_t frrem_samples; + uint64_t frrem_accum; @@ -43933,6 +54017,13 @@ index 0000000..bb4f67a +extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, + dwc_otg_transaction_type_e tr_type); + ++int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); ++void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); ++ ++extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); ++extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh); ++extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num); ++ +/** @} */ + +/** @name Interrupt Handler Functions */ @@ -44168,10 +54259,10 @@ index 0000000..bb4f67a +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c new file mode 100644 -index 0000000..274967b +index 0000000..ee920c4 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -@@ -0,0 +1,1133 @@ +@@ -0,0 +1,1132 @@ +/*========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $ + * $Revision: #10 $ @@ -44450,7 +54541,7 @@ index 0000000..274967b +static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) +{ + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; + + dwc_hc_t *hc = qh->channel; + if (dwc_qh_is_non_per(qh)) { @@ -44480,7 +54571,6 @@ index 0000000..274967b + dwc_memset(qh->desc_list, 0x00, + sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); + } -+ DWC_SPINLOCK_FREE(channel_lock); +} + +/** @@ -45307,10 +55397,10 @@ index 0000000..274967b +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h new file mode 100644 -index 0000000..4823167 +index 0000000..fb57db0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -@@ -0,0 +1,412 @@ +@@ -0,0 +1,417 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $ + * $Revision: #12 $ @@ -45426,6 +55516,11 @@ index 0000000..4823167 + */ +extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); + ++/** This function is used to handle the fast interrupt ++ * ++ */ ++extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void); ++ +/** + * Returns private data set by + * dwc_otg_hcd_set_priv_data function. @@ -45725,10 +55820,10 @@ index 0000000..4823167 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c new file mode 100644 -index 0000000..b41e164 +index 0000000..8a31562 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -0,0 +1,2106 @@ +@@ -0,0 +1,2713 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $ + * $Revision: #89 $ @@ -45766,44 +55861,99 @@ index 0000000..b41e164 +#include "dwc_otg_hcd.h" +#include "dwc_otg_regs.h" + ++#include ++#include ++#include ++ ++ +extern bool microframe_schedule; + +/** @file + * This file contains the implementation of the HCD Interrupt handlers. + */ + ++int fiq_done, int_done; ++ ++#ifdef FIQ_DEBUG ++char buffer[1000*16]; ++int wptr; ++void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) ++{ ++ FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB; ++ va_list args; ++ char text[17]; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; ++ ++ if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) ++ { ++ local_fiq_disable(); ++ snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); ++ va_start(args, fmt); ++ vsnprintf(text+8, 9, fmt, args); ++ va_end(args); ++ ++ memcpy(buffer + wptr, text, 16); ++ wptr = (wptr + 16) % sizeof(buffer); ++ local_fiq_enable(); ++ } ++} ++#endif ++ +/** This function handles interrupts for the HCD. */ +int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) +{ + int retval = 0; -+ ++ static int last_time; + dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; + gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ hfnum_data_t hfnum; ++ haintmsk_data_t haintmsk; ++ +#ifdef DEBUG + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; + -+ //GRAYG: debugging -+ if (NULL == global_regs) { -+ DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p " -+ "core_if=%p\n", -+ dwc_otg_hcd, global_regs); -+ return retval; -+ } +#endif + ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ + /* Exit from ISR if core is hibernated */ + if (core_if->hibernation_suspend == 1) { -+ return retval; ++ goto exit_handler_routine; + } + DWC_SPINLOCK(dwc_otg_hcd->lock); + /* Check if HOST Mode */ + if (dwc_otg_is_host_mode(core_if)) { -+ gintsts.d32 = dwc_otg_read_core_intr(core_if); -+ if (!gintsts.d32) { -+ DWC_SPINUNLOCK(dwc_otg_hcd->lock); -+ return 0; ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ /* Pull in from the FIQ's disabled mask */ ++ gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); ++ dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; + } ++ ++ if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) { ++ gintsts.b.hcintr = 1; ++ } ++ ++ /* Danger will robinson: fake a SOF if necessary */ ++ if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) { ++ gintsts.b.sofintr = 1; ++ } ++ gintsts.d32 &= gintmsk.d32; ++ ++ if (fiq_enable) { ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } ++ ++ if (!gintsts.d32) { ++ goto exit_handler_routine; ++ } ++ +#ifdef DEBUG ++ // We should be OK doing this because the common interrupts should already have been serviced + /* Don't print debug message in the interrupt handler on SOF */ +#ifndef DEBUG_SOF + if (gintsts.d32 != DWC_SOF_INTR_MASK) @@ -45819,10 +55969,11 @@ index 0000000..b41e164 + "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n", + gintsts.d32, core_if); +#endif -+ ++ hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); + if (gintsts.b.sofintr) { + retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); + } ++ + if (gintsts.b.rxstsqlvl) { + retval |= + dwc_otg_hcd_handle_rx_status_q_level_intr @@ -45837,7 +55988,18 @@ index 0000000..b41e164 + /** @todo Implement i2cintr handler. */ + } + if (gintsts.b.portintr) { ++ ++ gintmsk_data_t gintmsk = { .b.portintr = 1}; + retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ } + } + if (gintsts.b.hcintr) { + retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); @@ -45869,11 +56031,56 @@ index 0000000..b41e164 +#endif + + } ++ ++exit_handler_routine: ++ if (fiq_enable) { ++ gintmsk_data_t gintmsk_new; ++ haintmsk_data_t haintmsk_new; ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; ++ if(fiq_fsm_enable) ++ haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; ++ else ++ haintmsk_new.d32 = 0x0000FFFF; ++ ++ /* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */ ++ if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) { ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16)); ++ if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) { ++ fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR"); ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16))); ++ while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17))) ++ ; ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31)); ++ dwc_otg_hcd->fiq_state->mphi_int_count = 0; ++ } ++ int_done++; ++ } ++ haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); ++ /* Re-enable interrupts that the FIQ masked (first time round) */ ++ FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ ++ if ((jiffies / HZ) > last_time) { ++ //dwc_otg_qh_t *qh; ++ //dwc_list_link_t *cur; ++ /* Once a second output the fiq and irq numbers, useful for debug */ ++ last_time = jiffies / HZ; ++ // DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d", ++ // dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels, ++ // dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done); ++ //printk(KERN_WARNING "Periodic queues:\n"); ++ } ++ } ++ + DWC_SPINUNLOCK(dwc_otg_hcd->lock); + return retval; +} + +#ifdef DWC_TRACK_MISSED_SOFS ++ +#warning Compiling code to track missed SOFs +#define FRAME_NUM_ARRAY_SIZE 1000 +/** @@ -45916,10 +56123,12 @@ index 0000000..b41e164 +int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) +{ + hfnum_data_t hfnum; ++ gintsts_data_t gintsts = { .d32 = 0 }; + dwc_list_link_t *qh_entry; + dwc_otg_qh_t *qh; + dwc_otg_transaction_type_e tr_type; -+ gintsts_data_t gintsts = {.d32 = 0 }; ++ int did_something = 0; ++ int32_t next_sched_frame = -1; + + hfnum.d32 = + DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); @@ -45943,23 +56152,38 @@ index 0000000..b41e164 + qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); + qh_entry = qh_entry->next; + if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { ++ + /* + * Move QH to the ready list to be executed next + * (micro)frame. + */ + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, + &qh->qh_list_entry); ++ ++ did_something = 1; ++ } ++ else ++ { ++ if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame)) ++ { ++ next_sched_frame = qh->sched_frame; ++ } + } + } ++ if (fiq_enable) ++ hcd->fiq_state->next_sched_frame = next_sched_frame; ++ + tr_type = dwc_otg_hcd_select_transactions(hcd); + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ did_something = 1; + } + -+ /* Clear interrupt */ -+ gintsts.b.sofintr = 1; -+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -+ ++ /* Clear interrupt - but do not trample on the FIQ sof */ ++ if (!fiq_fsm_enable) { ++ gintsts.b.sofintr = 1; ++ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ } + return 1; +} + @@ -46235,12 +56459,25 @@ index 0000000..b41e164 +{ + int i; + int retval = 0; -+ haint_data_t haint; ++ haint_data_t haint = { .d32 = 0 } ; + + /* Clear appropriate bits in HCINTn to clear the interrupt bit in + * GINTSTS */ + -+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ if (!fiq_fsm_enable) ++ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ ++ // Overwrite with saved interrupts from fiq handler ++ if(fiq_fsm_enable) ++ { ++ /* check the mask? */ ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); ++ dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } + + for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { + if (haint.b2.chint & (1 << i)) { @@ -46282,7 +56519,8 @@ index 0000000..b41e164 + *short_read = (hctsiz.b.xfersize != 0); + } + } else if (hc->qh->do_split) { -+ length = qtd->ssplit_out_xfer_count; ++ //length = split_out_xfersize[hc->hc_num]; ++ length = qtd->ssplit_out_xfer_count; + } else { + length = hc->xfer_len; + } @@ -46326,7 +56564,6 @@ index 0000000..b41e164 + DWC_OTG_HC_XFER_COMPLETE, + &short_read); + -+ + /* non DWORD-aligned buffer case handling. */ + if (hc->align_buff && xfer_length && hc->ep_is_in) { + dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, @@ -46528,11 +56765,22 @@ index 0000000..b41e164 + dwc_otg_transaction_type_e tr_type; + int free_qtd; + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; ++ ++ int hog_port = 0; + + DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", + __func__, hc->hc_num, halt_status, hc->xfer_len); + ++ if(fiq_fsm_enable && hc->do_split) { ++ if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { ++ if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || ++ hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { ++ hog_port = 0; ++ } ++ } ++ } ++ + switch (halt_status) { + case DWC_OTG_HC_XFER_URB_COMPLETE: + free_qtd = 1; @@ -46585,6 +56833,8 @@ index 0000000..b41e164 + * function clears the channel interrupt enables and conditions, so + * there's no need to clear the Channel Halted interrupt separately. + */ ++ if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH) ++ dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num); + dwc_otg_hc_cleanup(hcd->core_if, hc); + DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); + @@ -46607,6 +56857,7 @@ index 0000000..b41e164 + + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + hcd->available_host_channels++; ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + } + @@ -46615,7 +56866,6 @@ index 0000000..b41e164 + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); + } -+ DWC_SPINLOCK_FREE(channel_lock); +} + +/** @@ -46653,7 +56903,15 @@ index 0000000..b41e164 + * be processed. + */ + gintmsk.b.nptxfempty = 1; -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } + } else { + /* + * Move the QH from the periodic queued schedule to @@ -46670,7 +56928,15 @@ index 0000000..b41e164 + * processed. + */ + gintmsk.b.ptxfempty = 1; -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } + } + } +} @@ -47026,6 +57292,17 @@ index 0000000..b41e164 + "NAK Received--\n", hc->hc_num); + + /* ++ * When we get bulk NAKs then remember this so we holdoff on this qh until ++ * the beginning of the next frame ++ */ ++ switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_BULK: ++ case UE_CONTROL: ++ if (nak_holdoff && qtd->qh->do_split) ++ hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); ++ } ++ ++ /* + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and + * interrupt. Re-start the SSPLIT transfer. + */ @@ -47047,7 +57324,11 @@ index 0000000..b41e164 + * transfers in DMA mode for the sole purpose of + * resetting the error count after a transaction error + * occurs. The core will continue transferring data. ++ * Disable other interrupts unmasked for the same ++ * reason. + */ ++ disable_hc_int(hc_regs, datatglerr); ++ disable_hc_int(hc_regs, ack); + qtd->error_count = 0; + goto handle_nak_done; + } @@ -47159,6 +57440,15 @@ index 0000000..b41e164 + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); + } + } else { ++ /* ++ * An unmasked ACK on a non-split DMA transaction is ++ * for the sole purpose of resetting error counts. Disable other ++ * interrupts unmasked for the same reason. ++ */ ++ if(hcd->core_if->dma_enable) { ++ disable_hc_int(hc_regs, datatglerr); ++ disable_hc_int(hc_regs, nak); ++ } + qtd->error_count = 0; + + if (hc->qh->ping_state) { @@ -47221,8 +57511,10 @@ index 0000000..b41e164 + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { + int frnum = dwc_otg_hcd_get_frame_number(hcd); + ++ // With the FIQ running we only ever see the failed NYET + if (dwc_full_frame_num(frnum) != -+ dwc_full_frame_num(hc->qh->sched_frame)) { ++ dwc_full_frame_num(hc->qh->sched_frame) || ++ fiq_fsm_enable) { + /* + * No longer in the same full speed frame. + * Treat this as a transaction error. @@ -47509,13 +57801,28 @@ index 0000000..b41e164 + dwc_otg_qtd_t * qtd) +{ + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " -+ "Data Toggle Error--\n", hc->hc_num); ++ "Data Toggle Error on %s transfer--\n", ++ hc->hc_num, (hc->ep_is_in ? "IN" : "OUT")); + -+ if (hc->ep_is_in) { ++ /* Data toggles on split transactions cause the hc to halt. ++ * restart transfer */ ++ if(hc->qh->do_split) ++ { ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ } else if (hc->ep_is_in) { ++ /* An unmasked data toggle error on a non-split DMA transaction is ++ * for the sole purpose of resetting error counts. Disable other ++ * interrupts unmasked for the same reason. ++ */ ++ if(hcd->core_if->dma_enable) { ++ disable_hc_int(hc_regs, ack); ++ disable_hc_int(hc_regs, nak); ++ } + qtd->error_count = 0; -+ } else { -+ DWC_ERROR("Data Toggle Error on OUT transfer," -+ "channel %d\n", hc->hc_num); + } + + disable_hc_int(hc_regs, datatglerr); @@ -47595,10 +57902,9 @@ index 0000000..b41e164 + dwc_otg_hc_regs_t * hc_regs, + dwc_otg_qtd_t * qtd) +{ ++ int out_nak_enh = 0; + hcint_data_t hcint; + hcintmsk_data_t hcintmsk; -+ int out_nak_enh = 0; -+ + /* For core with OUT NAK enhancement, the flow for high- + * speed CONTROL/BULK OUT is handled a little differently. + */ @@ -47628,6 +57934,7 @@ index 0000000..b41e164 + } + + /* Read the HCINTn register to determine the cause for the halt. */ ++ + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); + @@ -47668,6 +57975,8 @@ index 0000000..b41e164 + handle_hc_babble_intr(hcd, hc, hc_regs, qtd); + } else if (hcint.b.frmovrun) { + handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.datatglerr) { ++ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); + } else if (!out_nak_enh) { + if (hcint.b.nyet) { + /* @@ -47717,12 +58026,24 @@ index 0000000..b41e164 + DWC_READ_REG32(&hcd-> + core_if->core_global_regs-> + gintsts)); ++ /* Failthrough: use 3-strikes rule */ ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); + } + + } + } else { + DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", + hcint.d32); ++ /* Failthrough: use 3-strikes rule */ ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); + } +} + @@ -47759,6 +58080,361 @@ index 0000000..b41e164 + return 1; +} + ++ ++/** ++ * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on ++ * FIQ transfer completion ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * 1. Un-mangle the status as recorded in each iso_frame_desc status ++ * 2. Copy it from the dwc_otg_urb into the real URB ++ */ ++void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) ++{ ++ struct dwc_otg_hcd_urb *dwc_urb = qtd->urb; ++ int nr_frames = dwc_urb->packet_count; ++ int i; ++ hcint_data_t frame_hcint; ++ ++ for (i = 0; i < nr_frames; i++) { ++ frame_hcint.d32 = dwc_urb->iso_descs[i].status; ++ if (frame_hcint.b.xfercomp) { ++ dwc_urb->iso_descs[i].status = 0; ++ dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length; ++ } else if (frame_hcint.b.frmovrun) { ++ if (qh->ep_is_in) ++ dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES; ++ else ++ dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else if (frame_hcint.b.xacterr) { ++ dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else if (frame_hcint.b.bblerr) { ++ dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else { ++ /* Something went wrong */ ++ dwc_urb->iso_descs[i].status = -1; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ dwc_urb->error_count++; ++ } ++ } ++ //printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n", ++ // __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count); ++ hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0); ++ release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++} ++ ++/** ++ * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state. ++ * Returns total length of data or -1 if the buffers were not used. ++ * ++ */ ++int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) ++{ ++ dwc_hc_t *hc = qh->channel; ++ struct fiq_dma_blob *blob = hcd->fiq_dmab; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ uint8_t *ptr = NULL; ++ int index = 0, len = 0; ++ int i = 0; ++ if (hc->ep_is_in) { ++ /* Copy data out of the DMA bounce buffers to the URB's buffer. ++ * The align_buf is ignored as this is ignored on FSM enqueue. */ ++ ptr = qtd->urb->buf; ++ if (qh->ep_type == UE_ISOCHRONOUS) { ++ /* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */ ++ index = qtd->isoc_frame_index; ++ ptr += qtd->urb->iso_descs[index].offset; ++ } else { ++ /* Need to increment by actual_length for interrupt IN */ ++ ptr += qtd->urb->actual_length; ++ } ++ ++ for (i = 0; i < st->dma_info.index; i++) { ++ len += st->dma_info.slot_len[i]; ++ dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]); ++ ptr += st->dma_info.slot_len[i]; ++ } ++ return len; ++ } else { ++ /* OUT endpoints - nothing to do. */ ++ return -1; ++ } ++ ++} ++/** ++ * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt ++ * from a channel handled in the FIQ ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * If a host channel interrupt was received by the IRQ and this was a channel ++ * used by the FIQ, the execution flow for transfer completion is substantially ++ * different from the normal (messy) path. This function and its friends handles ++ * channel cleanup and transaction completion from a FIQ transaction. ++ */ ++void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) ++{ ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ dwc_hc_t *hc = hcd->hc_ptr_array[num]; ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); ++ dwc_otg_qh_t *qh = hc->qh; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; ++ hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; ++ int hostchannels = 0; ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); ++ ++ hostchannels = hcd->available_host_channels; ++ switch (st->fsm) { ++ case FIQ_TEST: ++ break; ++ ++ case FIQ_DEQUEUE_ISSUED: ++ /* hc_halt was called. QTD no longer exists. */ ++ /* TODO: for a nonperiodic split transaction, need to issue a ++ * CLEAR_TT_BUFFER hub command if we were in the start-split phase. ++ */ ++ release_channel(hcd, hc, NULL, hc->halt_status); ++ break; ++ ++ case FIQ_NP_SPLIT_DONE: ++ /* Nonperiodic transaction complete. */ ++ if (!hc->ep_is_in) { ++ qtd->ssplit_out_xfer_count = hc->xfer_len; ++ } ++ if (hcint.b.xfercomp) { ++ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.nak) { ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } ++ break; ++ ++ case FIQ_NP_SPLIT_HS_ABORTED: ++ /* A HS abort is a 3-strikes on the HS bus at any point in the transaction. ++ * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that ++ * because there's no guarantee which order a non-periodic split happened in. ++ * We could end up clearing a perfectly good transaction out of the buffer. ++ */ ++ if (hcint.b.xacterr) { ++ qtd->error_count += st->nr_errors; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ local_fiq_disable(); ++ BUG(); ++ } ++ break; ++ ++ case FIQ_NP_SPLIT_LS_ABORTED: ++ /* A few cases can cause this - either an unknown state on a SSPLIT or ++ * STALL/data toggle error response on a CSPLIT */ ++ if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.datatglerr) { ++ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ local_fiq_disable(); ++ BUG(); ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_DONE: ++ /* Isoc IN or Interrupt IN/OUT */ ++ ++ /* Flow control here is different from the normal execution by the driver. ++ * We need to completely ignore most of the driver's method of handling ++ * split transactions and do it ourselves. ++ */ ++ if (hc->ep_type == UE_INTERRUPT) { ++ if (hcint.b.nak) { ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } else if (hc->ep_is_in) { ++ int len; ++ len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num); ++ //printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length); ++ qtd->urb->actual_length += len; ++ if (qtd->urb->actual_length >= qtd->urb->length) { ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ /* Interrupt transfer not complete yet - is it a short read? */ ++ if (len < hc->max_packet) { ++ /* Interrupt transaction complete */ ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ /* Further transactions required */ ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ } else { ++ /* Interrupt OUT complete. */ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ qtd->urb->actual_length += hc->xfer_len; ++ if (qtd->urb->actual_length >= qtd->urb->length) { ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ } else { ++ /* ISOC IN complete. */ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ int len = 0; ++ /* Record errors, update qtd. */ ++ if (st->nr_errors) { ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } else { ++ frame_desc->status = 0; ++ /* Unswizzle dma */ ++ len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num); ++ frame_desc->actual_length = len; ++ } ++ qtd->isoc_frame_index++; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_DONE: { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ if (st->nr_errors) { ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } else { ++ frame_desc->status = 0; ++ frame_desc->actual_length = frame_desc->length; ++ } ++ qtd->isoc_frame_index++; ++ qtd->isoc_split_offset = 0; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_NYET_ABORTED: ++ /* Doh. lost the data. */ ++ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " ++ "- FIQ reported NYET. Data may have been lost.\n", ++ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); ++ if (hc->ep_type == UE_ISOCHRONOUS) { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ qtd->isoc_frame_index++; ++ qtd->isoc_split_offset = 0; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ case FIQ_HS_ISOC_DONE: ++ /* The FIQ has performed a whole pile of isochronous transactions. ++ * The status is recorded as the interrupt state should the transaction ++ * fail. ++ */ ++ dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num); ++ break; ++ ++ case FIQ_PER_SPLIT_LS_ABORTED: ++ if (hcint.b.xacterr) { ++ /* Hub has responded with an ERR packet. Device ++ * has been unplugged or the port has been disabled. ++ * TODO: need to issue a reset to the hub port. */ ++ qtd->error_count += 3; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed " ++ "- FIQ reported FSM=%d. Data may have been lost.\n", ++ st->fsm, hc->dev_addr, hc->ep_num); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_HS_ABORTED: ++ /* Either the SSPLIT phase suffered transaction errors or something ++ * unexpected happened. ++ */ ++ qtd->error_count += 3; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ /* Couldn't complete in the nominated frame */ ++ printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " ++ "- FIQ timed out. Data may have been lost.\n", ++ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); ++ if (hc->ep_type == UE_ISOCHRONOUS) { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ frame_desc->actual_length = 0; ++ if (hc->ep_is_in) { ++ frame_desc->status = -DWC_E_NO_STREAM_RES; ++ } else { ++ frame_desc->status = -DWC_E_COMMUNICATION; ++ } ++ qtd->isoc_frame_index++; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ default: ++ DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x", ++ hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num); ++ qtd->error_count++; ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ return; ++} ++ +/** Handles interrupt for a specific Host Channel */ +int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) +{ @@ -47773,15 +58449,41 @@ index 0000000..b41e164 + + hc = dwc_otg_hcd->hc_ptr_array[num]; + hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; ++ if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ /* We are responding to a channel disable. Driver ++ * state is cleared - our qtd has gone away. ++ */ ++ release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); ++ return 1; ++ } + qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); + ++ /* ++ * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ. ++ * Execution path is fundamentally different for the channels after a FIQ has completed ++ * a split transaction. ++ */ ++ if (fiq_fsm_enable) { ++ switch (dwc_otg_hcd->fiq_state->channel[num].fsm) { ++ case FIQ_PASSTHROUGH: ++ break; ++ case FIQ_PASSTHROUGH_ERRORSTATE: ++ /* Hook into the error count */ ++ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); ++ if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) { ++ qtd->error_count = 0; ++ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); ++ } ++ break; ++ default: ++ dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num); ++ return 1; ++ } ++ } ++ + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ DWC_DEBUGPL(DBG_HCDV, -+ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", -+ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); + hcint.d32 = hcint.d32 & hcintmsk.d32; -+ + if (!dwc_otg_hcd->core_if->dma_enable) { + if (hcint.b.chhltd && hcint.d32 != 0x2) { + hcint.b.chhltd = 0; @@ -47811,7 +58513,8 @@ index 0000000..b41e164 + retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); + } + if (hcint.b.ack) { -+ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ if(!hcint.b.chhltd) ++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); + } + if (hcint.b.nyet) { + retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); @@ -47833,14 +58536,14 @@ index 0000000..b41e164 + + return retval; +} -+ +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c new file mode 100644 -index 0000000..e4787f5 +index 0000000..1d28459 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -0,0 +1,893 @@ +@@ -0,0 +1,994 @@ ++ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ + * $Revision: #20 $ @@ -47893,12 +58596,14 @@ index 0000000..e4787f5 +#include +#include +#include ++#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +#include <../drivers/usb/core/hcd.h> +#else +#include +#endif ++#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) +#define USB_URB_EP_LINKING 1 @@ -47910,6 +58615,9 @@ index 0000000..e4787f5 +#include "dwc_otg_dbg.h" +#include "dwc_otg_driver.h" +#include "dwc_otg_hcd.h" ++ ++extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end; ++ +/** + * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is + * qualified with its direction (possible 32 endpoints per device). @@ -47919,6 +58627,8 @@ index 0000000..e4787f5 + +static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; + ++extern bool fiq_enable; ++ +/** @name Linux HC Driver API Functions */ +/** @{ */ +/* manage i/o requests, device state */ @@ -48102,13 +58812,15 @@ index 0000000..e4787f5 + +/** + * Sets the final status of an URB and returns it to the device driver. Any -+ * required cleanup of the URB is performed. ++ * required cleanup of the URB is performed. The HCD lock should be held on ++ * entry. + */ +static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, + dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) +{ + struct urb *urb = (struct urb *)urb_handle; -+ ++ urb_tq_entry_t *new_entry; ++ int rc = 0; + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { + DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", + __func__, urb, usb_pipedevice(urb->pipe), @@ -48122,7 +58834,7 @@ index 0000000..e4787f5 + } + } + } -+ ++ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t)); + urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); + /* Convert status value. */ + switch (status) { @@ -48144,6 +58856,9 @@ index 0000000..e4787f5 + case -DWC_E_OVERFLOW: + status = -EOVERFLOW; + break; ++ case -DWC_E_SHUTDOWN: ++ status = -ESHUTDOWN; ++ break; + default: + if (status) { + DWC_PRINTF("Uknown urb status %d\n", status); @@ -48183,20 +58898,34 @@ index 0000000..e4787f5 + urb); + } + } -+ + DWC_FREE(dwc_otg_urb); -+ ++ if (!new_entry) { ++ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); ++ urb->status = -EPROTO; ++ /* don't schedule the tasklet - ++ * directly return the packet here with error. */ +#if USB_URB_EP_LINKING -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); +#endif -+ DWC_SPINUNLOCK(hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); +#else -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); +#endif -+ DWC_SPINLOCK(hcd->lock); -+ ++ } else { ++ new_entry->urb = urb; ++#if USB_URB_EP_LINKING ++ rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); ++ if(0 == rc) { ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); ++ } ++#endif ++ if(0 == rc) { ++ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, ++ urb_tq_entries); ++ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet); ++ } ++ } + return 0; +} + @@ -48209,6 +58938,62 @@ index 0000000..e4787f5 + .get_b_hnp_enable = _get_b_hnp_enable, +}; + ++static struct fiq_handler fh = { ++ .name = "usb_fiq", ++}; ++ ++static void hcd_init_fiq(void *cookie) ++{ ++ dwc_otg_device_t *otg_dev = cookie; ++ dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; ++ struct pt_regs regs; ++ ++ if (claim_fiq(&fh)) { ++ DWC_ERROR("Can't claim FIQ"); ++ BUG(); ++ } ++ DWC_WARN("FIQ on core %d at 0x%08x", ++ smp_processor_id(), ++ (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); ++ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); ++ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); ++ memset(®s,0,sizeof(regs)); ++ ++ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; ++ if (fiq_fsm_enable) { ++ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; ++ //regs.ARM_r10 = dwc_otg_hcd->dma; ++ regs.ARM_fp = (long) dwc_otg_fiq_fsm; ++ } else { ++ regs.ARM_fp = (long) dwc_otg_fiq_nop; ++ } ++ ++ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); ++ ++// __show_regs(®s); ++ set_fiq_regs(®s); ++ ++ //Set the mphi periph to the required registers ++ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; ++ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; ++ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; ++ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; ++ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; ++ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; ++ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); ++ //Enable mphi peripheral ++ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); ++#ifdef DEBUG ++ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) ++ DWC_WARN("MPHI periph has been enabled"); ++ else ++ DWC_WARN("MPHI periph has NOT been enabled"); ++#endif ++ // Enable FIQ interrupt from USB peripheral ++ enable_fiq(INTERRUPT_VC_USB); ++ local_fiq_enable(); ++} ++ +/** + * Initializes the HCD. This function allocates memory for and initializes the + * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the @@ -48258,6 +59043,7 @@ index 0000000..e4787f5 + + hcd->regs = otg_dev->os_dep.base; + ++ + /* Initialize the DWC OTG HCD. */ + dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); + if (!dwc_otg_hcd) { @@ -48271,6 +59057,15 @@ index 0000000..e4787f5 + goto error2; + } + ++ if (fiq_enable) { ++ if (num_online_cpus() > 1) { ++ /* bcm2709: can run the FIQ on a separate core to IRQs */ ++ smp_call_function_single(1, hcd_init_fiq, otg_dev, 1); ++ } else { ++ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); ++ } ++ } ++ + otg_dev->hcd->otg_dev = otg_dev; + hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) @@ -48286,9 +59081,9 @@ index 0000000..e4787f5 + * IRQ line, and calls hcd_start method. + */ +#ifdef PLATFORM_INTERFACE -+ retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED | IRQF_DISABLED); ++ retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED | IRQF_DISABLED); +#else -+ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); ++ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); +#endif + if (retval < 0) { + goto error2; @@ -48385,9 +59180,13 @@ index 0000000..e4787f5 +/** Returns the current frame number. */ +static int get_frame_number(struct usb_hcd *hcd) +{ ++ hprt0_data_t hprt0; + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); -+ -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); ++ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3; ++ else ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); +} + +#ifdef DEBUG @@ -48450,9 +59249,7 @@ index 0000000..e4787f5 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + struct usb_host_endpoint *ep = urb->ep; +#endif -+#if USB_URB_EP_LINKING + dwc_irqflags_t irqflags; -+#endif + void **ref_ep_hcpriv = &ep->hcpriv; + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); + dwc_otg_hcd_urb_t *dwc_otg_urb; @@ -48504,9 +59301,8 @@ index 0000000..e4787f5 + if(dwc_otg_urb == NULL) + return -ENOMEM; + -+ urb->hcpriv = dwc_otg_urb; -+ if (!dwc_otg_urb && urb->number_of_packets) -+ return -ENOMEM; ++ if (!dwc_otg_urb && urb->number_of_packets) ++ return -ENOMEM; + + dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), ep_type, @@ -48546,37 +59342,42 @@ index 0000000..e4787f5 + iso_frame_desc[i].length); + } + ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); ++ urb->hcpriv = dwc_otg_urb; +#if USB_URB_EP_LINKING -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); + retval = usb_hcd_link_urb_to_ep(hcd, urb); -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); + if (0 == retval) +#endif -+ { -+ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -+ /*(dwc_otg_qh_t **)*/ -+ ref_ep_hcpriv, -+ mem_flags == GFP_ATOMIC ? 1 : 0); -+ if (0 == retval) { -+ if (alloc_bandwidth) { -+ allocate_bus_bandwidth(hcd, -+ dwc_otg_hcd_get_ep_bandwidth( -+ dwc_otg_hcd, *ref_ep_hcpriv), -+ urb); -+ } -+ } else { ++ { ++ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, ++ /*(dwc_otg_qh_t **)*/ ++ ref_ep_hcpriv, 1); ++ if (0 == retval) { ++ if (alloc_bandwidth) { ++ allocate_bus_bandwidth(hcd, ++ dwc_otg_hcd_get_ep_bandwidth( ++ dwc_otg_hcd, *ref_ep_hcpriv), ++ urb); ++ } ++ } else { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); +#if USB_URB_EP_LINKING -+ dwc_irqflags_t irqflags; -+ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -+ usb_hcd_unlink_urb_from_ep(hcd, urb); -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); ++ usb_hcd_unlink_urb_from_ep(hcd, urb); +#endif -+ if (retval == -DWC_E_NO_DEVICE) { -+ retval = -ENODEV; -+ } -+ } -+ } ++ DWC_FREE(dwc_otg_urb); ++ urb->hcpriv = NULL; ++ if (retval == -DWC_E_NO_DEVICE) ++ retval = -ENODEV; ++ } ++ } ++#if USB_URB_EP_LINKING ++ else ++ { ++ DWC_FREE(dwc_otg_urb); ++ urb->hcpriv = NULL; ++ } ++#endif ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); + return retval; +} + @@ -48620,6 +59421,8 @@ index 0000000..e4787f5 + usb_hcd_unlink_urb_from_ep(hcd, urb); +#endif + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++ ++ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + usb_hcd_giveback_urb(hcd, urb); +#else @@ -48736,10 +59539,10 @@ index 0000000..e4787f5 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c new file mode 100644 -index 0000000..0a1cbb7 +index 0000000..acd0dd7 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -0,0 +1,923 @@ +@@ -0,0 +1,957 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $ + * $Revision: #44 $ @@ -48924,6 +59727,7 @@ index 0000000..0a1cbb7 + if (microframe_schedule) + qh->speed = dev_speed; + ++ qh->nak_frame = 0xffff; + + if (((dev_speed == USB_SPEED_LOW) || + (dev_speed == USB_SPEED_FULL)) && @@ -48933,6 +59737,7 @@ index 0000000..0a1cbb7 + dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, + hub_port); + qh->do_split = 1; ++ qh->skip_count = 0; + } + + if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { @@ -49315,6 +60120,8 @@ index 0000000..0a1cbb7 + return status; +} + ++ ++ +/** + * Schedules an interrupt or isochronous transfer in the periodic schedule. + * @@ -49373,8 +60180,13 @@ index 0000000..0a1cbb7 + DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); + } + else { -+ /* Always start in the inactive schedule. */ -+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); ++ if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame))) ++ { ++ hcd->fiq_state->next_sched_frame = qh->sched_frame; ++ ++ } ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); + } + + if (!microframe_schedule) { @@ -49388,6 +60200,7 @@ index 0000000..0a1cbb7 + return status; +} + ++ +/** + * This function adds a QH to either the non periodic or periodic schedule if + * it is not already in the schedule. If the QH is already in the schedule, no @@ -49410,12 +60223,20 @@ index 0000000..0a1cbb7 + /* Always start in the inactive schedule. */ + DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, + &qh->qh_list_entry); ++ //hcd->fiq_state->kick_np_queues = 1; + } else { + status = schedule_periodic(hcd, qh); + if ( !hcd->periodic_qh_count ) { + intr_mask.b.sofintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -+ intr_mask.d32, intr_mask.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ } + } + hcd->periodic_qh_count++; + } @@ -49469,13 +60290,22 @@ index 0000000..0a1cbb7 + hcd->non_periodic_qh_ptr->next; + } + DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) ++ // hcd->fiq_state->kick_np_queues = 1; + } else { + deschedule_periodic(hcd, qh); + hcd->periodic_qh_count--; -+ if( !hcd->periodic_qh_count ) { ++ if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { + intr_mask.b.sofintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -+ intr_mask.d32, 0); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ } + } + } +} @@ -49501,6 +60331,7 @@ index 0000000..0a1cbb7 + if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { + /* Add back to inactive non-periodic schedule. */ + dwc_otg_hcd_qh_add(hcd, qh); ++ //hcd->fiq_state->kick_np_queues = 1; + } + } else { + uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); @@ -49510,6 +60341,7 @@ index 0000000..0a1cbb7 + if (sched_next_periodic_split) { + + qh->sched_frame = frame_number; ++ + if (dwc_frame_num_le(frame_number, + dwc_frame_num_inc + (qh->start_split_frame, @@ -49558,6 +60390,11 @@ index 0000000..0a1cbb7 + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, + &qh->qh_list_entry); + } else { ++ if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame)) ++ { ++ hcd->fiq_state->next_sched_frame = qh->sched_frame; ++ } ++ + DWC_LIST_MOVE_HEAD + (&hcd->periodic_sched_inactive, + &qh->qh_list_entry); @@ -49622,6 +60459,7 @@ index 0000000..0a1cbb7 + * QH to place the QTD into. If it does not find a QH, then it will create a + * new QH. If the QH to which the QTD is added is not currently scheduled, it + * is placed into the proper schedule based on its EP type. ++ * HCD lock must be held and interrupts must be disabled on entry + * + * @param[in] qtd The QTD to add + * @param[in] hcd The DWC HCD structure @@ -49634,8 +60472,6 @@ index 0000000..0a1cbb7 + dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc) +{ + int retval = 0; -+ dwc_irqflags_t flags; -+ + dwc_otg_hcd_urb_t *urb = qtd->urb; + + /* @@ -49645,18 +60481,19 @@ index 0000000..0a1cbb7 + if (*qh == NULL) { + *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); + if (*qh == NULL) { -+ retval = -1; ++ retval = -DWC_E_NO_MEMORY; + goto done; ++ } else { ++ if (fiq_enable) ++ hcd->fiq_state->kick_np_queues = 1; + } + } -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); + retval = dwc_otg_hcd_qh_add(hcd, *qh); + if (retval == 0) { + DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, + qtd_list_entry); ++ qtd->qh = *qh; + } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ +done: + + return retval; @@ -49665,10 +60502,10 @@ index 0000000..0a1cbb7 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h new file mode 100644 -index 0000000..e46d9bb +index 0000000..6b2c7d0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -@@ -0,0 +1,185 @@ +@@ -0,0 +1,188 @@ +#ifndef _DWC_OS_DEP_H_ +#define _DWC_OS_DEP_H_ + @@ -49768,6 +60605,9 @@ index 0000000..e46d9bb + /** Register offset for Diagnostic API */ + uint32_t reg_offset; + ++ /** Base address for MPHI peripheral */ ++ void *mphi_base; ++ +#ifdef LM_INTERFACE + struct lm_device *lmdev; +#elif defined(PCI_INTERFACE) @@ -53212,7 +64052,7 @@ index 0000000..c8d2e0e +#endif /* DWC_HOST_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c new file mode 100644 -index 0000000..1b1f83c +index 0000000..c8590b5 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c @@ -0,0 +1,5147 @@ @@ -57494,7 +68334,7 @@ index 0000000..1b1f83c + && (pcd->ep0state == EP0_OUT_DATA_PHASE)) + status.d32 = core_if->dev_if->out_desc_addr->status.d32; + if (pcd->ep0state == EP0_OUT_STATUS_PHASE) -+ status.d32 = status.d32 = core_if->dev_if-> ++ status.d32 = core_if->dev_if-> + out_desc_addr->status.d32; + + if (status.b.sr) { @@ -58365,10 +69205,10 @@ index 0000000..1b1f83c +#endif /* DWC_HOST_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c new file mode 100644 -index 0000000..5d310df +index 0000000..4b32941 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -@@ -0,0 +1,1358 @@ +@@ -0,0 +1,1360 @@ + /* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $ + * $Revision: #21 $ @@ -58430,6 +69270,8 @@ index 0000000..5d310df +#include "dwc_otg_driver.h" +#include "dwc_otg_dbg.h" + ++extern bool fiq_enable; ++ +static struct gadget_wrapper { + dwc_otg_pcd_t *pcd; + @@ -59593,13 +70435,13 @@ index 0000000..5d310df + */ +#ifdef PLATFORM_INTERFACE + DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", -+ platform_get_irq(_dev, 0)); -+ retval = request_irq(platform_get_irq(_dev, 0), dwc_otg_pcd_irq, ++ platform_get_irq(_dev, fiq_enable ? 0 : 1)); ++ retval = request_irq(platform_get_irq(_dev, fiq_enable ? 0 : 1), dwc_otg_pcd_irq, + IRQF_SHARED, gadget_wrapper->gadget.name, + otg_dev->pcd); + if (retval != 0) { + DWC_ERROR("request of irq%d failed\n", -+ platform_get_irq(_dev, 0)); ++ platform_get_irq(_dev, fiq_enable ? 0 : 1)); + free_wrapper(gadget_wrapper); + return -EBUSY; + } @@ -62987,10 +73829,10 @@ index 0000000..cdc9963 +test_main(); +0; -From 21af740ba39591ca0b84bf217e5e17748c912ac8 Mon Sep 17 00:00:00 2001 +From d88e00c9d4c9ea9ff9c3d65ac79afe580c083d14 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:54:32 +0100 -Subject: [PATCH 005/172] bcm2708 watchdog driver +Subject: [PATCH 005/104] bcm2708 watchdog driver Signed-off-by: popcornmix --- @@ -63001,7 +73843,7 @@ Signed-off-by: popcornmix create mode 100644 drivers/watchdog/bcm2708_wdog.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig -index 08f41ad..3bc50f2 100644 +index 16f2023..ca7d6463 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -452,6 +452,12 @@ config RETU_WATCHDOG @@ -63010,7 +73852,7 @@ index 08f41ad..3bc50f2 100644 +config BCM2708_WDT + tristate "BCM2708 Watchdog" -+ depends on ARCH_BCM2708 ++ depends on ARCH_BCM2708 || ARCH_BCM2709 + help + Enables BCM2708 watchdog support. + @@ -63018,7 +73860,7 @@ index 08f41ad..3bc50f2 100644 tristate "MOXART watchdog" depends on ARCH_MOXART diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile -index c569ec8..10e0665 100644 +index 5c19294..aac60bd 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o @@ -63418,318 +74260,10 @@ index 0000000..8a27d68 +MODULE_ALIAS_MISCDEV(TEMP_MINOR); +MODULE_LICENSE("GPL"); -From 44deeada252c455ee3325a19e375016d60826802 Mon Sep 17 00:00:00 2001 -From: Harm Hanemaaijer -Date: Thu, 20 Jun 2013 20:21:39 +0200 -Subject: [PATCH 008/172] Speed up console framebuffer imageblit function - -Especially on platforms with a slower CPU but a relatively high -framebuffer fill bandwidth, like current ARM devices, the existing -console monochrome imageblit function used to draw console text is -suboptimal for common pixel depths such as 16bpp and 32bpp. The existing -code is quite general and can deal with several pixel depths. By creating -special case functions for 16bpp and 32bpp, by far the most common pixel -formats used on modern systems, a significant speed-up is attained -which can be readily felt on ARM-based devices like the Raspberry Pi -and the Allwinner platform, but should help any platform using the -fb layer. - -The special case functions allow constant folding, eliminating a number -of instructions including divide operations, and allow the use of an -unrolled loop, eliminating instructions with a variable shift size, -reducing source memory access instructions, and eliminating excessive -branching. These unrolled loops also allow much better code optimization -by the C compiler. The code that selects which optimized variant is used -is also simplified, eliminating integer divide instructions. - -The speed-up, measured by timing 'cat file.txt' in the console, varies -between 40% and 70%, when testing on the Raspberry Pi and Allwinner -ARM-based platforms, depending on font size and the pixel depth, with -the greater benefit for 32bpp. - -Signed-off-by: Harm Hanemaaijer ---- - drivers/video/fbdev/core/cfbimgblt.c | 152 +++++++++++++++++++++++++++++++++-- - 1 file changed, 147 insertions(+), 5 deletions(-) - -diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c -index a2bb276..436494f 100644 ---- a/drivers/video/fbdev/core/cfbimgblt.c -+++ b/drivers/video/fbdev/core/cfbimgblt.c -@@ -28,6 +28,11 @@ - * - * Also need to add code to deal with cards endians that are different than - * the native cpu endians. I also need to deal with MSB position in the word. -+ * Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013: -+ * - Provide optimized versions of fast_imageblit for 16 and 32bpp that are -+ * significantly faster than the previous implementation. -+ * - Simplify the fast/slow_imageblit selection code, avoiding integer -+ * divides. - */ - #include - #include -@@ -262,6 +267,133 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * - } - } - -+/* -+ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded -+ * into the code, main loop unrolled. -+ */ -+ -+static inline void fast_imageblit16(const struct fb_image *image, -+ struct fb_info *p, u8 __iomem * dst1, -+ u32 fgcolor, u32 bgcolor) -+{ -+ u32 fgx = fgcolor, bgx = bgcolor; -+ u32 spitch = (image->width + 7) / 8; -+ u32 end_mask, eorx; -+ const char *s = image->data, *src; -+ u32 __iomem *dst; -+ const u32 *tab = NULL; -+ int i, j, k; -+ -+ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; -+ -+ fgx <<= 16; -+ bgx <<= 16; -+ fgx |= fgcolor; -+ bgx |= bgcolor; -+ -+ eorx = fgx ^ bgx; -+ k = image->width / 2; -+ -+ for (i = image->height; i--;) { -+ dst = (u32 __iomem *) dst1; -+ src = s; -+ -+ j = k; -+ while (j >= 4) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 6) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 4) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 2) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[bits & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ src++; -+ j -= 4; -+ } -+ if (j != 0) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 6) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ if (j >= 2) { -+ end_mask = tab[(bits >> 4) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ if (j == 3) { -+ end_mask = tab[(bits >> 2) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst); -+ } -+ } -+ } -+ dst1 += p->fix.line_length; -+ s += spitch; -+ } -+} -+ -+/* -+ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded -+ * into the code, main loop unrolled. -+ */ -+ -+static inline void fast_imageblit32(const struct fb_image *image, -+ struct fb_info *p, u8 __iomem * dst1, -+ u32 fgcolor, u32 bgcolor) -+{ -+ u32 fgx = fgcolor, bgx = bgcolor; -+ u32 spitch = (image->width + 7) / 8; -+ u32 end_mask, eorx; -+ const char *s = image->data, *src; -+ u32 __iomem *dst; -+ const u32 *tab = NULL; -+ int i, j, k; -+ -+ tab = cfb_tab32; -+ -+ eorx = fgx ^ bgx; -+ k = image->width; -+ -+ for (i = image->height; i--;) { -+ dst = (u32 __iomem *) dst1; -+ src = s; -+ -+ j = k; -+ while (j >= 8) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 6) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 5) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 4) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 3) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 2) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 1) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[bits & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ src++; -+ j -= 8; -+ } -+ if (j != 0) { -+ u32 bits = (u32) * src; -+ while (j > 1) { -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ bits <<= 1; -+ j--; -+ } -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst); -+ } -+ dst1 += p->fix.line_length; -+ s += spitch; -+ } -+} -+ - void cfb_imageblit(struct fb_info *p, const struct fb_image *image) - { - u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; -@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) - bgcolor = image->bg_color; - } - -- if (32 % bpp == 0 && !start_index && !pitch_index && -- ((width & (32/bpp-1)) == 0) && -- bpp >= 8 && bpp <= 32) -- fast_imageblit(image, p, dst1, fgcolor, bgcolor); -- else -+ if (!start_index && !pitch_index) { -+ if (bpp == 32) -+ fast_imageblit32(image, p, dst1, fgcolor, -+ bgcolor); -+ else if (bpp == 16 && (width & 1) == 0) -+ fast_imageblit16(image, p, dst1, fgcolor, -+ bgcolor); -+ else if (bpp == 8 && (width & 3) == 0) -+ fast_imageblit(image, p, dst1, fgcolor, -+ bgcolor); -+ else -+ slow_imageblit(image, p, dst1, fgcolor, -+ bgcolor, -+ start_index, pitch_index); -+ } else - slow_imageblit(image, p, dst1, fgcolor, bgcolor, - start_index, pitch_index); - } else - -From 33bcff1a531c7d7810dfd4f81546ddc3bffadeba Mon Sep 17 00:00:00 2001 -From: Siarhei Siamashka -Date: Mon, 17 Jun 2013 13:32:11 +0300 -Subject: [PATCH 009/172] fbdev: add FBIOCOPYAREA ioctl - -Based on the patch authored by Ali Gholami Rudi at - https://lkml.org/lkml/2009/7/13/153 - -Provide an ioctl for userspace applications, but only if this operation -is hardware accelerated (otherwide it does not make any sense). - -Signed-off-by: Siarhei Siamashka ---- - drivers/video/fbdev/core/fbmem.c | 30 ++++++++++++++++++++++++++++++ - include/uapi/linux/fb.h | 5 +++++ - 2 files changed, 35 insertions(+) - -diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c -index 0705d88..771992a 100644 ---- a/drivers/video/fbdev/core/fbmem.c -+++ b/drivers/video/fbdev/core/fbmem.c -@@ -1084,6 +1084,25 @@ fb_blank(struct fb_info *info, int blank) - } - EXPORT_SYMBOL(fb_blank); - -+static int fb_copyarea_user(struct fb_info *info, -+ struct fb_copyarea *copy) -+{ -+ int ret = 0; -+ if (!lock_fb_info(info)) -+ return -ENODEV; -+ if (copy->dx + copy->width > info->var.xres || -+ copy->sx + copy->width > info->var.xres || -+ copy->dy + copy->height > info->var.yres || -+ copy->sy + copy->height > info->var.yres) { -+ ret = -EINVAL; -+ goto out; -+ } -+ info->fbops->fb_copyarea(info, copy); -+out: -+ unlock_fb_info(info); -+ return ret; -+} -+ - static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) - { -@@ -1094,6 +1113,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - struct fb_event event; -+ struct fb_copyarea copy; - void __user *argp = (void __user *)arg; - long ret = 0; - -@@ -1211,6 +1231,15 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unlock_fb_info(info); - console_unlock(); - break; -+ case FBIOCOPYAREA: -+ if (info->flags & FBINFO_HWACCEL_COPYAREA) { -+ /* only provide this ioctl if it is accelerated */ -+ if (copy_from_user(©, argp, sizeof(copy))) -+ return -EFAULT; -+ ret = fb_copyarea_user(info, ©); -+ break; -+ } -+ /* fall through */ - default: - if (!lock_fb_info(info)) - return -ENODEV; -@@ -1365,6 +1394,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, - case FBIOPAN_DISPLAY: - case FBIOGET_CON2FBMAP: - case FBIOPUT_CON2FBMAP: -+ case FBIOCOPYAREA: - arg = (unsigned long) compat_ptr(arg); - case FBIOBLANK: - ret = do_fb_ioctl(info, cmd, arg); -diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h -index fb795c3..fa72af0 100644 ---- a/include/uapi/linux/fb.h -+++ b/include/uapi/linux/fb.h -@@ -34,6 +34,11 @@ - #define FBIOPUT_MODEINFO 0x4617 - #define FBIOGET_DISPINFO 0x4618 - #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) -+/* -+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might -+ * be concurrently added to the mainline kernel -+ */ -+#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea) - - #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ - #define FB_TYPE_PLANES 1 /* Non interleaved planes */ - -From 86c75685b2acfe9012480bb2301a818d3d0beea8 Mon Sep 17 00:00:00 2001 +From 5fd2663844abf7f14ef6ac8abbb26e117eb9e24f Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:55:09 +0100 -Subject: [PATCH 010/172] bcm2708 framebuffer driver +Subject: [PATCH 006/104] bcm2708 framebuffer driver Signed-off-by: popcornmix @@ -63773,6 +74307,8 @@ an interrupt instead for larger transfers. For small transfers, busy-waiting is still likely to be faster. Signed-off-by: Luke Diamand + +bcm2708: Make ioctl logging quieter --- arch/arm/mach-bcm2708/dma.c | 8 + arch/arm/mach-bcm2708/include/mach/dma.h | 2 + @@ -63821,7 +74357,7 @@ index f2568d4..a4aac4c 100644 /* When listing features we can ask for when allocating DMA channels give diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig -index 4916c97..95fcb7f 100644 +index b3dd417..b3768b4 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -224,6 +224,20 @@ config FB_TILEBLITTING @@ -63859,7 +74395,7 @@ index 1979aff..57181ad 100644 obj-$(CONFIG_FB_CLPS711X) += clps711x-fb.o diff --git a/drivers/video/fbdev/bcm2708_fb.c b/drivers/video/fbdev/bcm2708_fb.c new file mode 100644 -index 0000000..dcc4de2 +index 0000000..f632750 --- /dev/null +++ b/drivers/video/fbdev/bcm2708_fb.c @@ -0,0 +1,818 @@ @@ -64289,10 +74825,10 @@ index 0000000..dcc4de2 + + bcm_mailbox_property(&p, p[0]); + -+ pr_info("bcm2708_fb_ioctl %x,%lx returns=%d p[1]=0x%x\n", cmd, arg, p[5], p[1]); -+ + if ( p[1] == VCMSG_REQUEST_SUCCESSFUL ) + result = 0; ++ else ++ pr_err("bcm2708_fb_ioctl %x,%lx returns=%d p[1]=0x%x\n", cmd, arg, p[5], p[1]); + } + return result; +} @@ -67172,13139 +77708,2894 @@ index 3c14e43..7626beb6a 100644 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 -From 3f8261010097c731e1128600efb3107dc83aa24f Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 2 Jul 2013 23:42:01 +0100 -Subject: [PATCH 011/172] bcm2708 vchiq driver +From d4c6296fcab17d13cd97d83c8f53bc884d7081d6 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:22:53 +0100 +Subject: [PATCH 007/104] dmaengine: Add support for BCM2708 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -Signed-off-by: popcornmix +Add support for DMA controller of BCM2708 as used in the Raspberry Pi. +Currently it only supports cyclic DMA. -vchiq: create_pagelist copes with vmalloc memory +Signed-off-by: Florian Meier -Signed-off-by: Daniel Stone +dmaengine: expand functionality by supporting scatter/gather transfers sdhci-bcm2708 and dma.c: fix for LITE channels -vchiq: fix the shim message release +DMA: fix cyclic LITE length overflow bug -Signed-off-by: Daniel Stone +dmaengine: bcm2708: Remove chancnt affectations -vchiq: export additional symbols +Mirror bcm2835-dma.c commit 9eba5536a7434c69d8c185d4bd1c70734d92287d: +chancnt is already filled by dma_async_device_register, which uses the channel +list to know how much channels there is. -Signed-off-by: Daniel Stone +Since it's already filled, we can safely remove it from the drivers' probe +function. -VCHIQ: Make service closure fully synchronous (drv) +Signed-off-by: Noralf Trønnes -This is one half of a two-part patch, the other half of which is to -the vchiq_lib user library. With these patches, calls to -vchiq_close_service and vchiq_remove_service won't return until any -associated callbacks have been delivered to the callback thread. +dmaengine: bcm2708: overwrite dreq only if it is not set -VCHIQ: Add per-service tracing +dreq is set when the DMA channel is fetched from Device Tree. +slave_id is set using dmaengine_slave_config(). +Only overwrite dreq with slave_id if it is not set. -The new service option VCHIQ_SERVICE_OPTION_TRACE is a boolean that -toggles tracing for the specified service. +dreq/slave_id in the cyclic DMA case is not touched, because I don't +have hardware to test with. -This commit also introduces vchi_service_set_option and the associated -option VCHI_SERVICE_OPTION_TRACE. +Signed-off-by: Noralf Trønnes -vchiq: Make the synchronous-CLOSE logic more tolerant +dmaengine: bcm2708: do device registration in the board file -vchiq: Move logging control into debugfs +Don't register the device in the driver. Do it in the board file. -vchiq: Take care of a corner case tickled by VCSM +Signed-off-by: Noralf Trønnes -Closing a connection that isn't fully open requires care, since one -side does not know the other side's port number. Code was present to -handle the case where a CLOSE is sent immediately after an OPEN, i.e. -before the OPENACK has been received, but this was incorrectly being -used when an OPEN from a client using port 0 was rejected. +dmaengine: bcm2708: don't restrict DT support to ARCH_BCM2835 -(In the observed failure, the host was attempting to use the VCSM -service, which isn't present in the 'cutdown' firmware. The failure -was intermittent because sometimes the keepalive service would -grab port 0.) +Both ARCH_BCM2835 and ARCH_BCM270x are built with OF now. +Add Device Tree support to the non ARCH_BCM2835 case. +Use the same driver name regardless of architecture. -This case can be distinguished because the client's remoteport will -still be VCHIQ_PORT_FREE, and the srvstate will be OPENING. Either -condition is sufficient to differentiate it from the special case -described above. +Signed-off-by: Noralf Trønnes --- - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/vc04_services/Kconfig | 9 + - drivers/misc/vc04_services/Makefile | 17 + - .../interface/vchi/connections/connection.h | 328 ++ - .../interface/vchi/message_drivers/message.h | 204 ++ - drivers/misc/vc04_services/interface/vchi/vchi.h | 378 ++ - .../misc/vc04_services/interface/vchi/vchi_cfg.h | 224 ++ - .../interface/vchi/vchi_cfg_internal.h | 71 + - .../vc04_services/interface/vchi/vchi_common.h | 174 + - .../misc/vc04_services/interface/vchi/vchi_mh.h | 42 + - .../misc/vc04_services/interface/vchiq_arm/vchiq.h | 40 + - .../vc04_services/interface/vchiq_arm/vchiq_2835.h | 42 + - .../interface/vchiq_arm/vchiq_2835_arm.c | 561 +++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2883 +++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 223 ++ - .../interface/vchiq_arm/vchiq_build_info.h | 37 + - .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 66 + - .../interface/vchiq_arm/vchiq_connected.c | 119 + - .../interface/vchiq_arm/vchiq_connected.h | 50 + - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 3861 ++++++++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_core.h | 711 ++++ - .../interface/vchiq_arm/vchiq_debugfs.c | 383 ++ - .../interface/vchiq_arm/vchiq_debugfs.h | 52 + - .../interface/vchiq_arm/vchiq_genversion | 87 + - .../vc04_services/interface/vchiq_arm/vchiq_if.h | 189 + - .../interface/vchiq_arm/vchiq_ioctl.h | 131 + - .../interface/vchiq_arm/vchiq_kern_lib.c | 456 +++ - .../interface/vchiq_arm/vchiq_memdrv.h | 71 + - .../interface/vchiq_arm/vchiq_pagelist.h | 58 + - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 853 +++++ - .../vc04_services/interface/vchiq_arm/vchiq_util.c | 151 + - .../vc04_services/interface/vchiq_arm/vchiq_util.h | 81 + - .../interface/vchiq_arm/vchiq_version.c | 59 + - 34 files changed, 12613 insertions(+) - create mode 100644 drivers/misc/vc04_services/Kconfig - create mode 100644 drivers/misc/vc04_services/Makefile - create mode 100644 drivers/misc/vc04_services/interface/vchi/connections/connection.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/message_drivers/message.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_common.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c + arch/arm/mach-bcm2708/bcm2708.c | 6 + + arch/arm/mach-bcm2708/dma.c | 2 + + arch/arm/mach-bcm2708/include/mach/dma.h | 6 +- + arch/arm/mach-bcm2709/bcm2709.c | 6 + + drivers/dma/Kconfig | 6 + + drivers/dma/Makefile | 1 + + drivers/dma/bcm2708-dmaengine.c | 989 +++++++++++++++++++++++++++++++ + 7 files changed, 1015 insertions(+), 1 deletion(-) + create mode 100644 drivers/dma/bcm2708-dmaengine.c -diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index 006242c..4e6f46c3 100644 ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -524,6 +524,7 @@ source "drivers/misc/carma/Kconfig" - source "drivers/misc/altera-stapl/Kconfig" - source "drivers/misc/mei/Kconfig" - source "drivers/misc/vmw_vmci/Kconfig" -+source "drivers/misc/vc04_services/Kconfig" - source "drivers/misc/mic/Kconfig" - source "drivers/misc/genwqe/Kconfig" - source "drivers/misc/echo/Kconfig" -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index 7d5c4cd..c085ede 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -51,6 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ - obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ - obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o - obj-$(CONFIG_SRAM) += sram.o -+obj-y += vc04_services/ - obj-y += mic/ - obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ -diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig -new file mode 100644 -index 0000000..2663933 ---- /dev/null -+++ b/drivers/misc/vc04_services/Kconfig -@@ -0,0 +1,9 @@ -+config BCM2708_VCHIQ -+ tristate "Videocore VCHIQ" -+ depends on MACH_BCM2708 -+ default y -+ help -+ Kernel to VideoCore communication interface for the -+ BCM2708 family of products. -+ Defaults to Y when the Broadcom Videocore services -+ are included in the build, N otherwise. -diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile -new file mode 100644 -index 0000000..0c82520 ---- /dev/null -+++ b/drivers/misc/vc04_services/Makefile -@@ -0,0 +1,17 @@ -+ifeq ($(CONFIG_MACH_BCM2708),y) -+ -+obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o -+ -+vchiq-objs := \ -+ interface/vchiq_arm/vchiq_core.o \ -+ interface/vchiq_arm/vchiq_arm.o \ -+ interface/vchiq_arm/vchiq_kern_lib.o \ -+ interface/vchiq_arm/vchiq_2835_arm.o \ -+ interface/vchiq_arm/vchiq_debugfs.o \ -+ interface/vchiq_arm/vchiq_shim.o \ -+ interface/vchiq_arm/vchiq_util.o \ -+ interface/vchiq_arm/vchiq_connected.o \ -+ -+ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -+ -+endif -diff --git a/drivers/misc/vc04_services/interface/vchi/connections/connection.h b/drivers/misc/vc04_services/interface/vchi/connections/connection.h -new file mode 100644 -index 0000000..fef6ac3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/connections/connection.h -@@ -0,0 +1,328 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef CONNECTION_H_ -+#define CONNECTION_H_ -+ -+#include -+#include -+#include -+ -+#include "interface/vchi/vchi_cfg_internal.h" -+#include "interface/vchi/vchi_common.h" -+#include "interface/vchi/message_drivers/message.h" -+ -+/****************************************************************************** -+ Global defs -+ *****************************************************************************/ -+ -+// Opaque handle for a connection / service pair -+typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; -+ -+// opaque handle to the connection state information -+typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; -+ -+typedef struct vchi_connection_t VCHI_CONNECTION_T; -+ -+ -+/****************************************************************************** -+ API -+ *****************************************************************************/ -+ -+// Routine to init a connection with a particular low level driver -+typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, -+ const VCHI_MESSAGE_DRIVER_T * driver ); -+ -+// Routine to control CRC enabling at a connection level -+typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, -+ VCHI_CRC_CONTROL_T control ); -+ -+// Routine to create a service -+typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, -+ int32_t service_id, -+ uint32_t rx_fifo_size, -+ uint32_t tx_fifo_size, -+ int server, -+ VCHI_CALLBACK_T callback, -+ void *callback_param, -+ int32_t want_crc, -+ int32_t want_unaligned_bulk_rx, -+ int32_t want_unaligned_bulk_tx, -+ VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); -+ -+// Routine to close a service -+typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); -+ -+// Routine to queue a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// scatter-gather (vector) message queueing -+typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// Routine to dequeue a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to peek at a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to hold a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags, -+ void **message_handle ); -+ -+// Routine to initialise a received message iterator -+typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ VCHI_MSG_ITER_T *iter, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to release a held message -+typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *message_handle ); -+ -+// Routine to get info on a held message -+typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *message_handle, -+ void **data, -+ int32_t *msg_size, -+ uint32_t *tx_timestamp, -+ uint32_t *rx_timestamp ); -+ -+// Routine to check whether the iterator has a next message -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ const VCHI_MSG_ITER_T *iter ); -+ -+// Routine to advance the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter, -+ void **data, -+ uint32_t *msg_size ); -+ -+// Routine to remove the last message returned by the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter ); -+ -+// Routine to hold the last message returned by the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter, -+ void **msg_handle ); -+ -+// Routine to transmit bulk data -+typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle ); -+ -+// Routine to receive data -+typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle ); -+ -+// Routine to report if a server is available -+typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t peer_flags ); -+ -+// Routine to report the number of RX slots available -+typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); -+ -+// Routine to report the RX slot size -+typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); -+ -+// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO -+typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, -+ int32_t service, -+ uint32_t length, -+ MESSAGE_TX_CHANNEL_T channel, -+ uint32_t channel_params, -+ uint32_t data_length, -+ uint32_t data_offset); -+ -+// Callback to inform a service that a Xon or Xoff message has been received -+typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t xoff); -+ -+// Callback to inform a service that a server available reply message has been received -+typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, uint32_t flags); -+ -+// Callback to indicate that bulk auxiliary messages have arrived -+typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); -+ -+// Callback to indicate that bulk auxiliary messages have arrived -+typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); -+ -+// Callback with all the connection info you require -+typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); -+ -+// Callback to inform of a disconnect -+typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); -+ -+// Callback to inform of a power control request -+typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, int32_t enable); -+ -+// allocate memory suitably aligned for this connection -+typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); -+ -+// free memory allocated by buffer_allocate -+typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); -+ -+ -+/****************************************************************************** -+ System driver struct -+ *****************************************************************************/ -+ -+struct opaque_vchi_connection_api_t -+{ -+ // Routine to init the connection -+ VCHI_CONNECTION_INIT_T init; -+ -+ // Connection-level CRC control -+ VCHI_CONNECTION_CRC_CONTROL_T crc_control; -+ -+ // Routine to connect to or create service -+ VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; -+ -+ // Routine to disconnect from a service -+ VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; -+ -+ // Routine to queue a message -+ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; -+ -+ // scatter-gather (vector) message queue -+ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; -+ -+ // Routine to dequeue a message -+ VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; -+ -+ // Routine to peek at a message -+ VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; -+ -+ // Routine to hold a message -+ VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; -+ -+ // Routine to initialise a received message iterator -+ VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; -+ -+ // Routine to release a message -+ VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; -+ -+ // Routine to get information on a held message -+ VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; -+ -+ // Routine to check for next message on iterator -+ VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; -+ -+ // Routine to get next message on iterator -+ VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; -+ -+ // Routine to remove the last message returned by iterator -+ VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; -+ -+ // Routine to hold the last message returned by iterator -+ VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; -+ -+ // Routine to transmit bulk data -+ VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; -+ -+ // Routine to receive data -+ VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; -+ -+ // Routine to report the available servers -+ VCHI_CONNECTION_SERVER_PRESENT server_present; -+ -+ // Routine to report the number of RX slots available -+ VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; -+ -+ // Routine to report the RX slot size -+ VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; -+ -+ // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO -+ VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; -+ -+ // Callback to inform a service that a Xon or Xoff message has been received -+ VCHI_CONNECTION_FLOW_CONTROL flow_control; -+ -+ // Callback to inform a service that a server available reply message has been received -+ VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; -+ -+ // Callback to indicate that bulk auxiliary messages have arrived -+ VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; -+ -+ // Callback to indicate that a bulk auxiliary message has been transmitted -+ VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; -+ -+ // Callback to provide information about the connection -+ VCHI_CONNECTION_INFO connection_info; -+ -+ // Callback to notify that peer has requested disconnect -+ VCHI_CONNECTION_DISCONNECT disconnect; -+ -+ // Callback to notify that peer has requested power change -+ VCHI_CONNECTION_POWER_CONTROL power_control; -+ -+ // allocate memory suitably aligned for this connection -+ VCHI_BUFFER_ALLOCATE buffer_allocate; -+ -+ // free memory allocated by buffer_allocate -+ VCHI_BUFFER_FREE buffer_free; -+ +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index d9db081..5d8657b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -250,6 +250,11 @@ static struct platform_device bcm2708_dmaman_device = { + .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), + }; + ++static struct platform_device bcm2708_dmaengine_device = { ++ .name = "bcm2708-dmaengine", ++ .id = -1, +}; + -+struct vchi_connection_t { -+ const VCHI_CONNECTION_API_T *api; -+ VCHI_CONNECTION_STATE_T *state; -+#ifdef VCHI_COARSE_LOCKING -+ struct semaphore sem; -+#endif + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -508,6 +513,7 @@ void __init bcm2708_init(void) + clkdev_add(&lookups[i]); + + bcm_register_device(&bcm2708_dmaman_device); ++ bcm_register_device(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device(&bcm2708_gpio_device); +diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c +index 1da2413..a5e58d1 100644 +--- a/arch/arm/mach-bcm2708/dma.c ++++ b/arch/arm/mach-bcm2708/dma.c +@@ -156,6 +156,8 @@ static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, + dmaman->chan_available = chans_available; + dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ + dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ + } + + static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, +diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h +index a4aac4c..d03e7b5 100644 +--- a/arch/arm/mach-bcm2708/include/mach/dma.h ++++ b/arch/arm/mach-bcm2708/include/mach/dma.h +@@ -77,9 +77,13 @@ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); + those with higher priority smaller ordinal numbers */ + #define BCM_DMA_FEATURE_FAST_ORD 0 + #define BCM_DMA_FEATURE_BULK_ORD 1 ++#define BCM_DMA_FEATURE_NORMAL_ORD 2 ++#define BCM_DMA_FEATURE_LITE_ORD 3 + #define BCM_DMA_FEATURE_FAST (1< -+#include -+#include -+ -+#include "interface/vchi/vchi_cfg_internal.h" -+#include "interface/vchi/vchi_common.h" -+ -+ -+typedef enum message_event_type { -+ MESSAGE_EVENT_NONE, -+ MESSAGE_EVENT_NOP, -+ MESSAGE_EVENT_MESSAGE, -+ MESSAGE_EVENT_SLOT_COMPLETE, -+ MESSAGE_EVENT_RX_BULK_PAUSED, -+ MESSAGE_EVENT_RX_BULK_COMPLETE, -+ MESSAGE_EVENT_TX_COMPLETE, -+ MESSAGE_EVENT_MSG_DISCARDED -+} MESSAGE_EVENT_TYPE_T; -+ -+typedef enum vchi_msg_flags -+{ -+ VCHI_MSG_FLAGS_NONE = 0x0, -+ VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 -+} VCHI_MSG_FLAGS_T; -+ -+typedef enum message_tx_channel -+{ -+ MESSAGE_TX_CHANNEL_MESSAGE = 0, -+ MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards -+} MESSAGE_TX_CHANNEL_T; -+ -+// Macros used for cycling through bulk channels -+#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) -+#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) -+ -+typedef enum message_rx_channel -+{ -+ MESSAGE_RX_CHANNEL_MESSAGE = 0, -+ MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards -+} MESSAGE_RX_CHANNEL_T; -+ -+// Message receive slot information -+typedef struct rx_msg_slot_info { -+ -+ struct rx_msg_slot_info *next; -+ //struct slot_info *prev; -+#if !defined VCHI_COARSE_LOCKING -+ struct semaphore sem; -+#endif -+ -+ uint8_t *addr; // base address of slot -+ uint32_t len; // length of slot in bytes -+ -+ uint32_t write_ptr; // hardware causes this to advance -+ uint32_t read_ptr; // this module does the reading -+ int active; // is this slot in the hardware dma fifo? -+ uint32_t msgs_parsed; // count how many messages are in this slot -+ uint32_t msgs_released; // how many messages have been released -+ void *state; // connection state information -+ uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services -+} RX_MSG_SLOTINFO_T; -+ -+// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. -+// In particular, it mustn't use addr and len - they're the client buffer, but the message -+// driver will be tasked with sending the aligned core section. -+typedef struct rx_bulk_slotinfo_t { -+ struct rx_bulk_slotinfo_t *next; -+ -+ struct semaphore *blocking; -+ -+ // needed by DMA -+ void *addr; -+ uint32_t len; -+ -+ // needed for the callback -+ void *service; -+ void *handle; -+ VCHI_FLAGS_T flags; -+} RX_BULK_SLOTINFO_T; -+ -+ -+/* ---------------------------------------------------------------------- -+ * each connection driver will have a pool of the following struct. -+ * -+ * the pool will be managed by vchi_qman_* -+ * this means there will be multiple queues (single linked lists) -+ * a given struct message_info will be on exactly one of these queues -+ * at any one time -+ * -------------------------------------------------------------------- */ -+typedef struct rx_message_info { -+ -+ struct message_info *next; -+ //struct message_info *prev; -+ -+ uint8_t *addr; -+ uint32_t len; -+ RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message -+ uint32_t tx_timestamp; -+ uint32_t rx_timestamp; -+ -+} RX_MESSAGE_INFO_T; -+ -+typedef struct { -+ MESSAGE_EVENT_TYPE_T type; -+ -+ struct { -+ // for messages -+ void *addr; // address of message -+ uint16_t slot_delta; // whether this message indicated slot delta -+ uint32_t len; // length of message -+ RX_MSG_SLOTINFO_T *slot; // slot this message is in -+ int32_t service; // service id this message is destined for -+ uint32_t tx_timestamp; // timestamp from the header -+ uint32_t rx_timestamp; // timestamp when we parsed it -+ } message; -+ -+ // FIXME: cleanup slot reporting... -+ RX_MSG_SLOTINFO_T *rx_msg; -+ RX_BULK_SLOTINFO_T *rx_bulk; -+ void *tx_handle; -+ MESSAGE_TX_CHANNEL_T tx_channel; -+ -+} MESSAGE_EVENT_T; -+ -+ -+// callbacks -+typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); -+ -+typedef struct { -+ VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; -+} VCHI_MESSAGE_DRIVER_OPEN_T; -+ -+ -+// handle to this instance of message driver (as returned by ->open) -+typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; -+ -+struct opaque_vchi_message_driver_t { -+ VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); -+ int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, int32_t enable ); -+ int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message -+ int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) -+ int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) -+ void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver -+ int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, int32_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void -+ *address, uint32_t length_avail, uint32_t max_total_length, int32_t pad_to_fill, int32_t allow_partial ); -+ -+ int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); -+ int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); -+ void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); -+ void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); -+ int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); -+ int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); -+ -+ int32_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); -+ void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); -+ void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); -+}; -+ -+ -+#endif // _VCHI_MESSAGE_H_ -+ -+/****************************** End of file ***********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi.h b/drivers/misc/vc04_services/interface/vchi/vchi.h -new file mode 100644 -index 0000000..1b17e98 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi.h -@@ -0,0 +1,378 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_H_ -+#define VCHI_H_ -+ -+#include "interface/vchi/vchi_cfg.h" -+#include "interface/vchi/vchi_common.h" -+#include "interface/vchi/connections/connection.h" -+#include "vchi_mh.h" -+ -+ -+/****************************************************************************** -+ Global defs -+ *****************************************************************************/ -+ -+#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) -+#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) -+#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) -+ -+#ifdef USE_VCHIQ_ARM -+#define VCHI_BULK_ALIGNED(x) 1 -+#else -+#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) -+#endif -+ -+struct vchi_version { -+ uint32_t version; -+ uint32_t version_min; -+}; -+#define VCHI_VERSION(v_) { v_, v_ } -+#define VCHI_VERSION_EX(v_, m_) { v_, m_ } -+ -+typedef enum -+{ -+ VCHI_VEC_POINTER, -+ VCHI_VEC_HANDLE, -+ VCHI_VEC_LIST -+} VCHI_MSG_VECTOR_TYPE_T; -+ -+typedef struct vchi_msg_vector_ex { -+ -+ VCHI_MSG_VECTOR_TYPE_T type; -+ union -+ { -+ // a memory handle -+ struct -+ { -+ VCHI_MEM_HANDLE_T handle; -+ uint32_t offset; -+ int32_t vec_len; -+ } handle; -+ -+ // an ordinary data pointer -+ struct -+ { -+ const void *vec_base; -+ int32_t vec_len; -+ } ptr; -+ -+ // a nested vector list -+ struct -+ { -+ struct vchi_msg_vector_ex *vec; -+ uint32_t vec_len; -+ } list; -+ } u; -+} VCHI_MSG_VECTOR_EX_T; -+ -+ -+// Construct an entry in a msg vector for a pointer (p) of length (l) -+#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } -+ -+// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) -+#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } -+ -+// Macros to manipulate 'FOURCC' values -+#define MAKE_FOURCC(x) ((int32_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) -+#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF -+ -+ -+// Opaque service information -+struct opaque_vchi_service_t; -+ -+// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, -+// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. -+typedef struct -+{ -+ struct opaque_vchi_service_t *service; -+ void *message; -+} VCHI_HELD_MSG_T; -+ -+ -+ -+// structure used to provide the information needed to open a server or a client -+typedef struct { -+ struct vchi_version version; -+ int32_t service_id; -+ VCHI_CONNECTION_T *connection; -+ uint32_t rx_fifo_size; -+ uint32_t tx_fifo_size; -+ VCHI_CALLBACK_T callback; -+ void *callback_param; -+ /* client intends to receive bulk transfers of -+ odd lengths or into unaligned buffers */ -+ int32_t want_unaligned_bulk_rx; -+ /* client intends to transmit bulk transfers of -+ odd lengths or out of unaligned buffers */ -+ int32_t want_unaligned_bulk_tx; -+ /* client wants to check CRCs on (bulk) xfers. -+ Only needs to be set at 1 end - will do both directions. */ -+ int32_t want_crc; -+} SERVICE_CREATION_T; -+ -+// Opaque handle for a VCHI instance -+typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; -+ -+// Opaque handle for a server or client -+typedef struct opaque_vchi_service_handle_t *VCHI_SERVICE_HANDLE_T; -+ -+// Service registration & startup -+typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); -+ -+typedef struct service_info_tag { -+ const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ -+ VCHI_SERVICE_INIT init; /* Service initialisation function */ -+ void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ -+} SERVICE_INFO_T; -+ -+/****************************************************************************** -+ Global funcs - implementation is specific to which side you are on (local / remote) -+ *****************************************************************************/ -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, -+ const VCHI_MESSAGE_DRIVER_T * low_level); -+ -+ -+// Routine used to initialise the vchi on both local + remote connections -+extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); -+ -+extern int32_t vchi_exit( void ); -+ -+extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, -+ const uint32_t num_connections, -+ VCHI_INSTANCE_T instance_handle ); -+ -+//When this is called, ensure that all services have no data pending. -+//Bulk transfers can remain 'queued' -+extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); -+ -+// Global control over bulk CRC checking -+extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, -+ VCHI_CRC_CONTROL_T control ); -+ -+// helper functions -+extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); -+extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); -+extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); -+ -+ -+/****************************************************************************** -+ Global service API -+ *****************************************************************************/ -+// Routine to create a named service -+extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle ); -+ -+// Routine to destory a service -+extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to open a named service -+extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle); -+ -+extern int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, -+ short *peer_version ); -+ -+// Routine to close a named service -+extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to increment ref count on a named service -+extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to decrement ref count on a named service -+extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to set a control option for a named service -+extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_SERVICE_OPTION_T option, -+ int value); -+ -+// Routine to send a message across a service -+extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// scatter-gather (vector) and send message -+int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_EX_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// legacy scatter-gather (vector) and send message, only handles pointers -+int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// Routine to receive a msg from a service -+// Dequeue is equivalent to hold, copy into client buffer, release -+extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to look at a message in place. -+// The message is not dequeued, so a subsequent call to peek or dequeue -+// will return the same message. -+extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to remove a message after it has been read in place with peek -+// The first message on the queue is dequeued. -+extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to look at a message in place. -+// The message is dequeued, so the caller is left holding it; the descriptor is -+// filled in and must be released when the user has finished with the message. -+extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, -+ void **data, // } may be NULL, as info can be -+ uint32_t *msg_size, // } obtained from HELD_MSG_T -+ VCHI_FLAGS_T flags, -+ VCHI_HELD_MSG_T *message_descriptor ); -+ -+// Initialise an iterator to look through messages in place -+extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_ITER_T *iter, -+ VCHI_FLAGS_T flags ); -+ -+/****************************************************************************** -+ Global service support API - operations on held messages and message iterators -+ *****************************************************************************/ -+ -+// Routine to get the address of a held message -+extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the size of a held message -+extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the transmit timestamp as written into the header by the peer -+extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the reception timestamp, written as we parsed the header -+extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to release a held message after it has been processed -+extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); -+ -+// Indicates whether the iterator has a next message. -+extern int32_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); -+ -+// Return the pointer and length for the next message and advance the iterator. -+extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, -+ void **data, -+ uint32_t *msg_size ); -+ -+// Remove the last message returned by vchi_msg_iter_next. -+// Can only be called once after each call to vchi_msg_iter_next. -+extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); -+ -+// Hold the last message returned by vchi_msg_iter_next. -+// Can only be called once after each call to vchi_msg_iter_next. -+extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, -+ VCHI_HELD_MSG_T *message ); -+ -+// Return information for the next message, and hold it, advancing the iterator. -+extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, -+ void **data, // } may be NULL -+ uint32_t *msg_size, // } -+ VCHI_HELD_MSG_T *message ); -+ -+ -+/****************************************************************************** -+ Global bulk API -+ *****************************************************************************/ -+ -+// Routine to prepare interface for a transfer from the other side -+extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+ -+ -+// Prepare interface for a transfer from the other side into relocatable memory. -+int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T h_dst, -+ uint32_t offset, -+ uint32_t data_size, -+ const VCHI_FLAGS_T flags, -+ void * const bulk_handle ); -+ -+// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) -+extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+ -+ -+/****************************************************************************** -+ Configuration plumbing -+ *****************************************************************************/ -+ -+// function prototypes for the different mid layers (the state info gives the different physical connections) -+extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); -+//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); -+//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); -+ -+// declare all message drivers here -+const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T h_src, -+ uint32_t offset, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+#endif /* VCHI_H_ */ -+ -+/****************************** End of file **********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h -new file mode 100644 -index 0000000..26bc2d3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h -@@ -0,0 +1,224 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_CFG_H_ -+#define VCHI_CFG_H_ -+ -+/**************************************************************************************** -+ * Defines in this first section are part of the VCHI API and may be examined by VCHI -+ * services. -+ ***************************************************************************************/ -+ -+/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ -+/* Really determined by the message driver, and should be available from a run-time call. */ -+#ifndef VCHI_BULK_ALIGN -+# if __VCCOREVER__ >= 0x04000000 -+# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans -+# else -+# define VCHI_BULK_ALIGN 16 -+# endif -+#endif -+ -+/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ -+/* May be less than or greater than VCHI_BULK_ALIGN */ -+/* Really determined by the message driver, and should be available from a run-time call. */ -+#ifndef VCHI_BULK_GRANULARITY -+# if __VCCOREVER__ >= 0x04000000 -+# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans -+# else -+# define VCHI_BULK_GRANULARITY 16 -+# endif -+#endif -+ -+/* The largest possible message to be queued with vchi_msg_queue. */ -+#ifndef VCHI_MAX_MSG_SIZE -+# if defined VCHI_LOCAL_HOST_PORT -+# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? -+# else -+# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! -+# endif -+#endif -+ -+/****************************************************************************************** -+ * Defines below are system configuration options, and should not be used by VCHI services. -+ *****************************************************************************************/ -+ -+/* How many connections can we support? A localhost implementation uses 2 connections, -+ * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW -+ * driver. */ -+#ifndef VCHI_MAX_NUM_CONNECTIONS -+# define VCHI_MAX_NUM_CONNECTIONS 3 -+#endif -+ -+/* How many services can we open per connection? Extending this doesn't cost processing time, just a small -+ * amount of static memory. */ -+#ifndef VCHI_MAX_SERVICES_PER_CONNECTION -+# define VCHI_MAX_SERVICES_PER_CONNECTION 36 -+#endif -+ -+/* Adjust if using a message driver that supports more logical TX channels */ -+#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION -+# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels -+#endif -+ -+/* Adjust if using a message driver that supports more logical RX channels */ -+#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION -+# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI -+#endif -+ -+/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective -+ * receive queue space, less message headers. */ -+#ifndef VCHI_NUM_READ_SLOTS -+# if defined(VCHI_LOCAL_HOST_PORT) -+# define VCHI_NUM_READ_SLOTS 4 -+# else -+# define VCHI_NUM_READ_SLOTS 48 -+# endif -+#endif -+ -+/* Do we utilise overrun facility for receive message slots? Can aid peer transmit -+ * performance. Only define on VideoCore end, talking to host. -+ */ -+//#define VCHI_MSG_RX_OVERRUN -+ -+/* How many transmit slots do we use. Generally don't need many, as the hardware driver -+ * underneath VCHI will usually have its own buffering. */ -+#ifndef VCHI_NUM_WRITE_SLOTS -+# define VCHI_NUM_WRITE_SLOTS 4 -+#endif -+ -+/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, -+ * then it's taking up too much buffer space, and the peer service will be told to stop -+ * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS -+ * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency -+ * is too high. */ -+#ifndef VCHI_XOFF_THRESHOLD -+# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) -+#endif -+ -+/* After we've sent an XOFF, the peer will be told to resume transmission once the local -+ * service has dequeued/released enough messages that it's now occupying -+ * VCHI_XON_THRESHOLD slots or fewer. */ -+#ifndef VCHI_XON_THRESHOLD -+# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) -+#endif -+ -+/* A size below which a bulk transfer omits the handshake completely and always goes -+ * via the message channel, if bulk auxiliary is being sent on that service. (The user -+ * can guarantee this by enabling unaligned transmits). -+ * Not API. */ -+#ifndef VCHI_MIN_BULK_SIZE -+# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) -+#endif -+ -+/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between -+ * speed and latency; the smaller the chunk size the better change of messages and other -+ * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not -+ * break transmissions into chunks. -+ */ -+#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI -+# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) -+#endif -+ -+/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode -+ * with multiple-line frames. Only use if the receiver can cope. */ -+#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 -+# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 -+#endif -+ -+/* How many TX messages can we have pending in our transmit slots. Once exhausted, -+ * vchi_msg_queue will be blocked. */ -+#ifndef VCHI_TX_MSG_QUEUE_SIZE -+# define VCHI_TX_MSG_QUEUE_SIZE 256 -+#endif -+ -+/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing -+ * will be suspended until older messages are dequeued/released. */ -+#ifndef VCHI_RX_MSG_QUEUE_SIZE -+# define VCHI_RX_MSG_QUEUE_SIZE 256 -+#endif -+ -+/* Really should be able to cope if we run out of received message descriptors, by -+ * suspending parsing as the comment above says, but we don't. This sweeps the issue -+ * under the carpet. */ -+#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS -+# undef VCHI_RX_MSG_QUEUE_SIZE -+# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS -+#endif -+ -+/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit -+ * will be blocked. */ -+#ifndef VCHI_TX_BULK_QUEUE_SIZE -+# define VCHI_TX_BULK_QUEUE_SIZE 64 -+#endif -+ -+/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive -+ * will be blocked. */ -+#ifndef VCHI_RX_BULK_QUEUE_SIZE -+# define VCHI_RX_BULK_QUEUE_SIZE 64 -+#endif -+ -+/* A limit on how many outstanding bulk requests we expect the peer to give us. If -+ * the peer asks for more than this, VCHI will fail and assert. The number is determined -+ * by the peer's hardware - it's the number of outstanding requests that can be queued -+ * on all bulk channels. VC3's MPHI peripheral allows 16. */ -+#ifndef VCHI_MAX_PEER_BULK_REQUESTS -+# define VCHI_MAX_PEER_BULK_REQUESTS 32 -+#endif -+ -+/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 -+ * transmitter on and off. -+ */ -+/*#define VCHI_CCP2TX_MANUAL_POWER*/ -+ -+#ifndef VCHI_CCP2TX_MANUAL_POWER -+ -+/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set -+ * negative for no IDLE. -+ */ -+# ifndef VCHI_CCP2TX_IDLE_TIMEOUT -+# define VCHI_CCP2TX_IDLE_TIMEOUT 5 -+# endif -+ -+/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set -+ * negative for no OFF. -+ */ -+# ifndef VCHI_CCP2TX_OFF_TIMEOUT -+# define VCHI_CCP2TX_OFF_TIMEOUT 1000 -+# endif -+ -+#endif /* VCHI_CCP2TX_MANUAL_POWER */ -+ -+#endif /* VCHI_CFG_H_ */ -+ -+/****************************** End of file **********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h -new file mode 100644 -index 0000000..35dcba4 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h -@@ -0,0 +1,71 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_CFG_INTERNAL_H_ -+#define VCHI_CFG_INTERNAL_H_ -+ -+/**************************************************************************************** -+ * Control optimisation attempts. -+ ***************************************************************************************/ -+ -+// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second -+#define VCHI_COARSE_LOCKING -+ -+// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) -+// (only relevant if VCHI_COARSE_LOCKING) -+#define VCHI_ELIDE_BLOCK_EXIT_LOCK -+ -+// Avoid lock on non-blocking peek -+// (only relevant if VCHI_COARSE_LOCKING) -+#define VCHI_AVOID_PEEK_LOCK -+ -+// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. -+#define VCHI_MULTIPLE_HANDLER_THREADS -+ -+// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash -+// our way through the pool of descriptors. -+#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD -+ -+// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. -+#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS -+ -+// Don't use message descriptors for TX messages that don't need them -+#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS -+ -+// Nano-locks for multiqueue -+//#define VCHI_MQUEUE_NANOLOCKS -+ -+// Lock-free(er) dequeuing -+//#define VCHI_RX_NANOLOCKS -+ -+#endif /*VCHI_CFG_INTERNAL_H_*/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -new file mode 100644 -index 0000000..d76118c ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -@@ -0,0 +1,174 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_COMMON_H_ -+#define VCHI_COMMON_H_ -+ -+ -+//flags used when sending messages (must be bitmapped) -+typedef enum -+{ -+ VCHI_FLAGS_NONE = 0x0, -+ VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go -+ VCHI_FLAGS_ALLOW_PARTIAL = 0x8, -+ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, -+ VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, -+ -+ VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only -+ VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only -+ VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only -+ VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only -+ VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only -+ VCHI_FLAGS_INTERNAL = 0xFF0000 -+} VCHI_FLAGS_T; -+ -+// constants for vchi_crc_control() -+typedef enum { -+ VCHI_CRC_NOTHING = -1, -+ VCHI_CRC_PER_SERVICE = 0, -+ VCHI_CRC_EVERYTHING = 1, -+} VCHI_CRC_CONTROL_T; -+ -+//callback reasons when an event occurs on a service -+typedef enum -+{ -+ VCHI_CALLBACK_REASON_MIN, -+ -+ //This indicates that there is data available -+ //handle is the msg id that was transmitted with the data -+ // When a message is received and there was no FULL message available previously, send callback -+ // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails -+ VCHI_CALLBACK_MSG_AVAILABLE, -+ VCHI_CALLBACK_MSG_SENT, -+ VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented -+ -+ // This indicates that a transfer from the other side has completed -+ VCHI_CALLBACK_BULK_RECEIVED, -+ //This indicates that data queued up to be sent has now gone -+ //handle is the msg id that was used when sending the data -+ VCHI_CALLBACK_BULK_SENT, -+ VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented -+ VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented -+ -+ VCHI_CALLBACK_SERVICE_CLOSED, -+ -+ // this side has sent XOFF to peer due to lack of data consumption by service -+ // (suggests the service may need to take some recovery action if it has -+ // been deliberately holding off consuming data) -+ VCHI_CALLBACK_SENT_XOFF, -+ VCHI_CALLBACK_SENT_XON, -+ -+ // indicates that a bulk transfer has finished reading the source buffer -+ VCHI_CALLBACK_BULK_DATA_READ, -+ -+ // power notification events (currently host side only) -+ VCHI_CALLBACK_PEER_OFF, -+ VCHI_CALLBACK_PEER_SUSPENDED, -+ VCHI_CALLBACK_PEER_ON, -+ VCHI_CALLBACK_PEER_RESUMED, -+ VCHI_CALLBACK_FORCED_POWER_OFF, -+ -+#ifdef USE_VCHIQ_ARM -+ // some extra notifications provided by vchiq_arm -+ VCHI_CALLBACK_SERVICE_OPENED, -+ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, -+ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, -+#endif -+ -+ VCHI_CALLBACK_REASON_MAX -+} VCHI_CALLBACK_REASON_T; -+ -+// service control options -+typedef enum -+{ -+ VCHI_SERVICE_OPTION_MIN, -+ -+ VCHI_SERVICE_OPTION_TRACE, -+ -+ VCHI_SERVICE_OPTION_MAX -+} VCHI_SERVICE_OPTION_T; -+ -+ -+//Callback used by all services / bulk transfers -+typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param -+ VCHI_CALLBACK_REASON_T reason, -+ void *handle ); //for transmitting msg's only -+ -+ -+ ++++ b/drivers/dma/bcm2708-dmaengine.c +@@ -0,0 +1,989 @@ +/* -+ * Define vector struct for scatter-gather (vector) operations -+ * Vectors can be nested - if a vector element has negative length, then -+ * the data pointer is treated as pointing to another vector array, with -+ * '-vec_len' elements. Thus to append a header onto an existing vector, -+ * you can do this: ++ * BCM2835 DMA engine support + * -+ * void foo(const VCHI_MSG_VECTOR_T *v, int n) -+ * { -+ * VCHI_MSG_VECTOR_T nv[2]; -+ * nv[0].vec_base = my_header; -+ * nv[0].vec_len = sizeof my_header; -+ * nv[1].vec_base = v; -+ * nv[1].vec_len = -n; -+ * ... ++ * This driver supports cyclic and scatter/gather DMA transfers. + * -+ */ -+typedef struct vchi_msg_vector { -+ const void *vec_base; -+ int32_t vec_len; -+} VCHI_MSG_VECTOR_T; -+ -+// Opaque type for a connection API -+typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; -+ -+// Opaque type for a message driver -+typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; -+ -+ -+// Iterator structure for reading ahead through received message queue. Allocated by client, -+// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. -+// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - -+// will not proceed to messages received since. Behaviour is undefined if an iterator -+// is used again after messages for that service are removed/dequeued by any -+// means other than vchi_msg_iter_... calls on the iterator itself. -+typedef struct { -+ struct opaque_vchi_service_t *service; -+ void *last; -+ void *next; -+ void *remove; -+} VCHI_MSG_ITER_T; -+ -+ -+#endif // VCHI_COMMON_H_ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h -new file mode 100644 -index 0000000..198bd07 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h -@@ -0,0 +1,42 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * Author: Florian Meier ++ * Gellert Weisz ++ * Copyright 2013-2014 + * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. ++ * Based on ++ * OMAP DMAengine support by Russell King + * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. ++ * BCM2708 DMA Driver ++ * Copyright (C) 2010 Broadcom + * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * MARVELL MMP Peripheral DMA Driver ++ * Copyright 2012 Marvell International Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + */ + -+#ifndef VCHI_MH_H_ -+#define VCHI_MH_H_ -+ -+#include -+ -+typedef int32_t VCHI_MEM_HANDLE_T; -+#define VCHI_MEM_HANDLE_INVALID 0 -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h -new file mode 100644 -index 0000000..ad398ba ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h -@@ -0,0 +1,40 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_VCHIQ_H -+#define VCHIQ_VCHIQ_H -+ -+#include "vchiq_if.h" -+#include "vchiq_util.h" -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h -new file mode 100644 -index 0000000..7ea5c64 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h -@@ -0,0 +1,42 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_2835_H -+#define VCHIQ_2835_H -+ -+#include "vchiq_pagelist.h" -+ -+#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0 -+#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 -+ -+#endif /* VCHIQ_2835_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -new file mode 100644 -index 0000000..b3bdaa2 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -@@ -0,0 +1,561 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include ++#include +#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include +#include -+#include -+#include ++#include + -+#include ++#ifndef CONFIG_ARCH_BCM2835 + -+#include -+#include ++/* dma manager */ ++#include + -+#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) ++//#define DMA_COMPLETE DMA_SUCCESS + -+#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 -+#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x)) ++#endif + -+#include "vchiq_arm.h" -+#include "vchiq_2835.h" -+#include "vchiq_connected.h" ++#include ++#include + -+#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) ++#include "virt-dma.h" + -+typedef struct vchiq_2835_state_struct { -+ int inited; -+ VCHIQ_ARM_STATE_T arm_state; -+} VCHIQ_2835_ARM_STATE_T; + -+static char *g_slot_mem; -+static int g_slot_mem_size; -+dma_addr_t g_slot_phys; -+static FRAGMENTS_T *g_fragments_base; -+static FRAGMENTS_T *g_free_fragments; -+struct semaphore g_free_fragments_sema; ++struct bcm2835_dmadev { ++ struct dma_device ddev; ++ spinlock_t lock; ++ void __iomem *base; ++ struct device_dma_parameters dma_parms; ++}; + -+extern int vchiq_arm_log_level; ++struct bcm2835_dma_cb { ++ uint32_t info; ++ uint32_t src; ++ uint32_t dst; ++ uint32_t length; ++ uint32_t stride; ++ uint32_t next; ++ uint32_t pad[2]; ++}; + -+static DEFINE_SEMAPHORE(g_free_fragments_mutex); ++struct bcm2835_chan { ++ struct virt_dma_chan vc; ++ struct list_head node; + -+static irqreturn_t -+vchiq_doorbell_irq(int irq, void *dev_id); ++ struct dma_slave_config cfg; ++ bool cyclic; + -+static int -+create_pagelist(char __user *buf, size_t count, unsigned short type, -+ struct task_struct *task, PAGELIST_T ** ppagelist); ++ int ch; ++ struct bcm2835_desc *desc; + -+static void -+free_pagelist(PAGELIST_T *pagelist, int actual); ++ void __iomem *chan_base; ++ int irq_number; + -+int __init -+vchiq_platform_init(VCHIQ_STATE_T *state) ++ unsigned int dreq; ++}; ++ ++struct bcm2835_desc { ++ struct virt_dma_desc vd; ++ enum dma_transfer_direction dir; ++ ++ unsigned int control_block_size; ++ struct bcm2835_dma_cb *control_block_base; ++ dma_addr_t control_block_base_phys; ++ ++ unsigned int frames; ++ size_t size; ++}; ++ ++#define BCM2835_DMA_CS 0x00 ++#define BCM2835_DMA_ADDR 0x04 ++#define BCM2835_DMA_SOURCE_AD 0x0c ++#define BCM2835_DMA_DEST_AD 0x10 ++#define BCM2835_DMA_NEXTCB 0x1C ++ ++/* DMA CS Control and Status bits */ ++#define BCM2835_DMA_ACTIVE BIT(0) ++#define BCM2835_DMA_INT BIT(2) ++#define BCM2835_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ ++#define BCM2835_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ ++#define BCM2835_DMA_ERR BIT(8) ++#define BCM2835_DMA_ABORT BIT(30) /* Stop current CB, go to next, WO */ ++#define BCM2835_DMA_RESET BIT(31) /* WO, self clearing */ ++ ++#define BCM2835_DMA_INT_EN BIT(0) ++#define BCM2835_DMA_WAIT_RESP BIT(3) ++#define BCM2835_DMA_D_INC BIT(4) ++#define BCM2835_DMA_D_WIDTH BIT(5) ++#define BCM2835_DMA_D_DREQ BIT(6) ++#define BCM2835_DMA_S_INC BIT(8) ++#define BCM2835_DMA_S_WIDTH BIT(9) ++#define BCM2835_DMA_S_DREQ BIT(10) ++ ++#define BCM2835_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2835_DMA_WAITS(x) (((x)&0x1f) << 21) ++ ++#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ ++ ++#define BCM2835_DMA_DATA_TYPE_S8 1 ++#define BCM2835_DMA_DATA_TYPE_S16 2 ++#define BCM2835_DMA_DATA_TYPE_S32 4 ++#define BCM2835_DMA_DATA_TYPE_S128 16 ++ ++#define BCM2835_DMA_BULK_MASK BIT(0) ++#define BCM2835_DMA_FIQ_MASK (BIT(2) | BIT(3)) ++ ++ ++/* Valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ ++#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) ++ ++#define MAX_LITE_TRANSFER 32768 ++#define MAX_NORMAL_TRANSFER 1073741824 ++ ++static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d) +{ -+ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; -+ int frag_mem_size; -+ int err; -+ int i; -+ -+ /* Allocate space for the channels in coherent memory */ -+ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); -+ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS); -+ -+ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size, -+ &g_slot_phys, GFP_ATOMIC); -+ -+ if (!g_slot_mem) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocate channel memory"); -+ err = -ENOMEM; -+ goto failed_alloc; -+ } -+ -+ WARN_ON(((int)g_slot_mem & (PAGE_SIZE - 1)) != 0); -+ -+ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size); -+ if (!vchiq_slot_zero) { -+ err = -EINVAL; -+ goto failed_init_slots; -+ } -+ -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = -+ (int)g_slot_phys + g_slot_mem_size; -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = -+ MAX_FRAGMENTS; -+ -+ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size); -+ g_slot_mem_size += frag_mem_size; -+ -+ g_free_fragments = g_fragments_base; -+ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { -+ *(FRAGMENTS_T **)&g_fragments_base[i] = -+ &g_fragments_base[i + 1]; -+ } -+ *(FRAGMENTS_T **)&g_fragments_base[i] = NULL; -+ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); -+ -+ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) != -+ VCHIQ_SUCCESS) { -+ err = -EINVAL; -+ goto failed_vchiq_init; -+ } -+ -+ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq, -+ IRQF_IRQPOLL, "VCHIQ doorbell", -+ state); -+ if (err < 0) { -+ vchiq_log_error(vchiq_arm_log_level, "%s: failed to register " -+ "irq=%d err=%d", __func__, -+ VCHIQ_DOORBELL_IRQ, err); -+ goto failed_request_irq; -+ } -+ -+ /* Send the base address of the slots to VideoCore */ -+ -+ dsb(); /* Ensure all writes have completed */ -+ -+ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys); -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq_init - done (slots %x, phys %x)", -+ (unsigned int)vchiq_slot_zero, g_slot_phys); -+ -+ vchiq_call_connected_callbacks(); -+ -+ return 0; -+ -+failed_request_irq: -+failed_vchiq_init: -+failed_init_slots: -+ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys); -+ -+failed_alloc: -+ return err; ++ return container_of(d, struct bcm2835_dmadev, ddev); +} + -+void __exit -+vchiq_platform_exit(VCHIQ_STATE_T *state) ++static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c) +{ -+ free_irq(VCHIQ_DOORBELL_IRQ, state); -+ dma_free_coherent(NULL, g_slot_mem_size, -+ g_slot_mem, g_slot_phys); ++ return container_of(c, struct bcm2835_chan, vc.chan); +} + -+ -+VCHIQ_STATUS_T -+vchiq_platform_init_state(VCHIQ_STATE_T *state) ++static inline struct bcm2835_desc *to_bcm2835_dma_desc( ++ struct dma_async_tx_descriptor *t) +{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ state->platform_state = kzalloc(sizeof(VCHIQ_2835_ARM_STATE_T), GFP_KERNEL); -+ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 1; -+ status = vchiq_arm_init_state(state, &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state); -+ if(status != VCHIQ_SUCCESS) -+ { -+ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 0; -+ } -+ return status; ++ return container_of(t, struct bcm2835_desc, vd.tx); +} + -+VCHIQ_ARM_STATE_T* -+vchiq_platform_get_arm_state(VCHIQ_STATE_T *state) ++static void dma_dumpregs(struct bcm2835_chan *c) +{ -+ if(!((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited) -+ { -+ BUG(); -+ } -+ return &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state; ++ pr_debug("-------------DMA DUMPREGS-------------\n"); ++ pr_debug("CS= %u\n", ++ readl(c->chan_base + BCM2835_DMA_CS)); ++ pr_debug("ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_ADDR)); ++ pr_debug("SOURCE_ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_SOURCE_AD)); ++ pr_debug("DEST_AD= %u\n", ++ readl(c->chan_base + BCM2835_DMA_DEST_AD)); ++ pr_debug("NEXTCB= %u\n", ++ readl(c->chan_base + BCM2835_DMA_NEXTCB)); ++ pr_debug("--------------------------------------\n"); +} + -+void -+remote_event_signal(REMOTE_EVENT_T *event) ++static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) +{ -+ wmb(); -+ -+ event->fired = 1; -+ -+ dsb(); /* data barrier operation */ -+ -+ if (event->armed) { -+ /* trigger vc interrupt */ -+ -+ writel(0, __io_address(ARM_0_BELL2)); -+ } ++ struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd); ++ dma_free_coherent(desc->vd.tx.chan->device->dev, ++ desc->control_block_size, ++ desc->control_block_base, ++ desc->control_block_base_phys); ++ kfree(desc); +} + -+int -+vchiq_copy_from_user(void *dst, const void *src, int size) ++static int bcm2835_dma_abort(void __iomem *chan_base) +{ -+ if ((uint32_t)src < TASK_SIZE) { -+ return copy_from_user(dst, src, size); -+ } else { -+ memcpy(dst, src, size); ++ unsigned long cs; ++ long int timeout = 10000; ++ ++ cs = readl(chan_base + BCM2835_DMA_CS); ++ if (!(cs & BCM2835_DMA_ACTIVE)) + return 0; ++ ++ /* Write 0 to the active bit - Pause the DMA */ ++ writel(0, chan_base + BCM2835_DMA_CS); ++ ++ /* Wait for any current AXI transfer to complete */ ++ while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) { ++ cpu_relax(); ++ cs = readl(chan_base + BCM2835_DMA_CS); + } ++ ++ /* We'll un-pause when we set of our next DMA */ ++ if (!timeout) ++ return -ETIMEDOUT; ++ ++ if (!(cs & BCM2835_DMA_ACTIVE)) ++ return 0; ++ ++ /* Terminate the control block chain */ ++ writel(0, chan_base + BCM2835_DMA_NEXTCB); ++ ++ /* Abort the whole DMA */ ++ writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, ++ chan_base + BCM2835_DMA_CS); ++ ++ return 0; +} + -+VCHIQ_STATUS_T -+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, -+ void *offset, int size, int dir) ++ ++static void bcm2835_dma_start_desc(struct bcm2835_chan *c) +{ -+ PAGELIST_T *pagelist; ++ struct virt_dma_desc *vd = vchan_next_desc(&c->vc); ++ struct bcm2835_desc *d; ++ ++ if (!vd) { ++ c->desc = NULL; ++ return; ++ } ++ ++ list_del(&vd->node); ++ ++ c->desc = d = to_bcm2835_dma_desc(&vd->tx); ++ ++ writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR); ++ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); ++ ++} ++ ++static irqreturn_t bcm2835_dma_callback(int irq, void *data) ++{ ++ struct bcm2835_chan *c = data; ++ struct bcm2835_desc *d; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Acknowledge interrupt */ ++ writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); ++ ++ d = c->desc; ++ ++ if (d) { ++ if (c->cyclic) { ++ vchan_cyclic_callback(&d->vd); ++ ++ /* Keep the DMA engine running */ ++ writel(BCM2835_DMA_ACTIVE, ++ c->chan_base + BCM2835_DMA_CS); ++ ++ } else { ++ vchan_cookie_complete(&c->desc->vd); ++ bcm2835_dma_start_desc(c); ++ } ++ } ++ ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + int ret; + -+ WARN_ON(memhandle != VCHI_MEM_HANDLE_INVALID); ++ dev_dbg(c->vc.chan.device->dev, ++ "Allocating DMA channel %d\n", c->ch); + -+ ret = create_pagelist((char __user *)offset, size, -+ (dir == VCHIQ_BULK_RECEIVE) -+ ? PAGELIST_READ -+ : PAGELIST_WRITE, -+ current, -+ &pagelist); -+ if (ret != 0) -+ return VCHIQ_ERROR; -+ -+ bulk->handle = memhandle; -+ bulk->data = VCHIQ_ARM_ADDRESS(pagelist); -+ -+ /* Store the pagelist address in remote_data, which isn't used by the -+ slave. */ -+ bulk->remote_data = pagelist; -+ -+ return VCHIQ_SUCCESS; -+} -+ -+void -+vchiq_complete_bulk(VCHIQ_BULK_T *bulk) -+{ -+ if (bulk && bulk->remote_data && bulk->actual) -+ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual); -+} -+ -+void -+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk) -+{ -+ /* -+ * This should only be called on the master (VideoCore) side, but -+ * provide an implementation to avoid the need for ifdefery. -+ */ -+ BUG(); -+} -+ -+void -+vchiq_dump_platform_state(void *dump_context) -+{ -+ char buf[80]; -+ int len; -+ len = snprintf(buf, sizeof(buf), -+ " Platform: 2835 (VC master)"); -+ vchiq_dump(dump_context, buf, len + 1); -+} -+ -+VCHIQ_STATUS_T -+vchiq_platform_suspend(VCHIQ_STATE_T *state) -+{ -+ return VCHIQ_ERROR; -+} -+ -+VCHIQ_STATUS_T -+vchiq_platform_resume(VCHIQ_STATE_T *state) -+{ -+ return VCHIQ_SUCCESS; -+} -+ -+void -+vchiq_platform_paused(VCHIQ_STATE_T *state) -+{ -+} -+ -+void -+vchiq_platform_resumed(VCHIQ_STATE_T *state) -+{ -+} -+ -+int -+vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state) -+{ -+ return 1; // autosuspend not supported - videocore always wanted -+} -+ -+int -+vchiq_platform_use_suspend_timer(void) -+{ -+ return 0; -+} -+void -+vchiq_dump_platform_use_state(VCHIQ_STATE_T *state) -+{ -+ vchiq_log_info((vchiq_arm_log_level>=VCHIQ_LOG_INFO),"Suspend timer not in use"); -+} -+void -+vchiq_platform_handle_timeout(VCHIQ_STATE_T *state) -+{ -+ (void)state; -+} -+/* -+ * Local functions -+ */ -+ -+static irqreturn_t -+vchiq_doorbell_irq(int irq, void *dev_id) -+{ -+ VCHIQ_STATE_T *state = dev_id; -+ irqreturn_t ret = IRQ_NONE; -+ unsigned int status; -+ -+ /* Read (and clear) the doorbell */ -+ status = readl(__io_address(ARM_0_BELL0)); -+ -+ if (status & 0x4) { /* Was the doorbell rung? */ -+ remote_event_pollall(state); -+ ret = IRQ_HANDLED; -+ } ++ ret = request_irq(c->irq_number, ++ bcm2835_dma_callback, 0, "DMA IRQ", c); + + return ret; +} + -+/* There is a potential problem with partial cache lines (pages?) -+** at the ends of the block when reading. If the CPU accessed anything in -+** the same line (page?) then it may have pulled old data into the cache, -+** obscuring the new data underneath. We can solve this by transferring the -+** partial cache lines separately, and allowing the ARM to copy into the -+** cached area. -+ -+** N.B. This implementation plays slightly fast and loose with the Linux -+** driver programming rules, e.g. its use of __virt_to_bus instead of -+** dma_map_single, but it isn't a multi-platform driver and it benefits -+** from increased speed as a result. -+*/ -+ -+static int -+create_pagelist(char __user *buf, size_t count, unsigned short type, -+ struct task_struct *task, PAGELIST_T ** ppagelist) ++static void bcm2835_dma_free_chan_resources(struct dma_chan *chan) +{ -+ PAGELIST_T *pagelist; -+ struct page **pages; -+ struct page *page; -+ unsigned long *addrs; -+ unsigned int num_pages, offset, i; -+ char *addr, *base_addr, *next_addr; -+ int run, addridx, actual_pages; -+ unsigned long *need_release; ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + -+ offset = (unsigned int)buf & (PAGE_SIZE - 1); -+ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; ++ vchan_free_chan_resources(&c->vc); ++ free_irq(c->irq_number, c); + -+ *ppagelist = NULL; -+ -+ /* Allocate enough storage to hold the page pointers and the page -+ ** list -+ */ -+ pagelist = kmalloc(sizeof(PAGELIST_T) + -+ (num_pages * sizeof(unsigned long)) + -+ sizeof(unsigned long) + -+ (num_pages * sizeof(pages[0])), -+ GFP_KERNEL); -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "create_pagelist - %x", (unsigned int)pagelist); -+ if (!pagelist) -+ return -ENOMEM; -+ -+ addrs = pagelist->addrs; -+ need_release = (unsigned long *)(addrs + num_pages); -+ pages = (struct page **)(addrs + num_pages + 1); -+ -+ if (is_vmalloc_addr(buf)) { -+ for (actual_pages = 0; actual_pages < num_pages; actual_pages++) { -+ pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE)); -+ } -+ *need_release = 0; /* do not try and release vmalloc pages */ -+ } else { -+ down_read(&task->mm->mmap_sem); -+ actual_pages = get_user_pages(task, task->mm, -+ (unsigned long)buf & ~(PAGE_SIZE - 1), -+ num_pages, -+ (type == PAGELIST_READ) /*Write */ , -+ 0 /*Force */ , -+ pages, -+ NULL /*vmas */); -+ up_read(&task->mm->mmap_sem); -+ -+ if (actual_pages != num_pages) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "create_pagelist - only %d/%d pages locked", -+ actual_pages, -+ num_pages); -+ -+ /* This is probably due to the process being killed */ -+ while (actual_pages > 0) -+ { -+ actual_pages--; -+ page_cache_release(pages[actual_pages]); -+ } -+ kfree(pagelist); -+ if (actual_pages == 0) -+ actual_pages = -ENOMEM; -+ return actual_pages; -+ } -+ *need_release = 1; /* release user pages */ -+ } -+ -+ pagelist->length = count; -+ pagelist->type = type; -+ pagelist->offset = offset; -+ -+ /* Group the pages into runs of contiguous pages */ -+ -+ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0])); -+ next_addr = base_addr + PAGE_SIZE; -+ addridx = 0; -+ run = 0; -+ -+ for (i = 1; i < num_pages; i++) { -+ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i])); -+ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) { -+ next_addr += PAGE_SIZE; -+ run++; -+ } else { -+ addrs[addridx] = (unsigned long)base_addr + run; -+ addridx++; -+ base_addr = addr; -+ next_addr = addr + PAGE_SIZE; -+ run = 0; -+ } -+ } -+ -+ addrs[addridx] = (unsigned long)base_addr + run; -+ addridx++; -+ -+ /* Partial cache lines (fragments) require special measures */ -+ if ((type == PAGELIST_READ) && -+ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) || -+ ((pagelist->offset + pagelist->length) & -+ (CACHE_LINE_SIZE - 1)))) { -+ FRAGMENTS_T *fragments; -+ -+ if (down_interruptible(&g_free_fragments_sema) != 0) { -+ kfree(pagelist); -+ return -EINTR; -+ } -+ -+ WARN_ON(g_free_fragments == NULL); -+ -+ down(&g_free_fragments_mutex); -+ fragments = (FRAGMENTS_T *) g_free_fragments; -+ WARN_ON(fragments == NULL); -+ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments; -+ up(&g_free_fragments_mutex); -+ pagelist->type = -+ PAGELIST_READ_WITH_FRAGMENTS + (fragments - -+ g_fragments_base); -+ } -+ -+ for (page = virt_to_page(pagelist); -+ page <= virt_to_page(addrs + num_pages - 1); page++) { -+ flush_dcache_page(page); -+ } -+ -+ *ppagelist = pagelist; -+ -+ return 0; ++ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); +} + -+static void -+free_pagelist(PAGELIST_T *pagelist, int actual) ++static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d) +{ -+ unsigned long *need_release; -+ struct page **pages; -+ unsigned int num_pages, i; -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "free_pagelist - %x, %d", (unsigned int)pagelist, actual); -+ -+ num_pages = -+ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / -+ PAGE_SIZE; -+ -+ need_release = (unsigned long *)(pagelist->addrs + num_pages); -+ pages = (struct page **)(pagelist->addrs + num_pages + 1); -+ -+ /* Deal with any partial cache lines (fragments) */ -+ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { -+ FRAGMENTS_T *fragments = g_fragments_base + -+ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS); -+ int head_bytes, tail_bytes; -+ head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & -+ (CACHE_LINE_SIZE - 1); -+ tail_bytes = (pagelist->offset + actual) & -+ (CACHE_LINE_SIZE - 1); -+ -+ if ((actual >= 0) && (head_bytes != 0)) { -+ if (head_bytes > actual) -+ head_bytes = actual; -+ -+ memcpy((char *)page_address(pages[0]) + -+ pagelist->offset, -+ fragments->headbuf, -+ head_bytes); -+ } -+ if ((actual >= 0) && (head_bytes < actual) && -+ (tail_bytes != 0)) { -+ memcpy((char *)page_address(pages[num_pages - 1]) + -+ ((pagelist->offset + actual) & -+ (PAGE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)), -+ fragments->tailbuf, tail_bytes); -+ } -+ -+ down(&g_free_fragments_mutex); -+ *(FRAGMENTS_T **) fragments = g_free_fragments; -+ g_free_fragments = fragments; -+ up(&g_free_fragments_mutex); -+ up(&g_free_fragments_sema); -+ } -+ -+ if (*need_release) { -+ for (i = 0; i < num_pages; i++) { -+ if (pagelist->type != PAGELIST_WRITE) -+ set_page_dirty(pages[i]); -+ -+ page_cache_release(pages[i]); -+ } -+ } -+ -+ kfree(pagelist); -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -new file mode 100644 -index 0000000..2596818 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -0,0 +1,2883 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "vchiq_core.h" -+#include "vchiq_ioctl.h" -+#include "vchiq_arm.h" -+#include "vchiq_debugfs.h" -+ -+#define DEVICE_NAME "vchiq" -+ -+/* Override the default prefix, which would be vchiq_arm (from the filename) */ -+#undef MODULE_PARAM_PREFIX -+#define MODULE_PARAM_PREFIX DEVICE_NAME "." -+ -+#define VCHIQ_MINOR 0 -+ -+/* Some per-instance constants */ -+#define MAX_COMPLETIONS 16 -+#define MAX_SERVICES 64 -+#define MAX_ELEMENTS 8 -+#define MSG_QUEUE_SIZE 64 -+ -+#define KEEPALIVE_VER 1 -+#define KEEPALIVE_VER_MIN KEEPALIVE_VER -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -+ -+#define SUSPEND_TIMER_TIMEOUT_MS 100 -+#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 -+ -+#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ -+static const char *const suspend_state_names[] = { -+ "VC_SUSPEND_FORCE_CANCELED", -+ "VC_SUSPEND_REJECTED", -+ "VC_SUSPEND_FAILED", -+ "VC_SUSPEND_IDLE", -+ "VC_SUSPEND_REQUESTED", -+ "VC_SUSPEND_IN_PROGRESS", -+ "VC_SUSPEND_SUSPENDED" -+}; -+#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ -+static const char *const resume_state_names[] = { -+ "VC_RESUME_FAILED", -+ "VC_RESUME_IDLE", -+ "VC_RESUME_REQUESTED", -+ "VC_RESUME_IN_PROGRESS", -+ "VC_RESUME_RESUMED" -+}; -+/* The number of times we allow force suspend to timeout before actually -+** _forcing_ suspend. This is to cater for SW which fails to release vchiq -+** correctly - we don't want to prevent ARM suspend indefinitely in this case. -+*/ -+#define FORCE_SUSPEND_FAIL_MAX 8 -+ -+/* The time in ms allowed for videocore to go idle when force suspend has been -+ * requested */ -+#define FORCE_SUSPEND_TIMEOUT_MS 200 -+ -+ -+static void suspend_timer_callback(unsigned long context); -+ -+ -+typedef struct user_service_struct { -+ VCHIQ_SERVICE_T *service; -+ void *userdata; -+ VCHIQ_INSTANCE_T instance; -+ char is_vchi; -+ char dequeue_pending; -+ char close_pending; -+ int message_available_pos; -+ int msg_insert; -+ int msg_remove; -+ struct semaphore insert_event; -+ struct semaphore remove_event; -+ struct semaphore close_event; -+ VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; -+} USER_SERVICE_T; -+ -+struct bulk_waiter_node { -+ struct bulk_waiter bulk_waiter; -+ int pid; -+ struct list_head list; -+}; -+ -+struct vchiq_instance_struct { -+ VCHIQ_STATE_T *state; -+ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; -+ int completion_insert; -+ int completion_remove; -+ struct semaphore insert_event; -+ struct semaphore remove_event; -+ struct mutex completion_mutex; -+ -+ int connected; -+ int closing; -+ int pid; -+ int mark; -+ int use_close_delivered; -+ int trace; -+ -+ struct list_head bulk_waiter_list; -+ struct mutex bulk_waiter_list_mutex; -+ -+ VCHIQ_DEBUGFS_NODE_T debugfs_node; -+}; -+ -+typedef struct dump_context_struct { -+ char __user *buf; -+ size_t actual; -+ size_t space; -+ loff_t offset; -+} DUMP_CONTEXT_T; -+ -+static struct cdev vchiq_cdev; -+static dev_t vchiq_devid; -+static VCHIQ_STATE_T g_state; -+static struct class *vchiq_class; -+static struct device *vchiq_dev; -+static DEFINE_SPINLOCK(msg_queue_spinlock); -+ -+static const char *const ioctl_names[] = { -+ "CONNECT", -+ "SHUTDOWN", -+ "CREATE_SERVICE", -+ "REMOVE_SERVICE", -+ "QUEUE_MESSAGE", -+ "QUEUE_BULK_TRANSMIT", -+ "QUEUE_BULK_RECEIVE", -+ "AWAIT_COMPLETION", -+ "DEQUEUE_MESSAGE", -+ "GET_CLIENT_ID", -+ "GET_CONFIG", -+ "CLOSE_SERVICE", -+ "USE_SERVICE", -+ "RELEASE_SERVICE", -+ "SET_SERVICE_OPTION", -+ "DUMP_PHYS_MEM", -+ "LIB_VERSION", -+ "CLOSE_DELIVERED" -+}; -+ -+vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == -+ (VCHIQ_IOC_MAX + 1)); -+ -+static void -+dump_phys_mem(void *virt_addr, uint32_t num_bytes); -+ -+/**************************************************************************** -+* -+* add_completion -+* -+***************************************************************************/ -+ -+static VCHIQ_STATUS_T -+add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service, -+ void *bulk_userdata) -+{ -+ VCHIQ_COMPLETION_DATA_T *completion; -+ DEBUG_INITIALISE(g_state.local) -+ -+ while (instance->completion_insert == -+ (instance->completion_remove + MAX_COMPLETIONS)) { -+ /* Out of space - wait for the client */ -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ vchiq_log_trace(vchiq_arm_log_level, -+ "add_completion - completion queue full"); -+ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); -+ if (down_interruptible(&instance->remove_event) != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback interrupted"); -+ return VCHIQ_RETRY; -+ } else if (instance->closing) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback closing"); -+ return VCHIQ_ERROR; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ } -+ -+ completion = -+ &instance->completions[instance->completion_insert & -+ (MAX_COMPLETIONS - 1)]; -+ -+ completion->header = header; -+ completion->reason = reason; -+ /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ -+ completion->service_userdata = user_service->service; -+ completion->bulk_userdata = bulk_userdata; -+ -+ if (reason == VCHIQ_SERVICE_CLOSED) { -+ /* Take an extra reference, to be held until -+ this CLOSED notification is delivered. */ -+ lock_service(user_service->service); -+ if (instance->use_close_delivered) -+ user_service->close_pending = 1; -+ } -+ -+ /* A write barrier is needed here to ensure that the entire completion -+ record is written out before the insert point. */ -+ wmb(); -+ -+ if (reason == VCHIQ_MESSAGE_AVAILABLE) -+ user_service->message_available_pos = -+ instance->completion_insert; -+ instance->completion_insert++; -+ -+ up(&instance->insert_event); -+ -+ return VCHIQ_SUCCESS; ++ return d->size; +} + -+/**************************************************************************** -+* -+* service_callback -+* -+***************************************************************************/ -+ -+static VCHIQ_STATUS_T -+service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, -+ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) ++static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) +{ -+ /* How do we ensure the callback goes to the right client? -+ ** The service_user data points to a USER_SERVICE_T record containing -+ ** the original callback and the user state structure, which contains a -+ ** circular buffer for completion records. -+ */ -+ USER_SERVICE_T *user_service; -+ VCHIQ_SERVICE_T *service; -+ VCHIQ_INSTANCE_T instance; -+ DEBUG_INITIALISE(g_state.local) ++ unsigned int i; ++ size_t size; + -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ for (size = i = 0; i < d->frames; i++) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[i]; ++ size_t this_size = control_block->length; ++ dma_addr_t dma; + -+ service = handle_to_service(handle); -+ BUG_ON(!service); -+ user_service = (USER_SERVICE_T *)service->base.userdata; -+ instance = user_service->instance; -+ -+ if (!instance || instance->closing) -+ return VCHIQ_SUCCESS; -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "service_callback - service %lx(%d,%p), reason %d, header %lx, " -+ "instance %lx, bulk_userdata %lx", -+ (unsigned long)user_service, -+ service->localport, user_service->userdata, -+ reason, (unsigned long)header, -+ (unsigned long)instance, (unsigned long)bulk_userdata); -+ -+ if (header && user_service->is_vchi) { -+ spin_lock(&msg_queue_spinlock); -+ while (user_service->msg_insert == -+ (user_service->msg_remove + MSG_QUEUE_SIZE)) { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); -+ vchiq_log_trace(vchiq_arm_log_level, -+ "service_callback - msg queue full"); -+ /* If there is no MESSAGE_AVAILABLE in the completion -+ ** queue, add one -+ */ -+ if ((user_service->message_available_pos - -+ instance->completion_remove) < 0) { -+ VCHIQ_STATUS_T status; -+ vchiq_log_info(vchiq_arm_log_level, -+ "Inserting extra MESSAGE_AVAILABLE"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ status = add_completion(instance, reason, -+ NULL, user_service, bulk_userdata); -+ if (status != VCHIQ_SUCCESS) { -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return status; -+ } -+ } -+ -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ if (down_interruptible(&user_service->remove_event) -+ != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback interrupted"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return VCHIQ_RETRY; -+ } else if (instance->closing) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback closing"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return VCHIQ_ERROR; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ spin_lock(&msg_queue_spinlock); -+ } -+ -+ user_service->msg_queue[user_service->msg_insert & -+ (MSG_QUEUE_SIZE - 1)] = header; -+ user_service->msg_insert++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ up(&user_service->insert_event); -+ -+ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if -+ ** there is a MESSAGE_AVAILABLE in the completion queue then -+ ** bypass the completion queue. -+ */ -+ if (((user_service->message_available_pos - -+ instance->completion_remove) >= 0) || -+ user_service->dequeue_pending) { -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ user_service->dequeue_pending = 0; -+ return VCHIQ_SUCCESS; -+ } -+ -+ header = NULL; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ -+ return add_completion(instance, reason, header, user_service, -+ bulk_userdata); -+} -+ -+/**************************************************************************** -+* -+* user_service_free -+* -+***************************************************************************/ -+static void -+user_service_free(void *userdata) -+{ -+ kfree(userdata); -+} -+ -+/**************************************************************************** -+* -+* close_delivered -+* -+***************************************************************************/ -+static void close_delivered(USER_SERVICE_T *user_service) -+{ -+ vchiq_log_info(vchiq_arm_log_level, -+ "close_delivered(handle=%x)", -+ user_service->service->handle); -+ -+ if (user_service->close_pending) { -+ /* Allow the underlying service to be culled */ -+ unlock_service(user_service->service); -+ -+ /* Wake the user-thread blocked in close_ or remove_service */ -+ up(&user_service->close_event); -+ -+ user_service->close_pending = 0; -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_ioctl -+* -+***************************************************************************/ -+static long -+vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ VCHIQ_INSTANCE_T instance = file->private_data; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ VCHIQ_SERVICE_T *service = NULL; -+ long ret = 0; -+ int i, rc; -+ DEBUG_INITIALISE(g_state.local) -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "vchiq_ioctl - instance %x, cmd %s, arg %lx", -+ (unsigned int)instance, -+ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? -+ ioctl_names[_IOC_NR(cmd)] : "", arg); -+ -+ switch (cmd) { -+ case VCHIQ_IOC_SHUTDOWN: -+ if (!instance->connected) -+ break; -+ -+ /* Remove all services */ -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ status = vchiq_remove_service(service->handle); -+ unlock_service(service); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ service = NULL; -+ -+ if (status == VCHIQ_SUCCESS) { -+ /* Wake the completion thread and ask it to exit */ -+ instance->closing = 1; -+ up(&instance->insert_event); -+ } -+ -+ break; -+ -+ case VCHIQ_IOC_CONNECT: -+ if (instance->connected) { -+ ret = -EINVAL; -+ break; -+ } -+ rc = mutex_lock_interruptible(&instance->state->mutex); -+ if (rc != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq: connect: could not lock mutex for " -+ "state %d: %d", -+ instance->state->id, rc); -+ ret = -EINTR; -+ break; -+ } -+ status = vchiq_connect_internal(instance->state, instance); -+ mutex_unlock(&instance->state->mutex); -+ -+ if (status == VCHIQ_SUCCESS) -+ instance->connected = 1; ++ if (d->dir == DMA_DEV_TO_MEM) ++ dma = control_block->dst; + else -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq: could not connect: %d", status); -+ break; ++ dma = control_block->src; + -+ case VCHIQ_IOC_CREATE_SERVICE: { -+ VCHIQ_CREATE_SERVICE_T args; -+ USER_SERVICE_T *user_service = NULL; -+ void *userdata; -+ int srvstate; ++ if (size) ++ size += this_size; ++ else if (addr >= dma && addr < dma + this_size) ++ size += dma + this_size - addr; ++ } + -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } ++ return size; ++} + -+ user_service = kmalloc(sizeof(USER_SERVICE_T), GFP_KERNEL); -+ if (!user_service) { -+ ret = -ENOMEM; -+ break; -+ } ++static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, ++ dma_cookie_t cookie, struct dma_tx_state *txstate) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ struct bcm2835_desc *d; ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ dma_addr_t pos; + -+ if (args.is_open) { -+ if (!instance->connected) { -+ ret = -ENOTCONN; -+ kfree(user_service); -+ break; -+ } -+ srvstate = VCHIQ_SRVSTATE_OPENING; -+ } else { -+ srvstate = -+ instance->connected ? -+ VCHIQ_SRVSTATE_LISTENING : -+ VCHIQ_SRVSTATE_HIDDEN; -+ } ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE || !txstate) ++ return ret; + -+ userdata = args.params.userdata; -+ args.params.callback = service_callback; -+ args.params.userdata = user_service; -+ service = vchiq_add_service_internal( -+ instance->state, -+ &args.params, srvstate, -+ instance, user_service_free); ++ spin_lock_irqsave(&c->vc.lock, flags); ++ vd = vchan_find_desc(&c->vc, cookie); ++ if (vd) { ++ txstate->residue = ++ bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx)); ++ } else if (c->desc && c->desc->vd.tx.cookie == cookie) { ++ d = c->desc; + -+ if (service != NULL) { -+ user_service->service = service; -+ user_service->userdata = userdata; -+ user_service->instance = instance; -+ user_service->is_vchi = (args.is_vchi != 0); -+ user_service->dequeue_pending = 0; -+ user_service->close_pending = 0; -+ user_service->message_available_pos = -+ instance->completion_remove - 1; -+ user_service->msg_insert = 0; -+ user_service->msg_remove = 0; -+ sema_init(&user_service->insert_event, 0); -+ sema_init(&user_service->remove_event, 0); -+ sema_init(&user_service->close_event, 0); -+ -+ if (args.is_open) { -+ status = vchiq_open_service_internal -+ (service, instance->pid); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_remove_service(service->handle); -+ service = NULL; -+ ret = (status == VCHIQ_RETRY) ? -+ -EINTR : -EIO; -+ break; -+ } -+ } -+ -+ if (copy_to_user((void __user *) -+ &(((VCHIQ_CREATE_SERVICE_T __user *) -+ arg)->handle), -+ (const void *)&service->handle, -+ sizeof(service->handle)) != 0) { -+ ret = -EFAULT; -+ vchiq_remove_service(service->handle); -+ } -+ -+ service = NULL; -+ } else { -+ ret = -EEXIST; -+ kfree(user_service); -+ } -+ } break; -+ -+ case VCHIQ_IOC_CLOSE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ /* close_pending is false on first entry, and when the -+ wait in vchiq_close_service has been interrupted. */ -+ if (!user_service->close_pending) { -+ status = vchiq_close_service(service->handle); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ -+ /* close_pending is true once the underlying service -+ has been closed until the client library calls the -+ CLOSE_DELIVERED ioctl, signalling close_event. */ -+ if (user_service->close_pending && -+ down_interruptible(&user_service->close_event)) -+ status = VCHIQ_RETRY; -+ } ++ if (d->dir == DMA_MEM_TO_DEV) ++ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); ++ else if (d->dir == DMA_DEV_TO_MEM) ++ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); + else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_REMOVE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ /* close_pending is false on first entry, and when the -+ wait in vchiq_close_service has been interrupted. */ -+ if (!user_service->close_pending) { -+ status = vchiq_remove_service(service->handle); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ -+ /* close_pending is true once the underlying service -+ has been closed until the client library calls the -+ CLOSE_DELIVERED ioctl, signalling close_event. */ -+ if (user_service->close_pending && -+ down_interruptible(&user_service->close_event)) -+ status = VCHIQ_RETRY; -+ } -+ else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_USE_SERVICE: -+ case VCHIQ_IOC_RELEASE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? -+ vchiq_use_service_internal(service) : -+ vchiq_release_service_internal(service); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s: cmd %s returned error %d for " -+ "service %c%c%c%c:%03d", -+ __func__, -+ (cmd == VCHIQ_IOC_USE_SERVICE) ? -+ "VCHIQ_IOC_USE_SERVICE" : -+ "VCHIQ_IOC_RELEASE_SERVICE", -+ status, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->client_id); -+ ret = -EINVAL; -+ } -+ } else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_QUEUE_MESSAGE: { -+ VCHIQ_QUEUE_MESSAGE_T args; -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ -+ if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { -+ /* Copy elements into kernel space */ -+ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; -+ if (copy_from_user(elements, args.elements, -+ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) -+ status = vchiq_queue_message -+ (args.handle, -+ elements, args.count); -+ else -+ ret = -EFAULT; -+ } else { -+ ret = -EINVAL; -+ } -+ } break; -+ -+ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: -+ case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { -+ VCHIQ_QUEUE_BULK_TRANSFER_T args; -+ struct bulk_waiter_node *waiter = NULL; -+ VCHIQ_BULK_DIR_T dir = -+ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? -+ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; -+ -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (args.mode == VCHIQ_BULK_MODE_BLOCKING) { -+ waiter = kzalloc(sizeof(struct bulk_waiter_node), -+ GFP_KERNEL); -+ if (!waiter) { -+ ret = -ENOMEM; -+ break; -+ } -+ args.userdata = &waiter->bulk_waiter; -+ } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { -+ struct list_head *pos; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_for_each(pos, &instance->bulk_waiter_list) { -+ if (list_entry(pos, struct bulk_waiter_node, -+ list)->pid == current->pid) { -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ break; -+ } -+ -+ } -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ if (!waiter) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "no bulk_waiter found for pid %d", -+ current->pid); -+ ret = -ESRCH; -+ break; -+ } -+ vchiq_log_info(vchiq_arm_log_level, -+ "found bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); -+ args.userdata = &waiter->bulk_waiter; -+ } -+ status = vchiq_bulk_transfer -+ (args.handle, -+ VCHI_MEM_HANDLE_INVALID, -+ args.data, args.size, -+ args.userdata, args.mode, -+ dir); -+ if (!waiter) -+ break; -+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || -+ !waiter->bulk_waiter.bulk) { -+ if (waiter->bulk_waiter.bulk) { -+ /* Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ waiter->bulk_waiter.bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); -+ } -+ kfree(waiter); -+ } else { -+ const VCHIQ_BULK_MODE_T mode_waiting = -+ VCHIQ_BULK_MODE_WAITING; -+ waiter->pid = current->pid; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_add(&waiter->list, &instance->bulk_waiter_list); -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ vchiq_log_info(vchiq_arm_log_level, -+ "saved bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); -+ -+ if (copy_to_user((void __user *) -+ &(((VCHIQ_QUEUE_BULK_TRANSFER_T __user *) -+ arg)->mode), -+ (const void *)&mode_waiting, -+ sizeof(mode_waiting)) != 0) -+ ret = -EFAULT; -+ } -+ } break; -+ -+ case VCHIQ_IOC_AWAIT_COMPLETION: { -+ VCHIQ_AWAIT_COMPLETION_T args; -+ -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ if (!instance->connected) { -+ ret = -ENOTCONN; -+ break; -+ } -+ -+ if (copy_from_user(&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ mutex_lock(&instance->completion_mutex); -+ -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ while ((instance->completion_remove == -+ instance->completion_insert) -+ && !instance->closing) { -+ int rc; -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ mutex_unlock(&instance->completion_mutex); -+ rc = down_interruptible(&instance->insert_event); -+ mutex_lock(&instance->completion_mutex); -+ if (rc != 0) { -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ vchiq_log_info(vchiq_arm_log_level, -+ "AWAIT_COMPLETION interrupted"); -+ ret = -EINTR; -+ break; -+ } -+ } -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ -+ /* A read memory barrier is needed to stop prefetch of a stale -+ ** completion record -+ */ -+ rmb(); -+ -+ if (ret == 0) { -+ int msgbufcount = args.msgbufcount; -+ for (ret = 0; ret < args.count; ret++) { -+ VCHIQ_COMPLETION_DATA_T *completion; -+ VCHIQ_SERVICE_T *service; -+ USER_SERVICE_T *user_service; -+ VCHIQ_HEADER_T *header; -+ if (instance->completion_remove == -+ instance->completion_insert) -+ break; -+ completion = &instance->completions[ -+ instance->completion_remove & -+ (MAX_COMPLETIONS - 1)]; -+ -+ service = completion->service_userdata; -+ user_service = service->base.userdata; -+ completion->service_userdata = -+ user_service->userdata; -+ -+ header = completion->header; -+ if (header) { -+ void __user *msgbuf; -+ int msglen; -+ -+ msglen = header->size + -+ sizeof(VCHIQ_HEADER_T); -+ /* This must be a VCHIQ-style service */ -+ if (args.msgbufsize < msglen) { -+ vchiq_log_error( -+ vchiq_arm_log_level, -+ "header %x: msgbufsize" -+ " %x < msglen %x", -+ (unsigned int)header, -+ args.msgbufsize, -+ msglen); -+ WARN(1, "invalid message " -+ "size\n"); -+ if (ret == 0) -+ ret = -EMSGSIZE; -+ break; -+ } -+ if (msgbufcount <= 0) -+ /* Stall here for lack of a -+ ** buffer for the message. */ -+ break; -+ /* Get the pointer from user space */ -+ msgbufcount--; -+ if (copy_from_user(&msgbuf, -+ (const void __user *) -+ &args.msgbufs[msgbufcount], -+ sizeof(msgbuf)) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ /* Copy the message to user space */ -+ if (copy_to_user(msgbuf, header, -+ msglen) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ /* Now it has been copied, the message -+ ** can be released. */ -+ vchiq_release_message(service->handle, -+ header); -+ -+ /* The completion must point to the -+ ** msgbuf. */ -+ completion->header = msgbuf; -+ } -+ -+ if ((completion->reason == -+ VCHIQ_SERVICE_CLOSED) && -+ !instance->use_close_delivered) -+ unlock_service(service); -+ -+ if (copy_to_user((void __user *)( -+ (size_t)args.buf + -+ ret * sizeof(VCHIQ_COMPLETION_DATA_T)), -+ completion, -+ sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ instance->completion_remove++; -+ } -+ -+ if (msgbufcount != args.msgbufcount) { -+ if (copy_to_user((void __user *) -+ &((VCHIQ_AWAIT_COMPLETION_T *)arg)-> -+ msgbufcount, -+ &msgbufcount, -+ sizeof(msgbufcount)) != 0) { -+ ret = -EFAULT; -+ } -+ } -+ } -+ -+ if (ret != 0) -+ up(&instance->remove_event); -+ mutex_unlock(&instance->completion_mutex); -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ } break; -+ -+ case VCHIQ_IOC_DEQUEUE_MESSAGE: { -+ VCHIQ_DEQUEUE_MESSAGE_T args; -+ USER_SERVICE_T *user_service; -+ VCHIQ_HEADER_T *header; -+ -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ user_service = (USER_SERVICE_T *)service->base.userdata; -+ if (user_service->is_vchi == 0) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ spin_lock(&msg_queue_spinlock); -+ if (user_service->msg_remove == user_service->msg_insert) { -+ if (!args.blocking) { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ ret = -EWOULDBLOCK; -+ break; -+ } -+ user_service->dequeue_pending = 1; -+ do { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ if (down_interruptible( -+ &user_service->insert_event) != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "DEQUEUE_MESSAGE interrupted"); -+ ret = -EINTR; -+ break; -+ } -+ spin_lock(&msg_queue_spinlock); -+ } while (user_service->msg_remove == -+ user_service->msg_insert); -+ -+ if (ret) -+ break; -+ } -+ -+ BUG_ON((int)(user_service->msg_insert - -+ user_service->msg_remove) < 0); -+ -+ header = user_service->msg_queue[user_service->msg_remove & -+ (MSG_QUEUE_SIZE - 1)]; -+ user_service->msg_remove++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ up(&user_service->remove_event); -+ if (header == NULL) -+ ret = -ENOTCONN; -+ else if (header->size <= args.bufsize) { -+ /* Copy to user space if msgbuf is not NULL */ -+ if ((args.buf == NULL) || -+ (copy_to_user((void __user *)args.buf, -+ header->data, -+ header->size) == 0)) { -+ ret = header->size; -+ vchiq_release_message( -+ service->handle, -+ header); -+ } else -+ ret = -EFAULT; -+ } else { -+ vchiq_log_error(vchiq_arm_log_level, -+ "header %x: bufsize %x < size %x", -+ (unsigned int)header, args.bufsize, -+ header->size); -+ WARN(1, "invalid size\n"); -+ ret = -EMSGSIZE; -+ } -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ } break; -+ -+ case VCHIQ_IOC_GET_CLIENT_ID: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ ret = vchiq_get_client_id(handle); -+ } break; -+ -+ case VCHIQ_IOC_GET_CONFIG: { -+ VCHIQ_GET_CONFIG_T args; -+ VCHIQ_CONFIG_T config; -+ -+ if (copy_from_user(&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ if (args.config_size > sizeof(config)) { -+ ret = -EINVAL; -+ break; -+ } -+ status = vchiq_get_config(instance, args.config_size, &config); -+ if (status == VCHIQ_SUCCESS) { -+ if (copy_to_user((void __user *)args.pconfig, -+ &config, args.config_size) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ } -+ } break; -+ -+ case VCHIQ_IOC_SET_SERVICE_OPTION: { -+ VCHIQ_SET_SERVICE_OPTION_T args; -+ -+ if (copy_from_user( -+ &args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ status = vchiq_set_service_option( -+ args.handle, args.option, args.value); -+ } break; -+ -+ case VCHIQ_IOC_DUMP_PHYS_MEM: { -+ VCHIQ_DUMP_MEM_T args; -+ -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ dump_phys_mem(args.virt_addr, args.num_bytes); -+ } break; -+ -+ case VCHIQ_IOC_LIB_VERSION: { -+ unsigned int lib_version = (unsigned int)arg; -+ -+ if (lib_version < VCHIQ_VERSION_MIN) -+ ret = -EINVAL; -+ else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) -+ instance->use_close_delivered = 1; -+ } break; -+ -+ case VCHIQ_IOC_CLOSE_DELIVERED: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_closed_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ close_delivered(user_service); -+ } -+ else -+ ret = -EINVAL; -+ } break; -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ if (service) -+ unlock_service(service); -+ -+ if (ret == 0) { -+ if (status == VCHIQ_ERROR) -+ ret = -EIO; -+ else if (status == VCHIQ_RETRY) -+ ret = -EINTR; -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) && -+ (ret != -EWOULDBLOCK)) -+ vchiq_log_info(vchiq_arm_log_level, -+ " ioctl instance %lx, cmd %s -> status %d, %ld", -+ (unsigned long)instance, -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? -+ ioctl_names[_IOC_NR(cmd)] : -+ "", -+ status, ret); -+ else -+ vchiq_log_trace(vchiq_arm_log_level, -+ " ioctl instance %lx, cmd %s -> status %d, %ld", -+ (unsigned long)instance, -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? -+ ioctl_names[_IOC_NR(cmd)] : -+ "", -+ status, ret); -+ -+ return ret; -+} -+ -+/**************************************************************************** -+* -+* vchiq_open -+* -+***************************************************************************/ -+ -+static int -+vchiq_open(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode) & 0x0f; -+ vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); -+ switch (dev) { -+ case VCHIQ_MINOR: { -+ int ret; -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ VCHIQ_INSTANCE_T instance; -+ -+ if (!state) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq has no connection to VideoCore"); -+ return -ENOTCONN; -+ } -+ -+ instance = kzalloc(sizeof(*instance), GFP_KERNEL); -+ if (!instance) -+ return -ENOMEM; -+ -+ instance->state = state; -+ instance->pid = current->tgid; -+ -+ ret = vchiq_debugfs_add_instance(instance); -+ if (ret != 0) { -+ kfree(instance); -+ return ret; -+ } -+ -+ sema_init(&instance->insert_event, 0); -+ sema_init(&instance->remove_event, 0); -+ mutex_init(&instance->completion_mutex); -+ mutex_init(&instance->bulk_waiter_list_mutex); -+ INIT_LIST_HEAD(&instance->bulk_waiter_list); -+ -+ file->private_data = instance; -+ } break; -+ -+ default: -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unknown minor device: %d", dev); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vchiq_release -+* -+***************************************************************************/ -+ -+static int -+vchiq_release(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode) & 0x0f; -+ int ret = 0; -+ switch (dev) { -+ case VCHIQ_MINOR: { -+ VCHIQ_INSTANCE_T instance = file->private_data; -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq_release: instance=%lx", -+ (unsigned long)instance); -+ -+ if (!state) { -+ ret = -EPERM; -+ goto out; -+ } -+ -+ /* Ensure videocore is awake to allow termination. */ -+ vchiq_use_internal(instance->state, NULL, -+ USE_TYPE_VCHIQ); -+ -+ mutex_lock(&instance->completion_mutex); -+ -+ /* Wake the completion thread and ask it to exit */ -+ instance->closing = 1; -+ up(&instance->insert_event); -+ -+ mutex_unlock(&instance->completion_mutex); -+ -+ /* Wake the slot handler if the completion queue is full. */ -+ up(&instance->remove_event); -+ -+ /* Mark all services for termination... */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ USER_SERVICE_T *user_service = service->base.userdata; -+ -+ /* Wake the slot handler if the msg queue is full. */ -+ up(&user_service->remove_event); -+ -+ vchiq_terminate_service_internal(service); -+ unlock_service(service); -+ } -+ -+ /* ...and wait for them to die */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, &i)) -+ != NULL) { -+ USER_SERVICE_T *user_service = service->base.userdata; -+ -+ down(&service->remove_event); -+ -+ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); -+ -+ spin_lock(&msg_queue_spinlock); -+ -+ while (user_service->msg_remove != -+ user_service->msg_insert) { -+ VCHIQ_HEADER_T *header = user_service-> -+ msg_queue[user_service->msg_remove & -+ (MSG_QUEUE_SIZE - 1)]; -+ user_service->msg_remove++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ if (header) -+ vchiq_release_message( -+ service->handle, -+ header); -+ spin_lock(&msg_queue_spinlock); -+ } -+ -+ spin_unlock(&msg_queue_spinlock); -+ -+ unlock_service(service); -+ } -+ -+ /* Release any closed services */ -+ while (instance->completion_remove != -+ instance->completion_insert) { -+ VCHIQ_COMPLETION_DATA_T *completion; -+ VCHIQ_SERVICE_T *service; -+ completion = &instance->completions[ -+ instance->completion_remove & -+ (MAX_COMPLETIONS - 1)]; -+ service = completion->service_userdata; -+ if (completion->reason == VCHIQ_SERVICE_CLOSED) -+ { -+ USER_SERVICE_T *user_service = -+ service->base.userdata; -+ -+ /* Wake any blocked user-thread */ -+ if (instance->use_close_delivered) -+ up(&user_service->close_event); -+ unlock_service(service); -+ } -+ instance->completion_remove++; -+ } -+ -+ /* Release the PEER service count. */ -+ vchiq_release_internal(instance->state, NULL); -+ -+ { -+ struct list_head *pos, *next; -+ list_for_each_safe(pos, next, -+ &instance->bulk_waiter_list) { -+ struct bulk_waiter_node *waiter; -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ vchiq_log_info(vchiq_arm_log_level, -+ "bulk_waiter - cleaned up %x " -+ "for pid %d", -+ (unsigned int)waiter, waiter->pid); -+ kfree(waiter); -+ } -+ } -+ -+ vchiq_debugfs_remove_instance(instance); -+ -+ kfree(instance); -+ file->private_data = NULL; -+ } break; -+ -+ default: -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unknown minor device: %d", dev); -+ ret = -ENXIO; -+ } -+ -+out: -+ return ret; -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump -+* -+***************************************************************************/ -+ -+void -+vchiq_dump(void *dump_context, const char *str, int len) -+{ -+ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; -+ -+ if (context->actual < context->space) { -+ int copy_bytes; -+ if (context->offset > 0) { -+ int skip_bytes = min(len, (int)context->offset); -+ str += skip_bytes; -+ len -= skip_bytes; -+ context->offset -= skip_bytes; -+ if (context->offset > 0) -+ return; -+ } -+ copy_bytes = min(len, (int)(context->space - context->actual)); -+ if (copy_bytes == 0) -+ return; -+ if (copy_to_user(context->buf + context->actual, str, -+ copy_bytes)) -+ context->actual = -EFAULT; -+ context->actual += copy_bytes; -+ len -= copy_bytes; -+ -+ /* If tne terminating NUL is included in the length, then it -+ ** marks the end of a line and should be replaced with a -+ ** carriage return. */ -+ if ((len == 0) && (str[copy_bytes - 1] == '\0')) { -+ char cr = '\n'; -+ if (copy_to_user(context->buf + context->actual - 1, -+ &cr, 1)) -+ context->actual = -EFAULT; -+ } -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump_platform_instance_state -+* -+***************************************************************************/ -+ -+void -+vchiq_dump_platform_instances(void *dump_context) -+{ -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ char buf[80]; -+ int len; -+ int i; -+ -+ /* There is no list of instances, so instead scan all services, -+ marking those that have been dumped. */ -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ VCHIQ_INSTANCE_T instance; -+ -+ if (service && (service->base.callback == service_callback)) { -+ instance = service->instance; -+ if (instance) -+ instance->mark = 0; -+ } -+ } -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ VCHIQ_INSTANCE_T instance; -+ -+ if (service && (service->base.callback == service_callback)) { -+ instance = service->instance; -+ if (instance && !instance->mark) { -+ len = snprintf(buf, sizeof(buf), -+ "Instance %x: pid %d,%s completions " -+ "%d/%d", -+ (unsigned int)instance, instance->pid, -+ instance->connected ? " connected, " : -+ "", -+ instance->completion_insert - -+ instance->completion_remove, -+ MAX_COMPLETIONS); -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ instance->mark = 1; -+ } -+ } -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump_platform_service_state -+* -+***************************************************************************/ -+ -+void -+vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) -+{ -+ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; -+ char buf[80]; -+ int len; -+ -+ len = snprintf(buf, sizeof(buf), " instance %x", -+ (unsigned int)service->instance); -+ -+ if ((service->base.callback == service_callback) && -+ user_service->is_vchi) { -+ len += snprintf(buf + len, sizeof(buf) - len, -+ ", %d/%d messages", -+ user_service->msg_insert - user_service->msg_remove, -+ MSG_QUEUE_SIZE); -+ -+ if (user_service->dequeue_pending) -+ len += snprintf(buf + len, sizeof(buf) - len, -+ " (dequeue pending)"); -+ } -+ -+ vchiq_dump(dump_context, buf, len + 1); -+} -+ -+/**************************************************************************** -+* -+* dump_user_mem -+* -+***************************************************************************/ -+ -+static void -+dump_phys_mem(void *virt_addr, uint32_t num_bytes) -+{ -+ int rc; -+ uint8_t *end_virt_addr = virt_addr + num_bytes; -+ int num_pages; -+ int offset; -+ int end_offset; -+ int page_idx; -+ int prev_idx; -+ struct page *page; -+ struct page **pages; -+ uint8_t *kmapped_virt_ptr; -+ -+ /* Align virtAddr and endVirtAddr to 16 byte boundaries. */ -+ -+ virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL); -+ end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) & -+ ~0x0fuL); -+ -+ offset = (int)(long)virt_addr & (PAGE_SIZE - 1); -+ end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); -+ -+ num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; -+ -+ pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); -+ if (pages == NULL) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocation memory for %d pages\n", -+ num_pages); -+ return; -+ } -+ -+ down_read(¤t->mm->mmap_sem); -+ rc = get_user_pages(current, /* task */ -+ current->mm, /* mm */ -+ (unsigned long)virt_addr, /* start */ -+ num_pages, /* len */ -+ 0, /* write */ -+ 0, /* force */ -+ pages, /* pages (array of page pointers) */ -+ NULL); /* vmas */ -+ up_read(¤t->mm->mmap_sem); -+ -+ prev_idx = -1; -+ page = NULL; -+ -+ while (offset < end_offset) { -+ -+ int page_offset = offset % PAGE_SIZE; -+ page_idx = offset / PAGE_SIZE; -+ -+ if (page_idx != prev_idx) { -+ -+ if (page != NULL) -+ kunmap(page); -+ page = pages[page_idx]; -+ kmapped_virt_ptr = kmap(page); -+ -+ prev_idx = page_idx; -+ } -+ -+ if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE) -+ vchiq_log_dump_mem("ph", -+ (uint32_t)(unsigned long)&kmapped_virt_ptr[ -+ page_offset], -+ &kmapped_virt_ptr[page_offset], 16); -+ -+ offset += 16; -+ } -+ if (page != NULL) -+ kunmap(page); -+ -+ for (page_idx = 0; page_idx < num_pages; page_idx++) -+ page_cache_release(pages[page_idx]); -+ -+ kfree(pages); -+} -+ -+/**************************************************************************** -+* -+* vchiq_read -+* -+***************************************************************************/ -+ -+static ssize_t -+vchiq_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ DUMP_CONTEXT_T context; -+ context.buf = buf; -+ context.actual = 0; -+ context.space = count; -+ context.offset = *ppos; -+ -+ vchiq_dump_state(&context, &g_state); -+ -+ *ppos += context.actual; -+ -+ return context.actual; -+} -+ -+VCHIQ_STATE_T * -+vchiq_get_state(void) -+{ -+ -+ if (g_state.remote == NULL) -+ printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); -+ else if (g_state.remote->initialised != 1) -+ printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", -+ __func__, g_state.remote->initialised); -+ -+ return ((g_state.remote != NULL) && -+ (g_state.remote->initialised == 1)) ? &g_state : NULL; -+} -+ -+static const struct file_operations -+vchiq_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = vchiq_ioctl, -+ .open = vchiq_open, -+ .release = vchiq_release, -+ .read = vchiq_read -+}; -+ -+/* -+ * Autosuspend related functionality -+ */ -+ -+int -+vchiq_videocore_wanted(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ if (!arm_state) -+ /* autosuspend not supported - always return wanted */ -+ return 1; -+ else if (arm_state->blocked_count) -+ return 1; -+ else if (!arm_state->videocore_use_count) -+ /* usage count zero - check for override unless we're forcing */ -+ if (arm_state->resume_blocked) -+ return 0; -+ else -+ return vchiq_platform_videocore_wanted(state); -+ else -+ /* non-zero usage count - videocore still required */ -+ return 1; -+} -+ -+static VCHIQ_STATUS_T -+vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, -+ VCHIQ_SERVICE_HANDLE_T service_user, -+ void *bulk_user) -+{ -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s callback reason %d", __func__, reason); -+ return 0; -+} -+ -+static int -+vchiq_keepalive_thread_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ VCHIQ_STATUS_T status; -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_SERVICE_HANDLE_T ka_handle; -+ -+ VCHIQ_SERVICE_PARAMS_T params = { -+ .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'), -+ .callback = vchiq_keepalive_vchiq_callback, -+ .version = KEEPALIVE_VER, -+ .version_min = KEEPALIVE_VER_MIN -+ }; -+ -+ status = vchiq_initialise(&instance); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_initialise failed %d", __func__, status); -+ goto exit; -+ } -+ -+ status = vchiq_connect(instance); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_connect failed %d", __func__, status); -+ goto shutdown; -+ } -+ -+ status = vchiq_add_service(instance, ¶ms, &ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_open_service failed %d", __func__, status); -+ goto shutdown; -+ } -+ -+ while (1) { -+ long rc = 0, uc = 0; -+ if (wait_for_completion_interruptible(&arm_state->ka_evt) -+ != 0) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s interrupted", __func__); -+ flush_signals(current); -+ continue; -+ } -+ -+ /* read and clear counters. Do release_count then use_count to -+ * prevent getting more releases than uses */ -+ rc = atomic_xchg(&arm_state->ka_release_count, 0); -+ uc = atomic_xchg(&arm_state->ka_use_count, 0); -+ -+ /* Call use/release service the requisite number of times. -+ * Process use before release so use counts don't go negative */ -+ while (uc--) { -+ atomic_inc(&arm_state->ka_use_ack_count); -+ status = vchiq_use_service(ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_use_service error %d", -+ __func__, status); -+ } -+ } -+ while (rc--) { -+ status = vchiq_release_service(ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_release_service error %d", -+ __func__, status); -+ } -+ } -+ } -+ -+shutdown: -+ vchiq_shutdown(instance); -+exit: -+ return 0; -+} -+ -+ -+ -+VCHIQ_STATUS_T -+vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (arm_state) { -+ rwlock_init(&arm_state->susp_res_lock); -+ -+ init_completion(&arm_state->ka_evt); -+ atomic_set(&arm_state->ka_use_count, 0); -+ atomic_set(&arm_state->ka_use_ack_count, 0); -+ atomic_set(&arm_state->ka_release_count, 0); -+ -+ init_completion(&arm_state->vc_suspend_complete); -+ -+ init_completion(&arm_state->vc_resume_complete); -+ /* Initialise to 'done' state. We only want to block on resume -+ * completion while videocore is suspended. */ -+ set_resume_state(arm_state, VC_RESUME_RESUMED); -+ -+ init_completion(&arm_state->resume_blocker); -+ /* Initialise to 'done' state. We only want to block on this -+ * completion while resume is blocked */ -+ complete_all(&arm_state->resume_blocker); -+ -+ init_completion(&arm_state->blocked_blocker); -+ /* Initialise to 'done' state. We only want to block on this -+ * completion while things are waiting on the resume blocker */ -+ complete_all(&arm_state->blocked_blocker); -+ -+ arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; -+ arm_state->suspend_timer_running = 0; -+ init_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer.data = (unsigned long)(state); -+ arm_state->suspend_timer.function = suspend_timer_callback; -+ -+ arm_state->first_connect = 0; -+ -+ } -+ return status; -+} -+ -+/* -+** Functions to modify the state variables; -+** set_suspend_state -+** set_resume_state -+** -+** There are more state variables than we might like, so ensure they remain in -+** step. Suspend and resume state are maintained separately, since most of -+** these state machines can operate independently. However, there are a few -+** states where state transitions in one state machine cause a reset to the -+** other state machine. In addition, there are some completion events which -+** need to occur on state machine reset and end-state(s), so these are also -+** dealt with in these functions. -+** -+** In all states we set the state variable according to the input, but in some -+** cases we perform additional steps outlined below; -+** -+** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. -+** The suspend completion is completed after any suspend -+** attempt. When we reset the state machine we also reset -+** the completion. This reset occurs when videocore is -+** resumed, and also if we initiate suspend after a suspend -+** failure. -+** -+** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for -+** suspend - ie from this point on we must try to suspend -+** before resuming can occur. We therefore also reset the -+** resume state machine to VC_RESUME_IDLE in this state. -+** -+** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call -+** complete_all on the suspend completion to notify -+** anything waiting for suspend to happen. -+** -+** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also -+** initiate resume, so no need to alter resume state. -+** We call complete_all on the suspend completion to notify -+** of suspend rejection. -+** -+** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the -+** suspend completion and reset the resume state machine. -+** -+** VC_RESUME_IDLE - Initialise the resume completion at the same time. The -+** resume completion is in it's 'done' state whenever -+** videcore is running. Therfore, the VC_RESUME_IDLE state -+** implies that videocore is suspended. -+** Hence, any thread which needs to wait until videocore is -+** running can wait on this completion - it will only block -+** if videocore is suspended. -+** -+** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. -+** Call complete_all on the resume completion to unblock -+** any threads waiting for resume. Also reset the suspend -+** state machine to it's idle state. -+** -+** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. -+*/ -+ -+inline void -+set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_suspend_status new_state) -+{ -+ /* set the state in all cases */ -+ arm_state->vc_suspend_state = new_state; -+ -+ /* state specific additional actions */ -+ switch (new_state) { -+ case VC_SUSPEND_FORCE_CANCELED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_REJECTED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_FAILED: -+ complete_all(&arm_state->vc_suspend_complete); -+ arm_state->vc_resume_state = VC_RESUME_RESUMED; -+ complete_all(&arm_state->vc_resume_complete); -+ break; -+ case VC_SUSPEND_IDLE: -+ reinit_completion(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_REQUESTED: -+ break; -+ case VC_SUSPEND_IN_PROGRESS: -+ set_resume_state(arm_state, VC_RESUME_IDLE); -+ break; -+ case VC_SUSPEND_SUSPENDED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ default: -+ BUG(); -+ break; -+ } -+} -+ -+inline void -+set_resume_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_resume_status new_state) -+{ -+ /* set the state in all cases */ -+ arm_state->vc_resume_state = new_state; -+ -+ /* state specific additional actions */ -+ switch (new_state) { -+ case VC_RESUME_FAILED: -+ break; -+ case VC_RESUME_IDLE: -+ reinit_completion(&arm_state->vc_resume_complete); -+ break; -+ case VC_RESUME_REQUESTED: -+ break; -+ case VC_RESUME_IN_PROGRESS: -+ break; -+ case VC_RESUME_RESUMED: -+ complete_all(&arm_state->vc_resume_complete); -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ break; -+ default: -+ BUG(); -+ break; -+ } -+} -+ -+ -+/* should be called with the write lock held */ -+inline void -+start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ del_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer.expires = jiffies + -+ msecs_to_jiffies(arm_state-> -+ suspend_timer_timeout); -+ add_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer_running = 1; -+} -+ -+/* should be called with the write lock held */ -+static inline void -+stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ if (arm_state->suspend_timer_running) { -+ del_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer_running = 0; -+ } -+} -+ -+static inline int -+need_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && -+ (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && -+ vchiq_videocore_wanted(state); -+} -+ -+static int -+block_resume(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ int status = VCHIQ_SUCCESS; -+ const unsigned long timeout_val = -+ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS); -+ int resume_count = 0; -+ -+ /* Allow any threads which were blocked by the last force suspend to -+ * complete if they haven't already. Only give this one shot; if -+ * blocked_count is incremented after blocked_blocker is completed -+ * (which only happens when blocked_count hits 0) then those threads -+ * will have to wait until next time around */ -+ if (arm_state->blocked_count) { -+ reinit_completion(&arm_state->blocked_blocker); -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " -+ "blocked clients", __func__); -+ if (wait_for_completion_interruptible_timeout( -+ &arm_state->blocked_blocker, timeout_val) -+ <= 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s wait for " -+ "previously blocked clients failed" , __func__); -+ status = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s previously blocked " -+ "clients resumed", __func__); -+ write_lock_bh(&arm_state->susp_res_lock); -+ } -+ -+ /* We need to wait for resume to complete if it's in process */ -+ while (arm_state->vc_resume_state != VC_RESUME_RESUMED && -+ arm_state->vc_resume_state > VC_RESUME_IDLE) { -+ if (resume_count > 1) { -+ status = VCHIQ_ERROR; -+ vchiq_log_error(vchiq_susp_log_level, "%s waited too " -+ "many times for resume" , __func__); -+ goto out; -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s wait for resume", -+ __func__); -+ if (wait_for_completion_interruptible_timeout( -+ &arm_state->vc_resume_complete, timeout_val) -+ <= 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s wait for " -+ "resume failed (%s)", __func__, -+ resume_state_names[arm_state->vc_resume_state + -+ VC_RESUME_NUM_OFFSET]); -+ status = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__); -+ write_lock_bh(&arm_state->susp_res_lock); -+ resume_count++; -+ } -+ reinit_completion(&arm_state->resume_blocker); -+ arm_state->resume_blocked = 1; -+ -+out: -+ return status; -+} -+ -+static inline void -+unblock_resume(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ complete_all(&arm_state->resume_blocker); -+ arm_state->resume_blocked = 0; -+} -+ -+/* Initiate suspend via slot handler. Should be called with the write lock -+ * held */ -+VCHIQ_STATUS_T -+vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ status = VCHIQ_SUCCESS; -+ -+ -+ switch (arm_state->vc_suspend_state) { -+ case VC_SUSPEND_REQUESTED: -+ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " -+ "requested", __func__); -+ break; -+ case VC_SUSPEND_IN_PROGRESS: -+ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " -+ "progress", __func__); -+ break; -+ -+ default: -+ /* We don't expect to be in other states, so log but continue -+ * anyway */ -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s unexpected suspend state %s", __func__, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ /* fall through */ -+ case VC_SUSPEND_REJECTED: -+ case VC_SUSPEND_FAILED: -+ /* Ensure any idle state actions have been run */ -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ /* fall through */ -+ case VC_SUSPEND_IDLE: -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: suspending", __func__); -+ set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); -+ /* kick the slot handler thread to initiate suspend */ -+ request_poll(state, NULL, 0); -+ break; -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); -+ return status; -+} -+ -+void -+vchiq_platform_check_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int susp = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && -+ arm_state->vc_resume_state == VC_RESUME_RESUMED) { -+ set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); -+ susp = 1; -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (susp) -+ vchiq_platform_suspend(state); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+} -+ -+ -+static void -+output_timeout_error(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ char service_err[50] = ""; -+ int vc_use_count = arm_state->videocore_use_count; -+ int active_services = state->unused_service; -+ int i; -+ -+ if (!arm_state->videocore_use_count) { -+ snprintf(service_err, 50, " Videocore usecount is 0"); -+ goto output_msg; -+ } -+ for (i = 0; i < active_services; i++) { -+ VCHIQ_SERVICE_T *service_ptr = state->services[i]; -+ if (service_ptr && service_ptr->service_use_count && -+ (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { -+ snprintf(service_err, 50, " %c%c%c%c(%d) service has " -+ "use count %d%s", VCHIQ_FOURCC_AS_4CHARS( -+ service_ptr->base.fourcc), -+ service_ptr->client_id, -+ service_ptr->service_use_count, -+ service_ptr->service_use_count == -+ vc_use_count ? "" : " (+ more)"); -+ break; -+ } -+ } -+ -+output_msg: -+ vchiq_log_error(vchiq_susp_log_level, -+ "timed out waiting for vc suspend (%d).%s", -+ arm_state->autosuspend_override, service_err); -+ -+} -+ -+/* Try to get videocore into suspended state, regardless of autosuspend state. -+** We don't actually force suspend, since videocore may get into a bad state -+** if we force suspend at a bad time. Instead, we wait for autosuspend to -+** determine a good point to suspend. If this doesn't happen within 100ms we -+** report failure. -+** -+** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if -+** videocore failed to suspend in time or VCHIQ_ERROR if interrupted. -+*/ -+VCHIQ_STATUS_T -+vchiq_arm_force_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ long rc = 0; -+ int repeat = -1; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ -+ status = block_resume(arm_state); -+ if (status != VCHIQ_SUCCESS) -+ goto unlock; -+ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { -+ /* Already suspended - just block resume and exit */ -+ vchiq_log_info(vchiq_susp_log_level, "%s already suspended", -+ __func__); -+ status = VCHIQ_SUCCESS; -+ goto unlock; -+ } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) { -+ /* initiate suspend immediately in the case that we're waiting -+ * for the timeout */ -+ stop_suspend_timer(arm_state); -+ if (!vchiq_videocore_wanted(state)) { -+ vchiq_log_info(vchiq_susp_log_level, "%s videocore " -+ "idle, initiating suspend", __func__); -+ status = vchiq_arm_vcsuspend(state); -+ } else if (arm_state->autosuspend_override < -+ FORCE_SUSPEND_FAIL_MAX) { -+ vchiq_log_info(vchiq_susp_log_level, "%s letting " -+ "videocore go idle", __func__); -+ status = VCHIQ_SUCCESS; -+ } else { -+ vchiq_log_warning(vchiq_susp_log_level, "%s failed too " -+ "many times - attempting suspend", __func__); -+ status = vchiq_arm_vcsuspend(state); -+ } -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend " -+ "in progress - wait for completion", __func__); -+ status = VCHIQ_SUCCESS; -+ } -+ -+ /* Wait for suspend to happen due to system idle (not forced..) */ -+ if (status != VCHIQ_SUCCESS) -+ goto unblock_resume; -+ -+ do { -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ rc = wait_for_completion_interruptible_timeout( -+ &arm_state->vc_suspend_complete, -+ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS)); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (rc < 0) { -+ vchiq_log_warning(vchiq_susp_log_level, "%s " -+ "interrupted waiting for suspend", __func__); -+ status = VCHIQ_ERROR; -+ goto unblock_resume; -+ } else if (rc == 0) { -+ if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) { -+ /* Repeat timeout once if in progress */ -+ if (repeat < 0) { -+ repeat = 1; -+ continue; -+ } -+ } -+ arm_state->autosuspend_override++; -+ output_timeout_error(state); -+ -+ status = VCHIQ_RETRY; -+ goto unblock_resume; -+ } -+ } while (0 < (repeat--)); -+ -+ /* Check and report state in case we need to abort ARM suspend */ -+ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) { -+ status = VCHIQ_RETRY; -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s videocore suspend failed (state %s)", __func__, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ /* Reset the state only if it's still in an error state. -+ * Something could have already initiated another suspend. */ -+ if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE) -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ -+ goto unblock_resume; -+ } -+ -+ /* successfully suspended - unlock and exit */ -+ goto unlock; -+ -+unblock_resume: -+ /* all error states need to unblock resume before exit */ -+ unblock_resume(arm_state); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); -+ return status; -+} -+ -+void -+vchiq_check_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && -+ arm_state->first_connect && -+ !vchiq_videocore_wanted(state)) { -+ vchiq_arm_vcsuspend(state); -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+} -+ -+ -+int -+vchiq_arm_allow_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int resume = 0; -+ int ret = -1; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ unblock_resume(arm_state); -+ resume = vchiq_check_resume(state); -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (resume) { -+ if (wait_for_completion_interruptible( -+ &arm_state->vc_resume_complete) < 0) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s interrupted", __func__); -+ /* failed, cannot accurately derive suspend -+ * state, so exit early. */ -+ goto out; -+ } -+ } -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: Videocore remains suspended", __func__); -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: Videocore resumed", __func__); -+ ret = 0; -+ } -+ read_unlock_bh(&arm_state->susp_res_lock); -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+/* This function should be called with the write lock held */ -+int -+vchiq_check_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int resume = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (need_resume(state)) { -+ set_resume_state(arm_state, VC_RESUME_REQUESTED); -+ request_poll(state, NULL, 0); -+ resume = 1; -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return resume; -+} -+ -+void -+vchiq_platform_check_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int res = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->wake_address == 0) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: already awake", __func__); -+ goto unlock; -+ } -+ if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: already resuming", __func__); -+ goto unlock; -+ } -+ -+ if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) { -+ set_resume_state(arm_state, VC_RESUME_IN_PROGRESS); -+ res = 1; -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s: not resuming (resume state %s)", __func__, -+ resume_state_names[arm_state->vc_resume_state + -+ VC_RESUME_NUM_OFFSET]); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (res) -+ vchiq_platform_resume(state); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+ -+} -+ -+ -+ -+VCHIQ_STATUS_T -+vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ enum USE_TYPE_E use_type) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; -+ char entity[16]; -+ int *entity_uc; -+ int local_uc, local_entity_uc; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (use_type == USE_TYPE_VCHIQ) { -+ sprintf(entity, "VCHIQ: "); -+ entity_uc = &arm_state->peer_use_count; -+ } else if (service) { -+ sprintf(entity, "%c%c%c%c:%03d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id); -+ entity_uc = &service->service_use_count; -+ } else { -+ vchiq_log_error(vchiq_susp_log_level, "%s null service " -+ "ptr", __func__); -+ ret = VCHIQ_ERROR; -+ goto out; -+ } -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ while (arm_state->resume_blocked) { -+ /* If we call 'use' while force suspend is waiting for suspend, -+ * then we're about to block the thread which the force is -+ * waiting to complete, so we're bound to just time out. In this -+ * case, set the suspend state such that the wait will be -+ * canceled, so we can complete as quickly as possible. */ -+ if (arm_state->resume_blocked && arm_state->vc_suspend_state == -+ VC_SUSPEND_IDLE) { -+ set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); -+ break; -+ } -+ /* If suspend is already in progress then we need to block */ -+ if (!try_wait_for_completion(&arm_state->resume_blocker)) { -+ /* Indicate that there are threads waiting on the resume -+ * blocker. These need to be allowed to complete before -+ * a _second_ call to force suspend can complete, -+ * otherwise low priority threads might never actually -+ * continue */ -+ arm_state->blocked_count++; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " -+ "blocked - waiting...", __func__, entity); -+ if (wait_for_completion_killable( -+ &arm_state->resume_blocker) != 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s %s " -+ "wait for resume blocker interrupted", -+ __func__, entity); -+ ret = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ arm_state->blocked_count--; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " -+ "unblocked", __func__, entity); -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (--arm_state->blocked_count == 0) -+ complete_all(&arm_state->blocked_blocker); -+ } -+ } -+ -+ stop_suspend_timer(arm_state); -+ -+ local_uc = ++arm_state->videocore_use_count; -+ local_entity_uc = ++(*entity_uc); -+ -+ /* If there's a pending request which hasn't yet been serviced then -+ * just clear it. If we're past VC_SUSPEND_REQUESTED state then -+ * vc_resume_complete will block until we either resume or fail to -+ * suspend */ -+ if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ -+ if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { -+ set_resume_state(arm_state, VC_RESUME_REQUESTED); -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, local_entity_uc, local_uc); -+ request_poll(state, NULL, 0); -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, *entity_uc, local_uc); -+ -+ -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ /* Completion is in a done state when we're not suspended, so this won't -+ * block for the non-suspended case. */ -+ if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { -+ vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", -+ __func__, entity); -+ if (wait_for_completion_killable( -+ &arm_state->vc_resume_complete) != 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " -+ "resume interrupted", __func__, entity); -+ ret = VCHIQ_ERROR; -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, -+ entity); -+ } -+ -+ if (ret == VCHIQ_SUCCESS) { -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); -+ while (ack_cnt && (status == VCHIQ_SUCCESS)) { -+ /* Send the use notify to videocore */ -+ status = vchiq_send_remote_use_active(state); -+ if (status == VCHIQ_SUCCESS) -+ ack_cnt--; -+ else -+ atomic_add(ack_cnt, -+ &arm_state->ka_use_ack_count); -+ } -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; -+ char entity[16]; -+ int *entity_uc; -+ int local_uc, local_entity_uc; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (service) { -+ sprintf(entity, "%c%c%c%c:%03d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id); -+ entity_uc = &service->service_use_count; -+ } else { -+ sprintf(entity, "PEER: "); -+ entity_uc = &arm_state->peer_use_count; -+ } -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (!arm_state->videocore_use_count || !(*entity_uc)) { -+ /* Don't use BUG_ON - don't allow user thread to crash kernel */ -+ WARN_ON(!arm_state->videocore_use_count); -+ WARN_ON(!(*entity_uc)); -+ ret = VCHIQ_ERROR; -+ goto unlock; -+ } -+ local_uc = --arm_state->videocore_use_count; -+ local_entity_uc = --(*entity_uc); -+ -+ if (!vchiq_videocore_wanted(state)) { -+ if (vchiq_platform_use_suspend_timer() && -+ !arm_state->resume_blocked) { -+ /* Only use the timer if we're not trying to force -+ * suspend (=> resume_blocked) */ -+ start_suspend_timer(arm_state); -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s %s count %d, state count %d - suspending", -+ __func__, entity, *entity_uc, -+ arm_state->videocore_use_count); -+ vchiq_arm_vcsuspend(state); -+ } -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, *entity_uc, -+ arm_state->videocore_use_count); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+void -+vchiq_on_remote_use(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ atomic_inc(&arm_state->ka_use_count); -+ complete(&arm_state->ka_evt); -+} -+ -+void -+vchiq_on_remote_release(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ atomic_inc(&arm_state->ka_release_count); -+ complete(&arm_state->ka_evt); -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ return vchiq_release_internal(service->state, service); -+} -+ -+VCHIQ_DEBUGFS_NODE_T * -+vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance) -+{ -+ return &instance->debugfs_node; -+} -+ -+int -+vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int use_count = 0, i; -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ use_count += service->service_use_count; -+ unlock_service(service); -+ } -+ return use_count; -+} -+ -+int -+vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->pid; -+} -+ -+int -+vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->trace; -+} -+ -+void -+vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ service->trace = trace; -+ unlock_service(service); -+ } -+ instance->trace = (trace != 0); -+} -+ -+static void suspend_timer_callback(unsigned long context) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ if (!arm_state) -+ goto out; -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s - suspend timer expired - check suspend", __func__); -+ vchiq_check_suspend(state); -+out: -+ return; -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_use_internal(service->state, service, -+ USE_TYPE_SERVICE_NO_RESUME); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_use_internal(service->state, service, -+ USE_TYPE_SERVICE); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_release_internal(service->state, service); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+void -+vchiq_dump_service_use_state(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int i, j = 0; -+ /* Only dump 64 services */ -+ static const int local_max_services = 64; -+ /* If there's more than 64 services, only dump ones with -+ * non-zero counts */ -+ int only_nonzero = 0; -+ static const char *nz = "<-- preventing suspend"; -+ -+ enum vc_suspend_status vc_suspend_state; -+ enum vc_resume_status vc_resume_state; -+ int peer_count; -+ int vc_use_count; -+ int active_services; -+ struct service_data_struct { -+ int fourcc; -+ int clientid; -+ int use_count; -+ } service_data[local_max_services]; -+ -+ if (!arm_state) -+ return; -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ vc_suspend_state = arm_state->vc_suspend_state; -+ vc_resume_state = arm_state->vc_resume_state; -+ peer_count = arm_state->peer_use_count; -+ vc_use_count = arm_state->videocore_use_count; -+ active_services = state->unused_service; -+ if (active_services > local_max_services) -+ only_nonzero = 1; -+ -+ for (i = 0; (i < active_services) && (j < local_max_services); i++) { -+ VCHIQ_SERVICE_T *service_ptr = state->services[i]; -+ if (!service_ptr) -+ continue; -+ -+ if (only_nonzero && !service_ptr->service_use_count) -+ continue; -+ -+ if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) { -+ service_data[j].fourcc = service_ptr->base.fourcc; -+ service_data[j].clientid = service_ptr->client_id; -+ service_data[j++].use_count = service_ptr-> -+ service_use_count; -+ } -+ } -+ -+ read_unlock_bh(&arm_state->susp_res_lock); -+ -+ vchiq_log_warning(vchiq_susp_log_level, -+ "-- Videcore suspend state: %s --", -+ suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); -+ vchiq_log_warning(vchiq_susp_log_level, -+ "-- Videcore resume state: %s --", -+ resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); -+ -+ if (only_nonzero) -+ vchiq_log_warning(vchiq_susp_log_level, "Too many active " -+ "services (%d). Only dumping up to first %d services " -+ "with non-zero use-count", active_services, -+ local_max_services); -+ -+ for (i = 0; i < j; i++) { -+ vchiq_log_warning(vchiq_susp_log_level, -+ "----- %c%c%c%c:%d service count %d %s", -+ VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc), -+ service_data[i].clientid, -+ service_data[i].use_count, -+ service_data[i].use_count ? nz : ""); -+ } -+ vchiq_log_warning(vchiq_susp_log_level, -+ "----- VCHIQ use count count %d", peer_count); -+ vchiq_log_warning(vchiq_susp_log_level, -+ "--- Overall vchiq instance use count %d", vc_use_count); -+ -+ vchiq_dump_platform_use_state(state); -+} -+ -+VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_ARM_STATE_T *arm_state; -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ -+ if (!service || !service->state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ arm_state = vchiq_platform_get_arm_state(service->state); -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ if (service->service_use_count) -+ ret = VCHIQ_SUCCESS; -+ read_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (ret == VCHIQ_ERROR) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s ERROR - %c%c%c%c:%d service count %d, " -+ "state count %d, videocore suspend state %s", __func__, -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id, service->service_use_count, -+ arm_state->videocore_use_count, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ vchiq_dump_service_use_state(service->state); -+ } -+out: -+ return ret; -+} -+ -+/* stub functions */ -+void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) -+{ -+ (void)state; -+} -+ -+void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, -+ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, -+ get_conn_state_name(oldstate), get_conn_state_name(newstate)); -+ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (!arm_state->first_connect) { -+ char threadname[10]; -+ arm_state->first_connect = 1; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ snprintf(threadname, sizeof(threadname), "VCHIQka-%d", -+ state->id); -+ arm_state->ka_thread = kthread_create( -+ &vchiq_keepalive_thread_func, -+ (void *)state, -+ threadname); -+ if (arm_state->ka_thread == NULL) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "vchiq: FATAL: couldn't create thread %s", -+ threadname); -+ } else { -+ wake_up_process(arm_state->ka_thread); -+ } -+ } else -+ write_unlock_bh(&arm_state->susp_res_lock); -+ } -+} -+ -+ -+/**************************************************************************** -+* -+* vchiq_init - called when the module is loaded. -+* -+***************************************************************************/ -+ -+static int __init -+vchiq_init(void) -+{ -+ int err; -+ void *ptr_err; -+ -+ /* create debugfs entries */ -+ err = vchiq_debugfs_init(); -+ if (err != 0) -+ goto failed_debugfs_init; -+ -+ err = alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1, DEVICE_NAME); -+ if (err != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocate device number"); -+ goto failed_alloc_chrdev; -+ } -+ cdev_init(&vchiq_cdev, &vchiq_fops); -+ vchiq_cdev.owner = THIS_MODULE; -+ err = cdev_add(&vchiq_cdev, vchiq_devid, 1); -+ if (err != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to register device"); -+ goto failed_cdev_add; -+ } -+ -+ /* create sysfs entries */ -+ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); -+ ptr_err = vchiq_class; -+ if (IS_ERR(ptr_err)) -+ goto failed_class_create; -+ -+ vchiq_dev = device_create(vchiq_class, NULL, -+ vchiq_devid, NULL, "vchiq"); -+ ptr_err = vchiq_dev; -+ if (IS_ERR(ptr_err)) -+ goto failed_device_create; -+ -+ err = vchiq_platform_init(&g_state); -+ if (err != 0) -+ goto failed_platform_init; -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq: initialised - version %d (min %d), device %d.%d", -+ VCHIQ_VERSION, VCHIQ_VERSION_MIN, -+ MAJOR(vchiq_devid), MINOR(vchiq_devid)); -+ -+ return 0; -+ -+failed_platform_init: -+ device_destroy(vchiq_class, vchiq_devid); -+failed_device_create: -+ class_destroy(vchiq_class); -+failed_class_create: -+ cdev_del(&vchiq_cdev); -+ err = PTR_ERR(ptr_err); -+failed_cdev_add: -+ unregister_chrdev_region(vchiq_devid, 1); -+failed_alloc_chrdev: -+ vchiq_debugfs_deinit(); -+failed_debugfs_init: -+ vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq"); -+ return err; -+} -+ -+/**************************************************************************** -+* -+* vchiq_exit - called when the module is unloaded. -+* -+***************************************************************************/ -+ -+static void __exit -+vchiq_exit(void) -+{ -+ vchiq_platform_exit(&g_state); -+ device_destroy(vchiq_class, vchiq_devid); -+ class_destroy(vchiq_class); -+ cdev_del(&vchiq_cdev); -+ unregister_chrdev_region(vchiq_devid, 1); -+} -+ -+module_init(vchiq_init); -+module_exit(vchiq_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h -new file mode 100644 -index 0000000..d1e2741 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h -@@ -0,0 +1,223 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_ARM_H -+#define VCHIQ_ARM_H -+ -+#include -+#include -+#include -+#include "vchiq_core.h" -+#include "vchiq_debugfs.h" -+ -+ -+enum vc_suspend_status { -+ VC_SUSPEND_FORCE_CANCELED = -3, /* Force suspend canceled, too busy */ -+ VC_SUSPEND_REJECTED = -2, /* Videocore rejected suspend request */ -+ VC_SUSPEND_FAILED = -1, /* Videocore suspend failed */ -+ VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ -+ VC_SUSPEND_REQUESTED, /* User has requested suspend */ -+ VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ -+ VC_SUSPEND_SUSPENDED /* Videocore suspend succeeded */ -+}; -+ -+enum vc_resume_status { -+ VC_RESUME_FAILED = -1, /* Videocore resume failed */ -+ VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ -+ VC_RESUME_REQUESTED, /* User has requested resume */ -+ VC_RESUME_IN_PROGRESS, /* Slot handler has received resume request */ -+ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ -+}; -+ -+ -+enum USE_TYPE_E { -+ USE_TYPE_SERVICE, -+ USE_TYPE_SERVICE_NO_RESUME, -+ USE_TYPE_VCHIQ -+}; -+ -+ -+ -+typedef struct vchiq_arm_state_struct { -+ /* Keepalive-related data */ -+ struct task_struct *ka_thread; -+ struct completion ka_evt; -+ atomic_t ka_use_count; -+ atomic_t ka_use_ack_count; -+ atomic_t ka_release_count; -+ -+ struct completion vc_suspend_complete; -+ struct completion vc_resume_complete; -+ -+ rwlock_t susp_res_lock; -+ enum vc_suspend_status vc_suspend_state; -+ enum vc_resume_status vc_resume_state; -+ -+ unsigned int wake_address; -+ -+ struct timer_list suspend_timer; -+ int suspend_timer_timeout; -+ int suspend_timer_running; -+ -+ /* Global use count for videocore. -+ ** This is equal to the sum of the use counts for all services. When -+ ** this hits zero the videocore suspend procedure will be initiated. -+ */ -+ int videocore_use_count; -+ -+ /* Use count to track requests from videocore peer. -+ ** This use count is not associated with a service, so needs to be -+ ** tracked separately with the state. -+ */ -+ int peer_use_count; -+ -+ /* Flag to indicate whether resume is blocked. This happens when the -+ ** ARM is suspending -+ */ -+ struct completion resume_blocker; -+ int resume_blocked; -+ struct completion blocked_blocker; -+ int blocked_count; -+ -+ int autosuspend_override; -+ -+ /* Flag to indicate that the first vchiq connect has made it through. -+ ** This means that both sides should be fully ready, and we should -+ ** be able to suspend after this point. -+ */ -+ int first_connect; -+ -+ unsigned long long suspend_start_time; -+ unsigned long long sleep_start_time; -+ unsigned long long resume_start_time; -+ unsigned long long last_wake_time; -+ -+} VCHIQ_ARM_STATE_T; -+ -+extern int vchiq_arm_log_level; -+extern int vchiq_susp_log_level; -+ -+extern int __init -+vchiq_platform_init(VCHIQ_STATE_T *state); -+ -+extern void __exit -+vchiq_platform_exit(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATE_T * -+vchiq_get_state(void); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_vcsuspend(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_force_suspend(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_arm_allow_resume(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_vcresume(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state); -+ -+extern int -+vchiq_check_resume(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_check_suspend(VCHIQ_STATE_T *state); -+ VCHIQ_STATUS_T -+vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_STATUS_T -+vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_suspend(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_platform_videocore_wanted(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_platform_use_suspend_timer(void); -+ -+extern void -+vchiq_dump_platform_use_state(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump_service_use_state(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_ARM_STATE_T* -+vchiq_platform_get_arm_state(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_videocore_wanted(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ enum USE_TYPE_E use_type); -+extern VCHIQ_STATUS_T -+vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_DEBUGFS_NODE_T * -+vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance); -+ -+extern void -+vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace); -+ -+extern void -+set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_suspend_status new_state); -+ -+extern void -+set_resume_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_resume_status new_state); -+ -+extern void -+start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state); -+ -+ -+#endif /* VCHIQ_ARM_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h -new file mode 100644 -index 0000000..df64581 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h -@@ -0,0 +1,37 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+const char *vchiq_get_build_hostname(void); -+const char *vchiq_get_build_version(void); -+const char *vchiq_get_build_time(void); -+const char *vchiq_get_build_date(void); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -new file mode 100644 -index 0000000..c382740 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -@@ -0,0 +1,66 @@ -+/** -+ * Copyright (c) 2010-2014 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CFG_H -+#define VCHIQ_CFG_H -+ -+#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') -+/* The version of VCHIQ - change with any non-trivial change */ -+#define VCHIQ_VERSION 7 -+/* The minimum compatible version - update to match VCHIQ_VERSION with any -+** incompatible change */ -+#define VCHIQ_VERSION_MIN 3 -+ -+/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ -+#define VCHIQ_VERSION_LIB_VERSION 7 -+ -+/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ -+#define VCHIQ_VERSION_CLOSE_DELIVERED 7 -+ -+#define VCHIQ_MAX_STATES 1 -+#define VCHIQ_MAX_SERVICES 4096 -+#define VCHIQ_MAX_SLOTS 128 -+#define VCHIQ_MAX_SLOTS_PER_SIDE 64 -+ -+#define VCHIQ_NUM_CURRENT_BULKS 32 -+#define VCHIQ_NUM_SERVICE_BULKS 4 -+ -+#ifndef VCHIQ_ENABLE_DEBUG -+#define VCHIQ_ENABLE_DEBUG 1 -+#endif -+ -+#ifndef VCHIQ_ENABLE_STATS -+#define VCHIQ_ENABLE_STATS 1 -+#endif -+ -+#endif /* VCHIQ_CFG_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -new file mode 100644 -index 0000000..65f4b52 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -@@ -0,0 +1,119 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_connected.h" -+#include "vchiq_core.h" -+#include -+#include -+ -+#define MAX_CALLBACKS 10 -+ -+static int g_connected; -+static int g_num_deferred_callbacks; -+static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[MAX_CALLBACKS]; -+static int g_once_init; -+static struct mutex g_connected_mutex; -+ -+/**************************************************************************** -+* -+* Function to initialize our lock. -+* -+***************************************************************************/ -+ -+static void connected_init(void) -+{ -+ if (!g_once_init) { -+ mutex_init(&g_connected_mutex); -+ g_once_init = 1; -+ } -+} -+ -+/**************************************************************************** -+* -+* This function is used to defer initialization until the vchiq stack is -+* initialized. If the stack is already initialized, then the callback will -+* be made immediately, otherwise it will be deferred until -+* vchiq_call_connected_callbacks is called. -+* -+***************************************************************************/ -+ -+void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback) -+{ -+ connected_init(); -+ -+ if (mutex_lock_interruptible(&g_connected_mutex) != 0) -+ return; -+ -+ if (g_connected) -+ /* We're already connected. Call the callback immediately. */ -+ -+ callback(); -+ else { -+ if (g_num_deferred_callbacks >= MAX_CALLBACKS) -+ vchiq_log_error(vchiq_core_log_level, -+ "There already %d callback registered - " -+ "please increase MAX_CALLBACKS", -+ g_num_deferred_callbacks); -+ else { -+ g_deferred_callback[g_num_deferred_callbacks] = -+ callback; -+ g_num_deferred_callbacks++; -+ } -+ } -+ mutex_unlock(&g_connected_mutex); -+} -+ -+/**************************************************************************** -+* -+* This function is called by the vchiq stack once it has been connected to -+* the videocore and clients can start to use the stack. -+* -+***************************************************************************/ -+ -+void vchiq_call_connected_callbacks(void) -+{ -+ int i; -+ -+ connected_init(); -+ -+ if (mutex_lock_interruptible(&g_connected_mutex) != 0) -+ return; -+ -+ for (i = 0; i < g_num_deferred_callbacks; i++) -+ g_deferred_callback[i](); -+ -+ g_num_deferred_callbacks = 0; -+ g_connected = 1; -+ mutex_unlock(&g_connected_mutex); -+} -+EXPORT_SYMBOL(vchiq_add_connected_callback); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h -new file mode 100644 -index 0000000..863b3e3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h -@@ -0,0 +1,50 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CONNECTED_H -+#define VCHIQ_CONNECTED_H -+ -+/* ---- Include Files ----------------------------------------------------- */ -+ -+/* ---- Constants and Types ---------------------------------------------- */ -+ -+typedef void (*VCHIQ_CONNECTED_CALLBACK_T)(void); -+ -+/* ---- Variable Externs ------------------------------------------------- */ -+ -+/* ---- Function Prototypes ---------------------------------------------- */ -+ -+void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback); -+void vchiq_call_connected_callbacks(void); -+ -+#endif /* VCHIQ_CONNECTED_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -new file mode 100644 -index 0000000..f962027 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -0,0 +1,3861 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_core.h" -+ -+#define VCHIQ_SLOT_HANDLER_STACK 8192 -+ -+#define HANDLE_STATE_SHIFT 12 -+ -+#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index)) -+#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index)) -+#define SLOT_INDEX_FROM_DATA(state, data) \ -+ (((unsigned int)((char *)data - (char *)state->slot_data)) / \ -+ VCHIQ_SLOT_SIZE) -+#define SLOT_INDEX_FROM_INFO(state, info) \ -+ ((unsigned int)(info - state->slot_info)) -+#define SLOT_QUEUE_INDEX_FROM_POS(pos) \ -+ ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) -+ -+#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) -+ -+#define SRVTRACE_LEVEL(srv) \ -+ (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level) -+#define SRVTRACE_ENABLED(srv, lev) \ -+ (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev))) -+ -+struct vchiq_open_payload { -+ int fourcc; -+ int client_id; -+ short version; -+ short version_min; -+}; -+ -+struct vchiq_openack_payload { -+ short version; -+}; -+ -+/* we require this for consistency between endpoints */ -+vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); -+vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); -+vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS)); -+vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES)); -+vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN); -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; -+ -+static atomic_t pause_bulks_count = ATOMIC_INIT(0); -+ -+static DEFINE_SPINLOCK(service_spinlock); -+DEFINE_SPINLOCK(bulk_waiter_spinlock); -+DEFINE_SPINLOCK(quota_spinlock); -+ -+VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; -+static unsigned int handle_seq; -+ -+static const char *const srvstate_names[] = { -+ "FREE", -+ "HIDDEN", -+ "LISTENING", -+ "OPENING", -+ "OPEN", -+ "OPENSYNC", -+ "CLOSESENT", -+ "CLOSERECVD", -+ "CLOSEWAIT", -+ "CLOSED" -+}; -+ -+static const char *const reason_names[] = { -+ "SERVICE_OPENED", -+ "SERVICE_CLOSED", -+ "MESSAGE_AVAILABLE", -+ "BULK_TRANSMIT_DONE", -+ "BULK_RECEIVE_DONE", -+ "BULK_TRANSMIT_ABORTED", -+ "BULK_RECEIVE_ABORTED" -+}; -+ -+static const char *const conn_state_names[] = { -+ "DISCONNECTED", -+ "CONNECTING", -+ "CONNECTED", -+ "PAUSING", -+ "PAUSE_SENT", -+ "PAUSED", -+ "RESUMING", -+ "PAUSE_TIMEOUT", -+ "RESUME_TIMEOUT" -+}; -+ -+ -+static void -+release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header); -+ -+static const char *msg_type_str(unsigned int msg_type) -+{ -+ switch (msg_type) { -+ case VCHIQ_MSG_PADDING: return "PADDING"; -+ case VCHIQ_MSG_CONNECT: return "CONNECT"; -+ case VCHIQ_MSG_OPEN: return "OPEN"; -+ case VCHIQ_MSG_OPENACK: return "OPENACK"; -+ case VCHIQ_MSG_CLOSE: return "CLOSE"; -+ case VCHIQ_MSG_DATA: return "DATA"; -+ case VCHIQ_MSG_BULK_RX: return "BULK_RX"; -+ case VCHIQ_MSG_BULK_TX: return "BULK_TX"; -+ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE"; -+ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE"; -+ case VCHIQ_MSG_PAUSE: return "PAUSE"; -+ case VCHIQ_MSG_RESUME: return "RESUME"; -+ case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE"; -+ case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE"; -+ case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE"; -+ } -+ return "???"; -+} -+ -+static inline void -+vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate) -+{ -+ vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate], -+ srvstate_names[newstate]); -+ service->srvstate = newstate; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->handle == handle)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_by_port(VCHIQ_STATE_T *state, int localport) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ if ((unsigned int)localport <= VCHIQ_PORT_MAX) { -+ spin_lock(&service_spinlock); -+ service = state->services[localport]; -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ } -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid port %d", localport); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle) { -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->handle == handle) && -+ (service->instance == instance)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle) { -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && -+ ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && -+ (service->handle == handle) && -+ (service->instance == instance)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, -+ int *pidx) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ int idx = *pidx; -+ -+ spin_lock(&service_spinlock); -+ while (idx < state->unused_service) { -+ VCHIQ_SERVICE_T *srv = state->services[idx++]; -+ if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (srv->instance == instance)) { -+ service = srv; -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ break; -+ } -+ } -+ spin_unlock(&service_spinlock); -+ -+ *pidx = idx; -+ -+ return service; -+} -+ -+void -+lock_service(VCHIQ_SERVICE_T *service) -+{ -+ spin_lock(&service_spinlock); -+ BUG_ON(!service || (service->ref_count == 0)); -+ if (service) -+ service->ref_count++; -+ spin_unlock(&service_spinlock); -+} -+ -+void -+unlock_service(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ spin_lock(&service_spinlock); -+ BUG_ON(!service || (service->ref_count == 0)); -+ if (service && service->ref_count) { -+ service->ref_count--; -+ if (!service->ref_count) { -+ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); -+ state->services[service->localport] = NULL; -+ } else -+ service = NULL; -+ } -+ spin_unlock(&service_spinlock); -+ -+ if (service && service->userdata_term) -+ service->userdata_term(service->base.userdata); -+ -+ kfree(service); -+} -+ -+int -+vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ int id; -+ -+ id = service ? service->client_id : 0; -+ if (service) -+ unlock_service(service); -+ -+ return id; -+} -+ -+void * -+vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = handle_to_service(handle); -+ -+ return service ? service->base.userdata : NULL; -+} -+ -+int -+vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = handle_to_service(handle); -+ -+ return service ? service->base.fourcc : 0; -+} -+ -+static void -+mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ VCHIQ_SERVICE_QUOTA_T *service_quota; -+ -+ service->closing = 1; -+ -+ /* Synchronise with other threads. */ -+ mutex_lock(&state->recycle_mutex); -+ mutex_unlock(&state->recycle_mutex); -+ if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) { -+ /* If we're pausing then the slot_mutex is held until resume -+ * by the slot handler. Therefore don't try to acquire this -+ * mutex if we're the slot handler and in the pause sent state. -+ * We don't need to in this case anyway. */ -+ mutex_lock(&state->slot_mutex); -+ mutex_unlock(&state->slot_mutex); -+ } -+ -+ /* Unblock any sending thread. */ -+ service_quota = &state->service_quotas[service->localport]; -+ up(&service_quota->quota_event); -+} -+ -+static void -+mark_service_closing(VCHIQ_SERVICE_T *service) -+{ -+ mark_service_closing_internal(service, 0); -+} -+ -+static inline VCHIQ_STATUS_T -+make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, void *bulk_userdata) -+{ -+ VCHIQ_STATUS_T status; -+ vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %x, %x)", -+ service->state->id, service->localport, reason_names[reason], -+ (unsigned int)header, (unsigned int)bulk_userdata); -+ status = service->base.callback(reason, header, service->handle, -+ bulk_userdata); -+ if (status == VCHIQ_ERROR) { -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: ignoring ERROR from callback to service %x", -+ service->state->id, service->handle); -+ status = VCHIQ_SUCCESS; -+ } -+ return status; -+} -+ -+inline void -+vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) -+{ -+ VCHIQ_CONNSTATE_T oldstate = state->conn_state; -+ vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id, -+ conn_state_names[oldstate], -+ conn_state_names[newstate]); -+ state->conn_state = newstate; -+ vchiq_platform_conn_state_changed(state, oldstate, newstate); -+} -+ -+static inline void -+remote_event_create(REMOTE_EVENT_T *event) -+{ -+ event->armed = 0; -+ /* Don't clear the 'fired' flag because it may already have been set -+ ** by the other side. */ -+ sema_init(event->event, 0); -+} -+ -+static inline void -+remote_event_destroy(REMOTE_EVENT_T *event) -+{ -+ (void)event; -+} -+ -+static inline int -+remote_event_wait(REMOTE_EVENT_T *event) -+{ -+ if (!event->fired) { -+ event->armed = 1; -+ dsb(); -+ if (!event->fired) { -+ if (down_interruptible(event->event) != 0) { -+ event->armed = 0; -+ return 0; -+ } -+ } -+ event->armed = 0; -+ wmb(); -+ } -+ -+ event->fired = 0; -+ return 1; -+} -+ -+static inline void -+remote_event_signal_local(REMOTE_EVENT_T *event) -+{ -+ event->armed = 0; -+ up(event->event); -+} -+ -+static inline void -+remote_event_poll(REMOTE_EVENT_T *event) -+{ -+ if (event->fired && event->armed) -+ remote_event_signal_local(event); -+} -+ -+void -+remote_event_pollall(VCHIQ_STATE_T *state) -+{ -+ remote_event_poll(&state->local->sync_trigger); -+ remote_event_poll(&state->local->sync_release); -+ remote_event_poll(&state->local->trigger); -+ remote_event_poll(&state->local->recycle); -+} -+ -+/* Round up message sizes so that any space at the end of a slot is always big -+** enough for a header. This relies on header size being a power of two, which -+** has been verified earlier by a static assertion. */ -+ -+static inline unsigned int -+calc_stride(unsigned int size) -+{ -+ /* Allow room for the header */ -+ size += sizeof(VCHIQ_HEADER_T); -+ -+ /* Round up */ -+ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) -+ - 1); -+} -+ -+/* Called by the slot handler thread */ -+static VCHIQ_SERVICE_T * -+get_listening_service(VCHIQ_STATE_T *state, int fourcc) -+{ -+ int i; -+ -+ WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ if (service && -+ (service->public_fourcc == fourcc) && -+ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && -+ (service->remoteport == VCHIQ_PORT_FREE)))) { -+ lock_service(service); -+ return service; -+ } -+ } -+ -+ return NULL; -+} -+ -+/* Called by the slot handler thread */ -+static VCHIQ_SERVICE_T * -+get_connected_service(VCHIQ_STATE_T *state, unsigned int port) -+{ -+ int i; -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) -+ && (service->remoteport == port)) { -+ lock_service(service); -+ return service; -+ } -+ } -+ return NULL; -+} -+ -+inline void -+request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type) -+{ -+ uint32_t value; -+ -+ if (service) { -+ do { -+ value = atomic_read(&service->poll_flags); -+ } while (atomic_cmpxchg(&service->poll_flags, value, -+ value | (1 << poll_type)) != value); -+ -+ do { -+ value = atomic_read(&state->poll_services[ -+ service->localport>>5]); -+ } while (atomic_cmpxchg( -+ &state->poll_services[service->localport>>5], -+ value, value | (1 << (service->localport & 0x1f))) -+ != value); -+ } -+ -+ state->poll_needed = 1; -+ wmb(); -+ -+ /* ... and ensure the slot handler runs. */ -+ remote_event_signal_local(&state->local->trigger); -+} -+ -+/* Called from queue_message, by the slot handler and application threads, -+** with slot_mutex held */ -+static VCHIQ_HEADER_T * -+reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ int tx_pos = state->local_tx_pos; -+ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK); -+ -+ if (space > slot_space) { -+ VCHIQ_HEADER_T *header; -+ /* Fill the remaining space with padding */ -+ WARN_ON(state->tx_data == NULL); -+ header = (VCHIQ_HEADER_T *) -+ (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); -+ header->msgid = VCHIQ_MSGID_PADDING; -+ header->size = slot_space - sizeof(VCHIQ_HEADER_T); -+ -+ tx_pos += slot_space; -+ } -+ -+ /* If necessary, get the next slot. */ -+ if ((tx_pos & VCHIQ_SLOT_MASK) == 0) { -+ int slot_index; -+ -+ /* If there is no free slot... */ -+ -+ if (down_trylock(&state->slot_available_event) != 0) { -+ /* ...wait for one. */ -+ -+ VCHIQ_STATS_INC(state, slot_stalls); -+ -+ /* But first, flush through the last slot. */ -+ state->local_tx_pos = tx_pos; -+ local->tx_pos = tx_pos; -+ remote_event_signal(&state->remote->trigger); -+ -+ if (!is_blocking || -+ (down_interruptible( -+ &state->slot_available_event) != 0)) -+ return NULL; /* No space available */ -+ } -+ -+ BUG_ON(tx_pos == -+ (state->slot_queue_available * VCHIQ_SLOT_SIZE)); -+ -+ slot_index = local->slot_queue[ -+ SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ state->tx_data = -+ (char *)SLOT_DATA_FROM_INDEX(state, slot_index); -+ } -+ -+ state->local_tx_pos = tx_pos + space; -+ -+ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); -+} -+ -+/* Called by the recycle thread. */ -+static void -+process_free_queue(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; -+ int slot_queue_available; -+ -+ /* Use a read memory barrier to ensure that any state that may have -+ ** been modified by another thread is not masked by stale prefetched -+ ** values. */ -+ rmb(); -+ -+ /* Find slots which have been freed by the other side, and return them -+ ** to the available queue. */ -+ slot_queue_available = state->slot_queue_available; -+ -+ while (slot_queue_available != local->slot_queue_recycle) { -+ unsigned int pos; -+ int slot_index = local->slot_queue[slot_queue_available++ & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); -+ int data_found = 0; -+ -+ vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%x %x %x", -+ state->id, slot_index, (unsigned int)data, -+ local->slot_queue_recycle, slot_queue_available); -+ -+ /* Initialise the bitmask for services which have used this -+ ** slot */ -+ BITSET_ZERO(service_found); -+ -+ pos = 0; -+ -+ while (pos < VCHIQ_SLOT_SIZE) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)(data + pos); -+ int msgid = header->msgid; -+ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) { -+ int port = VCHIQ_MSG_SRCPORT(msgid); -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[port]; -+ int count; -+ spin_lock("a_spinlock); -+ count = service_quota->message_use_count; -+ if (count > 0) -+ service_quota->message_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ -+ if (count == service_quota->message_quota) -+ /* Signal the service that it -+ ** has dropped below its quota -+ */ -+ up(&service_quota->quota_event); -+ else if (count == 0) { -+ vchiq_log_error(vchiq_core_log_level, -+ "service %d " -+ "message_use_count=%d " -+ "(header %x, msgid %x, " -+ "header->msgid %x, " -+ "header->size %x)", -+ port, -+ service_quota-> -+ message_use_count, -+ (unsigned int)header, msgid, -+ header->msgid, -+ header->size); -+ WARN(1, "invalid message use count\n"); -+ } -+ if (!BITSET_IS_SET(service_found, port)) { -+ /* Set the found bit for this service */ -+ BITSET_SET(service_found, port); -+ -+ spin_lock("a_spinlock); -+ count = service_quota->slot_use_count; -+ if (count > 0) -+ service_quota->slot_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ -+ if (count > 0) { -+ /* Signal the service in case -+ ** it has dropped below its -+ ** quota */ -+ up(&service_quota->quota_event); -+ vchiq_log_trace( -+ vchiq_core_log_level, -+ "%d: pfq:%d %x@%x - " -+ "slot_use->%d", -+ state->id, port, -+ header->size, -+ (unsigned int)header, -+ count - 1); -+ } else { -+ vchiq_log_error( -+ vchiq_core_log_level, -+ "service %d " -+ "slot_use_count" -+ "=%d (header %x" -+ ", msgid %x, " -+ "header->msgid" -+ " %x, header->" -+ "size %x)", -+ port, count, -+ (unsigned int)header, -+ msgid, -+ header->msgid, -+ header->size); -+ WARN(1, "bad slot use count\n"); -+ } -+ } -+ -+ data_found = 1; -+ } -+ -+ pos += calc_stride(header->size); -+ if (pos > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "pfq - pos %x: header %x, msgid %x, " -+ "header->msgid %x, header->size %x", -+ pos, (unsigned int)header, msgid, -+ header->msgid, header->size); -+ WARN(1, "invalid slot position\n"); -+ } -+ } -+ -+ if (data_found) { -+ int count; -+ spin_lock("a_spinlock); -+ count = state->data_use_count; -+ if (count > 0) -+ state->data_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ if (count == state->data_quota) -+ up(&state->data_quota_event); -+ } -+ -+ state->slot_queue_available = slot_queue_available; -+ up(&state->slot_available_event); -+ } -+} -+ -+/* Called by the slot handler and application threads */ -+static VCHIQ_STATUS_T -+queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ int msgid, const VCHIQ_ELEMENT_T *elements, -+ int count, int size, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; -+ VCHIQ_HEADER_T *header; -+ int type = VCHIQ_MSG_TYPE(msgid); -+ -+ unsigned int stride; -+ -+ local = state->local; -+ -+ stride = calc_stride(size); -+ -+ WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); -+ -+ if ((type != VCHIQ_MSG_RESUME) && -+ (mutex_lock_interruptible(&state->slot_mutex) != 0)) -+ return VCHIQ_RETRY; -+ -+ if (type == VCHIQ_MSG_DATA) { -+ int tx_end_index; -+ -+ BUG_ON(!service); -+ -+ if (service->closing) { -+ /* The service has been closed */ -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_ERROR; -+ } -+ -+ service_quota = &state->service_quotas[service->localport]; -+ -+ spin_lock("a_spinlock); -+ -+ /* Ensure this service doesn't use more than its quota of -+ ** messages or slots */ -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ -+ /* Ensure data messages don't use more than their quota of -+ ** slots */ -+ while ((tx_end_index != state->previous_data_index) && -+ (state->data_use_count == state->data_quota)) { -+ VCHIQ_STATS_INC(state, data_stalls); -+ spin_unlock("a_spinlock); -+ mutex_unlock(&state->slot_mutex); -+ -+ if (down_interruptible(&state->data_quota_event) -+ != 0) -+ return VCHIQ_RETRY; -+ -+ mutex_lock(&state->slot_mutex); -+ spin_lock("a_spinlock); -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ if ((tx_end_index == state->previous_data_index) || -+ (state->data_use_count < state->data_quota)) { -+ /* Pass the signal on to other waiters */ -+ up(&state->data_quota_event); -+ break; -+ } -+ } -+ -+ while ((service_quota->message_use_count == -+ service_quota->message_quota) || -+ ((tx_end_index != service_quota->previous_tx_index) && -+ (service_quota->slot_use_count == -+ service_quota->slot_quota))) { -+ spin_unlock("a_spinlock); -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: qm:%d %s,%x - quota stall " -+ "(msg %d, slot %d)", -+ state->id, service->localport, -+ msg_type_str(type), size, -+ service_quota->message_use_count, -+ service_quota->slot_use_count); -+ VCHIQ_SERVICE_STATS_INC(service, quota_stalls); -+ mutex_unlock(&state->slot_mutex); -+ if (down_interruptible(&service_quota->quota_event) -+ != 0) -+ return VCHIQ_RETRY; -+ if (service->closing) -+ return VCHIQ_ERROR; -+ if (mutex_lock_interruptible(&state->slot_mutex) != 0) -+ return VCHIQ_RETRY; -+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { -+ /* The service has been closed */ -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_ERROR; -+ } -+ spin_lock("a_spinlock); -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ } -+ -+ spin_unlock("a_spinlock); -+ } -+ -+ header = reserve_space(state, stride, is_blocking); -+ -+ if (!header) { -+ if (service) -+ VCHIQ_SERVICE_STATS_INC(service, slot_stalls); -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_RETRY; -+ } -+ -+ if (type == VCHIQ_MSG_DATA) { -+ int i, pos; -+ int tx_end_index; -+ int slot_use_count; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: qm %s@%x,%x (%d->%d)", -+ state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ -+ BUG_ON(!service); -+ -+ for (i = 0, pos = 0; i < (unsigned int)count; -+ pos += elements[i++].size) -+ if (elements[i].size) { -+ if (vchiq_copy_from_user -+ (header->data + pos, elements[i].data, -+ (size_t) elements[i].size) != -+ VCHIQ_SUCCESS) { -+ mutex_unlock(&state->slot_mutex); -+ VCHIQ_SERVICE_STATS_INC(service, -+ error_count); -+ return VCHIQ_ERROR; -+ } -+ if (i == 0) { -+ if (SRVTRACE_ENABLED(service, -+ VCHIQ_LOG_INFO)) -+ vchiq_log_dump_mem("Sent", 0, -+ header->data + pos, -+ min(64u, -+ elements[0].size)); -+ } -+ } -+ -+ spin_lock("a_spinlock); -+ service_quota->message_use_count++; -+ -+ tx_end_index = -+ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1); -+ -+ /* If this transmission can't fit in the last slot used by any -+ ** service, the data_use_count must be increased. */ -+ if (tx_end_index != state->previous_data_index) { -+ state->previous_data_index = tx_end_index; -+ state->data_use_count++; -+ } -+ -+ /* If this isn't the same slot last used by this service, -+ ** the service's slot_use_count must be increased. */ -+ if (tx_end_index != service_quota->previous_tx_index) { -+ service_quota->previous_tx_index = tx_end_index; -+ slot_use_count = ++service_quota->slot_use_count; -+ } else { -+ slot_use_count = 0; -+ } -+ -+ spin_unlock("a_spinlock); -+ -+ if (slot_use_count) -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: qm:%d %s,%x - slot_use->%d (hdr %p)", -+ state->id, service->localport, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, -+ slot_use_count, header); -+ -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); -+ } else { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: qm %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ if (size != 0) { -+ WARN_ON(!((count == 1) && (size == elements[0].size))); -+ memcpy(header->data, elements[0].data, -+ elements[0].size); -+ } -+ VCHIQ_STATS_INC(state, ctrl_tx_count); -+ } -+ -+ header->msgid = msgid; -+ header->size = size; -+ -+ { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ VCHIQ_MSG_TYPE(msgid), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid), -+ size); -+ } -+ -+ /* Make sure the new header is visible to the peer. */ -+ wmb(); -+ -+ /* Make the new tx_pos visible to the peer. */ -+ local->tx_pos = state->local_tx_pos; -+ wmb(); -+ -+ if (service && (type == VCHIQ_MSG_CLOSE)) -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); -+ -+ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ mutex_unlock(&state->slot_mutex); -+ -+ remote_event_signal(&state->remote->trigger); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+/* Called by the slot handler and application threads */ -+static VCHIQ_STATUS_T -+queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ int msgid, const VCHIQ_ELEMENT_T *elements, -+ int count, int size, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_HEADER_T *header; -+ -+ local = state->local; -+ -+ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) && -+ (mutex_lock_interruptible(&state->sync_mutex) != 0)) -+ return VCHIQ_RETRY; -+ -+ remote_event_wait(&local->sync_release); -+ -+ rmb(); -+ -+ header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ local->slot_sync); -+ -+ { -+ int oldmsgid = header->msgid; -+ if (oldmsgid != VCHIQ_MSGID_PADDING) -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: qms - msgid %x, not PADDING", -+ state->id, oldmsgid); -+ } -+ -+ if (service) { -+ int i, pos; -+ -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: qms %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ -+ for (i = 0, pos = 0; i < (unsigned int)count; -+ pos += elements[i++].size) -+ if (elements[i].size) { -+ if (vchiq_copy_from_user -+ (header->data + pos, elements[i].data, -+ (size_t) elements[i].size) != -+ VCHIQ_SUCCESS) { -+ mutex_unlock(&state->sync_mutex); -+ VCHIQ_SERVICE_STATS_INC(service, -+ error_count); -+ return VCHIQ_ERROR; -+ } -+ if (i == 0) { -+ if (vchiq_sync_log_level >= -+ VCHIQ_LOG_TRACE) -+ vchiq_log_dump_mem("Sent Sync", -+ 0, header->data + pos, -+ min(64u, -+ elements[0].size)); -+ } -+ } -+ -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); -+ } else { -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: qms %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ if (size != 0) { -+ WARN_ON(!((count == 1) && (size == elements[0].size))); -+ memcpy(header->data, elements[0].data, -+ elements[0].size); -+ } -+ VCHIQ_STATS_INC(state, ctrl_tx_count); -+ } -+ -+ header->size = size; -+ header->msgid = msgid; -+ -+ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ -+ vchiq_log_trace(vchiq_sync_log_level, -+ "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ VCHIQ_MSG_TYPE(msgid), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid), -+ size); -+ } -+ -+ /* Make sure the new header is visible to the peer. */ -+ wmb(); -+ -+ remote_event_signal(&state->remote->sync_trigger); -+ -+ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ mutex_unlock(&state->sync_mutex); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+static inline void -+claim_slot(VCHIQ_SLOT_INFO_T *slot) -+{ -+ slot->use_count++; -+} -+ -+static void -+release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info, -+ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service) -+{ -+ int release_count; -+ -+ mutex_lock(&state->recycle_mutex); -+ -+ if (header) { -+ int msgid = header->msgid; -+ if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) || -+ (service && service->closing)) { -+ mutex_unlock(&state->recycle_mutex); -+ return; -+ } -+ -+ /* Rewrite the message header to prevent a double -+ ** release */ -+ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; -+ } -+ -+ release_count = slot_info->release_count; -+ slot_info->release_count = ++release_count; -+ -+ if (release_count == slot_info->use_count) { -+ int slot_queue_recycle; -+ /* Add to the freed queue */ -+ -+ /* A read barrier is necessary here to prevent speculative -+ ** fetches of remote->slot_queue_recycle from overtaking the -+ ** mutex. */ -+ rmb(); -+ -+ slot_queue_recycle = state->remote->slot_queue_recycle; -+ state->remote->slot_queue[slot_queue_recycle & -+ VCHIQ_SLOT_QUEUE_MASK] = -+ SLOT_INDEX_FROM_INFO(state, slot_info); -+ state->remote->slot_queue_recycle = slot_queue_recycle + 1; -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: release_slot %d - recycle->%x", -+ state->id, SLOT_INDEX_FROM_INFO(state, slot_info), -+ state->remote->slot_queue_recycle); -+ -+ /* A write barrier is necessary, but remote_event_signal -+ ** contains one. */ -+ remote_event_signal(&state->remote->recycle); -+ } -+ -+ mutex_unlock(&state->recycle_mutex); -+} -+ -+/* Called by the slot handler - don't hold the bulk mutex */ -+static VCHIQ_STATUS_T -+notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue, -+ int retry_poll) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: nb:%d %cx - p=%x rn=%x r=%x", -+ service->state->id, service->localport, -+ (queue == &service->bulk_tx) ? 't' : 'r', -+ queue->process, queue->remote_notify, queue->remove); -+ -+ if (service->state->is_master) { -+ while (queue->remote_notify != queue->process) { -+ VCHIQ_BULK_T *bulk = -+ &queue->bulks[BULK_INDEX(queue->remote_notify)]; -+ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE; -+ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, -+ service->remoteport); -+ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 }; -+ /* Only reply to non-dummy bulk requests */ -+ if (bulk->remote_data) { -+ status = queue_message(service->state, NULL, -+ msgid, &element, 1, 4, 0); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ queue->remote_notify++; -+ } -+ } else { -+ queue->remote_notify = queue->process; -+ } -+ -+ if (status == VCHIQ_SUCCESS) { -+ while (queue->remove != queue->remote_notify) { -+ VCHIQ_BULK_T *bulk = -+ &queue->bulks[BULK_INDEX(queue->remove)]; -+ -+ /* Only generate callbacks for non-dummy bulk -+ ** requests, and non-terminated services */ -+ if (bulk->data && service->instance) { -+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { -+ if (bulk->dir == VCHIQ_BULK_TRANSMIT) { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, -+ bulk_tx_bytes, -+ bulk->actual); -+ } else { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_rx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, -+ bulk_rx_bytes, -+ bulk->actual); -+ } -+ } else { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_aborted_count); -+ } -+ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { -+ struct bulk_waiter *waiter; -+ spin_lock(&bulk_waiter_spinlock); -+ waiter = bulk->userdata; -+ if (waiter) { -+ waiter->actual = bulk->actual; -+ up(&waiter->event); -+ } -+ spin_unlock(&bulk_waiter_spinlock); -+ } else if (bulk->mode == -+ VCHIQ_BULK_MODE_CALLBACK) { -+ VCHIQ_REASON_T reason = (bulk->dir == -+ VCHIQ_BULK_TRANSMIT) ? -+ ((bulk->actual == -+ VCHIQ_BULK_ACTUAL_ABORTED) ? -+ VCHIQ_BULK_TRANSMIT_ABORTED : -+ VCHIQ_BULK_TRANSMIT_DONE) : -+ ((bulk->actual == -+ VCHIQ_BULK_ACTUAL_ABORTED) ? -+ VCHIQ_BULK_RECEIVE_ABORTED : -+ VCHIQ_BULK_RECEIVE_DONE); -+ status = make_service_callback(service, -+ reason, NULL, bulk->userdata); -+ if (status == VCHIQ_RETRY) -+ break; -+ } -+ } -+ -+ queue->remove++; -+ up(&service->bulk_remove_event); -+ } -+ if (!retry_poll) -+ status = VCHIQ_SUCCESS; -+ } -+ -+ if (status == VCHIQ_RETRY) -+ request_poll(service->state, service, -+ (queue == &service->bulk_tx) ? -+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); -+ -+ return status; -+} -+ -+/* Called by the slot handler thread */ -+static void -+poll_services(VCHIQ_STATE_T *state) -+{ -+ int group, i; -+ -+ for (group = 0; group < BITSET_SIZE(state->unused_service); group++) { -+ uint32_t flags; -+ flags = atomic_xchg(&state->poll_services[group], 0); -+ for (i = 0; flags; i++) { -+ if (flags & (1 << i)) { -+ VCHIQ_SERVICE_T *service = -+ find_service_by_port(state, -+ (group<<5) + i); -+ uint32_t service_flags; -+ flags &= ~(1 << i); -+ if (!service) -+ continue; -+ service_flags = -+ atomic_xchg(&service->poll_flags, 0); -+ if (service_flags & -+ (1 << VCHIQ_POLL_REMOVE)) { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: ps - remove %d<->%d", -+ state->id, service->localport, -+ service->remoteport); -+ -+ /* Make it look like a client, because -+ it must be removed and not left in -+ the LISTENING state. */ -+ service->public_fourcc = -+ VCHIQ_FOURCC_INVALID; -+ -+ if (vchiq_close_service_internal( -+ service, 0/*!close_recvd*/) != -+ VCHIQ_SUCCESS) -+ request_poll(state, service, -+ VCHIQ_POLL_REMOVE); -+ } else if (service_flags & -+ (1 << VCHIQ_POLL_TERMINATE)) { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: ps - terminate %d<->%d", -+ state->id, service->localport, -+ service->remoteport); -+ if (vchiq_close_service_internal( -+ service, 0/*!close_recvd*/) != -+ VCHIQ_SUCCESS) -+ request_poll(state, service, -+ VCHIQ_POLL_TERMINATE); -+ } -+ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY)) -+ notify_bulks(service, -+ &service->bulk_tx, -+ 1/*retry_poll*/); -+ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY)) -+ notify_bulks(service, -+ &service->bulk_rx, -+ 1/*retry_poll*/); -+ unlock_service(service); -+ } -+ } -+ } -+} -+ -+/* Called by the slot handler or application threads, holding the bulk mutex. */ -+static int -+resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ int resolved = 0; -+ int rc; -+ -+ while ((queue->process != queue->local_insert) && -+ (queue->process != queue->remote_insert)) { -+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: rb:%d %cx - li=%x ri=%x p=%x", -+ state->id, service->localport, -+ (queue == &service->bulk_tx) ? 't' : 'r', -+ queue->local_insert, queue->remote_insert, -+ queue->process); -+ -+ WARN_ON(!((int)(queue->local_insert - queue->process) > 0)); -+ WARN_ON(!((int)(queue->remote_insert - queue->process) > 0)); -+ -+ rc = mutex_lock_interruptible(&state->bulk_transfer_mutex); -+ if (rc != 0) -+ break; -+ -+ vchiq_transfer_bulk(bulk); -+ mutex_unlock(&state->bulk_transfer_mutex); -+ -+ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { -+ const char *header = (queue == &service->bulk_tx) ? -+ "Send Bulk to" : "Recv Bulk from"; -+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d len:%d %x<->%x", -+ header, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ (unsigned int)bulk->data, -+ (unsigned int)bulk->remote_data); -+ else -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d ABORTED - tx len:%d," -+ " rx len:%d %x<->%x", -+ header, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ bulk->remote_size, -+ (unsigned int)bulk->data, -+ (unsigned int)bulk->remote_data); -+ } -+ -+ vchiq_complete_bulk(bulk); -+ queue->process++; -+ resolved++; -+ } -+ return resolved; -+} -+ -+/* Called with the bulk_mutex held */ -+static void -+abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) -+{ -+ int is_tx = (queue == &service->bulk_tx); -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: aob:%d %cx - li=%x ri=%x p=%x", -+ service->state->id, service->localport, is_tx ? 't' : 'r', -+ queue->local_insert, queue->remote_insert, queue->process); -+ -+ WARN_ON(!((int)(queue->local_insert - queue->process) >= 0)); -+ WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0)); -+ -+ while ((queue->process != queue->local_insert) || -+ (queue->process != queue->remote_insert)) { -+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; -+ -+ if (queue->process == queue->remote_insert) { -+ /* fabricate a matching dummy bulk */ -+ bulk->remote_data = NULL; -+ bulk->remote_size = 0; -+ queue->remote_insert++; -+ } -+ -+ if (queue->process != queue->local_insert) { -+ vchiq_complete_bulk(bulk); -+ -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d ABORTED - tx len:%d, " -+ "rx len:%d", -+ is_tx ? "Send Bulk to" : "Recv Bulk from", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ bulk->remote_size); -+ } else { -+ /* fabricate a matching dummy bulk */ -+ bulk->data = NULL; -+ bulk->size = 0; -+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; -+ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : -+ VCHIQ_BULK_RECEIVE; -+ queue->local_insert++; -+ } -+ -+ queue->process++; -+ } -+} -+ -+/* Called from the slot handler thread */ -+static void -+pause_bulks(VCHIQ_STATE_T *state) -+{ -+ if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) { -+ WARN_ON_ONCE(1); -+ atomic_set(&pause_bulks_count, 1); -+ return; -+ } -+ -+ /* Block bulk transfers from all services */ -+ mutex_lock(&state->bulk_transfer_mutex); -+} -+ -+/* Called from the slot handler thread */ -+static void -+resume_bulks(VCHIQ_STATE_T *state) -+{ -+ int i; -+ if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) { -+ WARN_ON_ONCE(1); -+ atomic_set(&pause_bulks_count, 0); -+ return; -+ } -+ -+ /* Allow bulk transfers from all services */ -+ mutex_unlock(&state->bulk_transfer_mutex); -+ -+ if (state->deferred_bulks == 0) -+ return; -+ -+ /* Deal with any bulks which had to be deferred due to being in -+ * paused state. Don't try to match up to number of deferred bulks -+ * in case we've had something come and close the service in the -+ * interim - just process all bulk queues for all services */ -+ vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks", -+ __func__, state->deferred_bulks); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ int resolved_rx = 0; -+ int resolved_tx = 0; -+ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) -+ continue; -+ -+ mutex_lock(&service->bulk_mutex); -+ resolved_rx = resolve_bulks(service, &service->bulk_rx); -+ resolved_tx = resolve_bulks(service, &service->bulk_tx); -+ mutex_unlock(&service->bulk_mutex); -+ if (resolved_rx) -+ notify_bulks(service, &service->bulk_rx, 1); -+ if (resolved_tx) -+ notify_bulks(service, &service->bulk_tx, 1); -+ } -+ state->deferred_bulks = 0; -+} -+ -+static int -+parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ msgid = header->msgid; -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ if (size >= sizeof(struct vchiq_open_payload)) { -+ const struct vchiq_open_payload *payload = -+ (struct vchiq_open_payload *)header->data; -+ unsigned int fourcc; -+ -+ fourcc = payload->fourcc; -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs OPEN@%x (%d->'%c%c%c%c')", -+ state->id, (unsigned int)header, -+ localport, -+ VCHIQ_FOURCC_AS_4CHARS(fourcc)); -+ -+ service = get_listening_service(state, fourcc); -+ -+ if (service) { -+ /* A matching service exists */ -+ short version = payload->version; -+ short version_min = payload->version_min; -+ if ((service->version < version_min) || -+ (version < service->version_min)) { -+ /* Version mismatch */ -+ vchiq_loud_error_header(); -+ vchiq_loud_error("%d: service %d (%c%c%c%c) " -+ "version mismatch - local (%d, min %d)" -+ " vs. remote (%d, min %d)", -+ state->id, service->localport, -+ VCHIQ_FOURCC_AS_4CHARS(fourcc), -+ service->version, service->version_min, -+ version, version_min); -+ vchiq_loud_error_footer(); -+ unlock_service(service); -+ service = NULL; -+ goto fail_open; -+ } -+ service->peer_version = version; -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { -+ struct vchiq_openack_payload ack_payload = { -+ service->version -+ }; -+ VCHIQ_ELEMENT_T body = { -+ &ack_payload, -+ sizeof(ack_payload) -+ }; -+ -+ /* Acknowledge the OPEN */ -+ if (service->sync) { -+ if (queue_message_sync(state, NULL, -+ VCHIQ_MAKE_MSG( -+ VCHIQ_MSG_OPENACK, -+ service->localport, -+ remoteport), -+ &body, 1, sizeof(ack_payload), -+ 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ } else { -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG( -+ VCHIQ_MSG_OPENACK, -+ service->localport, -+ remoteport), -+ &body, 1, sizeof(ack_payload), -+ 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ } -+ -+ /* The service is now open */ -+ vchiq_set_service_state(service, -+ service->sync ? VCHIQ_SRVSTATE_OPENSYNC -+ : VCHIQ_SRVSTATE_OPEN); -+ } -+ -+ service->remoteport = remoteport; -+ service->client_id = ((int *)header->data)[1]; -+ if (make_service_callback(service, VCHIQ_SERVICE_OPENED, -+ NULL, NULL) == VCHIQ_RETRY) { -+ /* Bail out if not ready */ -+ service->remoteport = VCHIQ_PORT_FREE; -+ goto bail_not_ready; -+ } -+ -+ /* Success - the message has been dealt with */ -+ unlock_service(service); -+ return 1; -+ } -+ } -+ -+fail_open: -+ /* No available service, or an invalid request - send a CLOSE */ -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)), -+ NULL, 0, 0, 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ -+ return 1; -+ -+bail_not_ready: -+ if (service) -+ unlock_service(service); -+ -+ return 0; -+} -+ -+/* Called by the slot handler thread */ -+static void -+parse_rx_slots(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_SHARED_STATE_T *remote = state->remote; -+ VCHIQ_SERVICE_T *service = NULL; -+ int tx_pos; -+ DEBUG_INITIALISE(state->local) -+ -+ tx_pos = remote->tx_pos; -+ -+ while (state->rx_pos != tx_pos) { -+ VCHIQ_HEADER_T *header; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (!state->rx_data) { -+ int rx_index; -+ WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0)); -+ rx_index = remote->slot_queue[ -+ SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, -+ rx_index); -+ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index); -+ -+ /* Initialise use_count to one, and increment -+ ** release_count at the end of the slot to avoid -+ ** releasing the slot prematurely. */ -+ state->rx_info->use_count = 1; -+ state->rx_info->release_count = 0; -+ } -+ -+ header = (VCHIQ_HEADER_T *)(state->rx_data + -+ (state->rx_pos & VCHIQ_SLOT_MASK)); -+ DEBUG_VALUE(PARSE_HEADER, (int)header); -+ msgid = header->msgid; -+ DEBUG_VALUE(PARSE_MSGID, msgid); -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ -+ if (type != VCHIQ_MSG_DATA) -+ VCHIQ_STATS_INC(state, ctrl_rx_count); -+ -+ switch (type) { -+ case VCHIQ_MSG_OPENACK: -+ case VCHIQ_MSG_CLOSE: -+ case VCHIQ_MSG_DATA: -+ case VCHIQ_MSG_BULK_RX: -+ case VCHIQ_MSG_BULK_TX: -+ case VCHIQ_MSG_BULK_RX_DONE: -+ case VCHIQ_MSG_BULK_TX_DONE: -+ service = find_service_by_port(state, localport); -+ if ((!service || -+ ((service->remoteport != remoteport) && -+ (service->remoteport != VCHIQ_PORT_FREE))) && -+ (localport == 0) && -+ (type == VCHIQ_MSG_CLOSE)) { -+ /* This could be a CLOSE from a client which -+ hadn't yet received the OPENACK - look for -+ the connected service */ -+ if (service) -+ unlock_service(service); -+ service = get_connected_service(state, -+ remoteport); -+ if (service) -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) - " -+ "found connected service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ service->localport); -+ } -+ -+ if (!service) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) - " -+ "invalid/closed service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, localport); -+ goto skip_message; -+ } -+ break; -+ default: -+ break; -+ } -+ -+ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d " -+ "len:%d", -+ msg_type_str(type), type, -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ remoteport, localport, size); -+ if (size > 0) -+ vchiq_log_dump_mem("Rcvd", 0, header->data, -+ min(64, size)); -+ } -+ -+ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) -+ > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "header %x (msgid %x) - size %x too big for " -+ "slot", -+ (unsigned int)header, (unsigned int)msgid, -+ (unsigned int)size); -+ WARN(1, "oversized for slot\n"); -+ } -+ -+ switch (type) { -+ case VCHIQ_MSG_OPEN: -+ WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0)); -+ if (!parse_open(state, header)) -+ goto bail_not_ready; -+ break; -+ case VCHIQ_MSG_OPENACK: -+ if (size >= sizeof(struct vchiq_openack_payload)) { -+ const struct vchiq_openack_payload *payload = -+ (struct vchiq_openack_payload *) -+ header->data; -+ service->peer_version = payload->version; -+ } -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs OPENACK@%x,%x (%d->%d) v:%d", -+ state->id, (unsigned int)header, size, -+ remoteport, localport, service->peer_version); -+ if (service->srvstate == -+ VCHIQ_SRVSTATE_OPENING) { -+ service->remoteport = remoteport; -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_OPEN); -+ up(&service->remove_event); -+ } else -+ vchiq_log_error(vchiq_core_log_level, -+ "OPENACK received in state %s", -+ srvstate_names[service->srvstate]); -+ break; -+ case VCHIQ_MSG_CLOSE: -+ WARN_ON(size != 0); /* There should be no data */ -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs CLOSE@%x (%d->%d)", -+ state->id, (unsigned int)header, -+ remoteport, localport); -+ -+ mark_service_closing_internal(service, 1); -+ -+ if (vchiq_close_service_internal(service, -+ 1/*close_recvd*/) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "Close Service %c%c%c%c s:%u d:%d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->localport, -+ service->remoteport); -+ break; -+ case VCHIQ_MSG_DATA: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs DATA@%x,%x (%d->%d)", -+ state->id, (unsigned int)header, size, -+ remoteport, localport); -+ -+ if ((service->remoteport == remoteport) -+ && (service->srvstate == -+ VCHIQ_SRVSTATE_OPEN)) { -+ header->msgid = msgid | VCHIQ_MSGID_CLAIMED; -+ claim_slot(state->rx_info); -+ DEBUG_TRACE(PARSE_LINE); -+ if (make_service_callback(service, -+ VCHIQ_MESSAGE_AVAILABLE, header, -+ NULL) == VCHIQ_RETRY) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, -+ size); -+ } else { -+ VCHIQ_STATS_INC(state, error_count); -+ } -+ break; -+ case VCHIQ_MSG_CONNECT: -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs CONNECT@%x", -+ state->id, (unsigned int)header); -+ up(&state->connect); -+ break; -+ case VCHIQ_MSG_BULK_RX: -+ case VCHIQ_MSG_BULK_TX: { -+ VCHIQ_BULK_QUEUE_T *queue; -+ WARN_ON(!state->is_master); -+ queue = (type == VCHIQ_MSG_BULK_RX) ? -+ &service->bulk_tx : &service->bulk_rx; -+ if ((service->remoteport == remoteport) -+ && (service->srvstate == -+ VCHIQ_SRVSTATE_OPEN)) { -+ VCHIQ_BULK_T *bulk; -+ int resolved = 0; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (mutex_lock_interruptible( -+ &service->bulk_mutex) != 0) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ -+ WARN_ON(!(queue->remote_insert < queue->remove + -+ VCHIQ_NUM_SERVICE_BULKS)); -+ bulk = &queue->bulks[ -+ BULK_INDEX(queue->remote_insert)]; -+ bulk->remote_data = -+ (void *)((int *)header->data)[0]; -+ bulk->remote_size = ((int *)header->data)[1]; -+ wmb(); -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) %x@%x", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ bulk->remote_size, -+ (unsigned int)bulk->remote_data); -+ -+ queue->remote_insert++; -+ -+ if (atomic_read(&pause_bulks_count)) { -+ state->deferred_bulks++; -+ vchiq_log_info(vchiq_core_log_level, -+ "%s: deferring bulk (%d)", -+ __func__, -+ state->deferred_bulks); -+ if (state->conn_state != -+ VCHIQ_CONNSTATE_PAUSE_SENT) -+ vchiq_log_error( -+ vchiq_core_log_level, -+ "%s: bulks paused in " -+ "unexpected state %s", -+ __func__, -+ conn_state_names[ -+ state->conn_state]); -+ } else if (state->conn_state == -+ VCHIQ_CONNSTATE_CONNECTED) { -+ DEBUG_TRACE(PARSE_LINE); -+ resolved = resolve_bulks(service, -+ queue); -+ } -+ -+ mutex_unlock(&service->bulk_mutex); -+ if (resolved) -+ notify_bulks(service, queue, -+ 1/*retry_poll*/); -+ } -+ } break; -+ case VCHIQ_MSG_BULK_RX_DONE: -+ case VCHIQ_MSG_BULK_TX_DONE: -+ WARN_ON(state->is_master); -+ if ((service->remoteport == remoteport) -+ && (service->srvstate != -+ VCHIQ_SRVSTATE_FREE)) { -+ VCHIQ_BULK_QUEUE_T *queue; -+ VCHIQ_BULK_T *bulk; -+ -+ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ? -+ &service->bulk_rx : &service->bulk_tx; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (mutex_lock_interruptible( -+ &service->bulk_mutex) != 0) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ if ((int)(queue->remote_insert - -+ queue->local_insert) >= 0) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) " -+ "unexpected (ri=%d,li=%d)", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ queue->remote_insert, -+ queue->local_insert); -+ mutex_unlock(&service->bulk_mutex); -+ break; -+ } -+ -+ BUG_ON(queue->process == queue->local_insert); -+ BUG_ON(queue->process != queue->remote_insert); -+ -+ bulk = &queue->bulks[ -+ BULK_INDEX(queue->remote_insert)]; -+ bulk->actual = *(int *)header->data; -+ queue->remote_insert++; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) %x@%x", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ bulk->actual, (unsigned int)bulk->data); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs:%d %cx li=%x ri=%x p=%x", -+ state->id, localport, -+ (type == VCHIQ_MSG_BULK_RX_DONE) ? -+ 'r' : 't', -+ queue->local_insert, -+ queue->remote_insert, queue->process); -+ -+ DEBUG_TRACE(PARSE_LINE); -+ WARN_ON(queue->process == queue->local_insert); -+ vchiq_complete_bulk(bulk); -+ queue->process++; -+ mutex_unlock(&service->bulk_mutex); -+ DEBUG_TRACE(PARSE_LINE); -+ notify_bulks(service, queue, 1/*retry_poll*/); -+ DEBUG_TRACE(PARSE_LINE); -+ } -+ break; -+ case VCHIQ_MSG_PADDING: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs PADDING@%x,%x", -+ state->id, (unsigned int)header, size); -+ break; -+ case VCHIQ_MSG_PAUSE: -+ /* If initiated, signal the application thread */ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs PAUSE@%x,%x", -+ state->id, (unsigned int)header, size); -+ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: PAUSE received in state PAUSED", -+ state->id); -+ break; -+ } -+ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { -+ /* Send a PAUSE in response */ -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -+ NULL, 0, 0, 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ if (state->is_master) -+ pause_bulks(state); -+ } -+ /* At this point slot_mutex is held */ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); -+ vchiq_platform_paused(state); -+ break; -+ case VCHIQ_MSG_RESUME: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs RESUME@%x,%x", -+ state->id, (unsigned int)header, size); -+ /* Release the slot mutex */ -+ mutex_unlock(&state->slot_mutex); -+ if (state->is_master) -+ resume_bulks(state); -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); -+ vchiq_platform_resumed(state); -+ break; -+ -+ case VCHIQ_MSG_REMOTE_USE: -+ vchiq_on_remote_use(state); -+ break; -+ case VCHIQ_MSG_REMOTE_RELEASE: -+ vchiq_on_remote_release(state); -+ break; -+ case VCHIQ_MSG_REMOTE_USE_ACTIVE: -+ vchiq_on_remote_use_active(state); -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs invalid msgid %x@%x,%x", -+ state->id, msgid, (unsigned int)header, size); -+ WARN(1, "invalid message\n"); -+ break; -+ } -+ -+skip_message: -+ if (service) { -+ unlock_service(service); -+ service = NULL; -+ } -+ -+ state->rx_pos += calc_stride(size); -+ -+ DEBUG_TRACE(PARSE_LINE); -+ /* Perform some housekeeping when the end of the slot is -+ ** reached. */ -+ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) { -+ /* Remove the extra reference count. */ -+ release_slot(state, state->rx_info, NULL, NULL); -+ state->rx_data = NULL; -+ } -+ } -+ -+bail_not_ready: -+ if (service) -+ unlock_service(service); -+} -+ -+/* Called by the slot handler thread */ -+static int -+slot_handler_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ DEBUG_INITIALISE(local) -+ -+ while (1) { -+ DEBUG_COUNT(SLOT_HANDLER_COUNT); -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ remote_event_wait(&local->trigger); -+ -+ rmb(); -+ -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ if (state->poll_needed) { -+ /* Check if we need to suspend - may change our -+ * conn_state */ -+ vchiq_platform_check_suspend(state); -+ -+ state->poll_needed = 0; -+ -+ /* Handle service polling and other rare conditions here -+ ** out of the mainline code */ -+ switch (state->conn_state) { -+ case VCHIQ_CONNSTATE_CONNECTED: -+ /* Poll the services as requested */ -+ poll_services(state); -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSING: -+ if (state->is_master) -+ pause_bulks(state); -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -+ NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ vchiq_set_conn_state(state, -+ VCHIQ_CONNSTATE_PAUSE_SENT); -+ } else { -+ if (state->is_master) -+ resume_bulks(state); -+ /* Retry later */ -+ state->poll_needed = 1; -+ } -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSED: -+ vchiq_platform_resume(state); -+ break; -+ -+ case VCHIQ_CONNSTATE_RESUMING: -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), -+ NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ if (state->is_master) -+ resume_bulks(state); -+ vchiq_set_conn_state(state, -+ VCHIQ_CONNSTATE_CONNECTED); -+ vchiq_platform_resumed(state); -+ } else { -+ /* This should really be impossible, -+ ** since the PAUSE should have flushed -+ ** through outstanding messages. */ -+ vchiq_log_error(vchiq_core_log_level, -+ "Failed to send RESUME " -+ "message"); -+ BUG(); -+ } -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSE_TIMEOUT: -+ case VCHIQ_CONNSTATE_RESUME_TIMEOUT: -+ vchiq_platform_handle_timeout(state); -+ break; -+ default: -+ break; -+ } -+ -+ -+ } -+ -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ parse_rx_slots(state); -+ } -+ return 0; -+} -+ -+ -+/* Called by the recycle thread */ -+static int -+recycle_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ -+ while (1) { -+ remote_event_wait(&local->recycle); -+ -+ process_free_queue(state); -+ } -+ return 0; -+} -+ -+ -+/* Called by the sync thread */ -+static int -+sync_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ state->remote->slot_sync); -+ -+ while (1) { -+ VCHIQ_SERVICE_T *service; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ remote_event_wait(&local->sync_trigger); -+ -+ rmb(); -+ -+ msgid = header->msgid; -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ -+ service = find_service_by_port(state, localport); -+ -+ if (!service) { -+ vchiq_log_error(vchiq_sync_log_level, -+ "%d: sf %s@%x (%d->%d) - " -+ "invalid/closed service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, localport); -+ release_message_sync(state, header); -+ continue; -+ } -+ -+ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ vchiq_log_trace(vchiq_sync_log_level, -+ "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d", -+ msg_type_str(type), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ remoteport, localport, size); -+ if (size > 0) -+ vchiq_log_dump_mem("Rcvd", 0, header->data, -+ min(64, size)); -+ } -+ -+ switch (type) { -+ case VCHIQ_MSG_OPENACK: -+ if (size >= sizeof(struct vchiq_openack_payload)) { -+ const struct vchiq_openack_payload *payload = -+ (struct vchiq_openack_payload *) -+ header->data; -+ service->peer_version = payload->version; -+ } -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: sf OPENACK@%x,%x (%d->%d) v:%d", -+ state->id, (unsigned int)header, size, -+ remoteport, localport, service->peer_version); -+ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { -+ service->remoteport = remoteport; -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_OPENSYNC); -+ up(&service->remove_event); -+ } -+ release_message_sync(state, header); -+ break; -+ -+ case VCHIQ_MSG_DATA: -+ vchiq_log_trace(vchiq_sync_log_level, -+ "%d: sf DATA@%x,%x (%d->%d)", -+ state->id, (unsigned int)header, size, -+ remoteport, localport); -+ -+ if ((service->remoteport == remoteport) && -+ (service->srvstate == -+ VCHIQ_SRVSTATE_OPENSYNC)) { -+ if (make_service_callback(service, -+ VCHIQ_MESSAGE_AVAILABLE, header, -+ NULL) == VCHIQ_RETRY) -+ vchiq_log_error(vchiq_sync_log_level, -+ "synchronous callback to " -+ "service %d returns " -+ "VCHIQ_RETRY", -+ localport); -+ } -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_sync_log_level, -+ "%d: sf unexpected msgid %x@%x,%x", -+ state->id, msgid, (unsigned int)header, size); -+ release_message_sync(state, header); -+ break; -+ } -+ -+ unlock_service(service); -+ } -+ -+ return 0; -+} -+ -+ -+static void -+init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) -+{ -+ queue->local_insert = 0; -+ queue->remote_insert = 0; -+ queue->process = 0; -+ queue->remote_notify = 0; -+ queue->remove = 0; -+} -+ -+ -+inline const char * -+get_conn_state_name(VCHIQ_CONNSTATE_T conn_state) -+{ -+ return conn_state_names[conn_state]; -+} -+ -+ -+VCHIQ_SLOT_ZERO_T * -+vchiq_init_slots(void *mem_base, int mem_size) -+{ -+ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK; -+ VCHIQ_SLOT_ZERO_T *slot_zero = -+ (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align); -+ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE; -+ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS; -+ -+ /* Ensure there is enough memory to run an absolutely minimum system */ -+ num_slots -= first_data_slot; -+ -+ if (num_slots < 4) { -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_init_slots - insufficient memory %x bytes", -+ mem_size); -+ return NULL; -+ } -+ -+ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T)); -+ -+ slot_zero->magic = VCHIQ_MAGIC; -+ slot_zero->version = VCHIQ_VERSION; -+ slot_zero->version_min = VCHIQ_VERSION_MIN; -+ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T); -+ slot_zero->slot_size = VCHIQ_SLOT_SIZE; -+ slot_zero->max_slots = VCHIQ_MAX_SLOTS; -+ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE; -+ -+ slot_zero->master.slot_sync = first_data_slot; -+ slot_zero->master.slot_first = first_data_slot + 1; -+ slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1; -+ slot_zero->slave.slot_sync = first_data_slot + (num_slots/2); -+ slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1; -+ slot_zero->slave.slot_last = first_data_slot + num_slots - 1; -+ -+ return slot_zero; -+} -+ -+VCHIQ_STATUS_T -+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, -+ int is_master) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_STATUS_T status; -+ char threadname[10]; -+ static int id; -+ int i; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%s: slot_zero = 0x%08lx, is_master = %d", -+ __func__, (unsigned long)slot_zero, is_master); -+ -+ /* Check the input configuration */ -+ -+ if (slot_zero->magic != VCHIQ_MAGIC) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Invalid VCHIQ magic value found."); -+ vchiq_loud_error("slot_zero=%x: magic=%x (expected %x)", -+ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (slot_zero->version < VCHIQ_VERSION_MIN) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Incompatible VCHIQ versions found."); -+ vchiq_loud_error("slot_zero=%x: VideoCore version=%d " -+ "(minimum %d)", -+ (unsigned int)slot_zero, slot_zero->version, -+ VCHIQ_VERSION_MIN); -+ vchiq_loud_error("Restart with a newer VideoCore image."); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (VCHIQ_VERSION < slot_zero->version_min) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Incompatible VCHIQ versions found."); -+ vchiq_loud_error("slot_zero=%x: version=%d (VideoCore " -+ "minimum %d)", -+ (unsigned int)slot_zero, VCHIQ_VERSION, -+ slot_zero->version_min); -+ vchiq_loud_error("Restart with a newer kernel."); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) || -+ (slot_zero->slot_size != VCHIQ_SLOT_SIZE) || -+ (slot_zero->max_slots != VCHIQ_MAX_SLOTS) || -+ (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) { -+ vchiq_loud_error_header(); -+ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) -+ vchiq_loud_error("slot_zero=%x: slot_zero_size=%x " -+ "(expected %x)", -+ (unsigned int)slot_zero, -+ slot_zero->slot_zero_size, -+ sizeof(VCHIQ_SLOT_ZERO_T)); -+ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE) -+ vchiq_loud_error("slot_zero=%x: slot_size=%d " -+ "(expected %d", -+ (unsigned int)slot_zero, slot_zero->slot_size, -+ VCHIQ_SLOT_SIZE); -+ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS) -+ vchiq_loud_error("slot_zero=%x: max_slots=%d " -+ "(expected %d)", -+ (unsigned int)slot_zero, slot_zero->max_slots, -+ VCHIQ_MAX_SLOTS); -+ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE) -+ vchiq_loud_error("slot_zero=%x: max_slots_per_side=%d " -+ "(expected %d)", -+ (unsigned int)slot_zero, -+ slot_zero->max_slots_per_side, -+ VCHIQ_MAX_SLOTS_PER_SIDE); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (is_master) { -+ local = &slot_zero->master; -+ remote = &slot_zero->slave; -+ } else { -+ local = &slot_zero->slave; -+ remote = &slot_zero->master; -+ } -+ -+ if (local->initialised) { -+ vchiq_loud_error_header(); -+ if (remote->initialised) -+ vchiq_loud_error("local state has already been " -+ "initialised"); -+ else -+ vchiq_loud_error("master/slave mismatch - two %ss", -+ is_master ? "master" : "slave"); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ memset(state, 0, sizeof(VCHIQ_STATE_T)); -+ -+ state->id = id++; -+ state->is_master = is_master; -+ -+ /* -+ initialize shared state pointers -+ */ -+ -+ state->local = local; -+ state->remote = remote; -+ state->slot_data = (VCHIQ_SLOT_T *)slot_zero; -+ -+ /* -+ initialize events and mutexes -+ */ -+ -+ sema_init(&state->connect, 0); -+ mutex_init(&state->mutex); -+ sema_init(&state->trigger_event, 0); -+ sema_init(&state->recycle_event, 0); -+ sema_init(&state->sync_trigger_event, 0); -+ sema_init(&state->sync_release_event, 0); -+ -+ mutex_init(&state->slot_mutex); -+ mutex_init(&state->recycle_mutex); -+ mutex_init(&state->sync_mutex); -+ mutex_init(&state->bulk_transfer_mutex); -+ -+ sema_init(&state->slot_available_event, 0); -+ sema_init(&state->slot_remove_event, 0); -+ sema_init(&state->data_quota_event, 0); -+ -+ state->slot_queue_available = 0; -+ -+ for (i = 0; i < VCHIQ_MAX_SERVICES; i++) { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[i]; -+ sema_init(&service_quota->quota_event, 0); -+ } -+ -+ for (i = local->slot_first; i <= local->slot_last; i++) { -+ local->slot_queue[state->slot_queue_available++] = i; -+ up(&state->slot_available_event); -+ } -+ -+ state->default_slot_quota = state->slot_queue_available/2; -+ state->default_message_quota = -+ min((unsigned short)(state->default_slot_quota * 256), -+ (unsigned short)~0); -+ -+ state->previous_data_index = -1; -+ state->data_use_count = 0; -+ state->data_quota = state->slot_queue_available - 1; -+ -+ local->trigger.event = &state->trigger_event; -+ remote_event_create(&local->trigger); -+ local->tx_pos = 0; -+ -+ local->recycle.event = &state->recycle_event; -+ remote_event_create(&local->recycle); -+ local->slot_queue_recycle = state->slot_queue_available; -+ -+ local->sync_trigger.event = &state->sync_trigger_event; -+ remote_event_create(&local->sync_trigger); -+ -+ local->sync_release.event = &state->sync_release_event; -+ remote_event_create(&local->sync_release); -+ -+ /* At start-of-day, the slot is empty and available */ -+ ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid -+ = VCHIQ_MSGID_PADDING; -+ remote_event_signal_local(&local->sync_release); -+ -+ local->debug[DEBUG_ENTRIES] = DEBUG_MAX; -+ -+ status = vchiq_platform_init_state(state); -+ -+ /* -+ bring up slot handler thread -+ */ -+ snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); -+ state->slot_handler_thread = kthread_create(&slot_handler_func, -+ (void *)state, -+ threadname); -+ -+ if (state->slot_handler_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->slot_handler_thread, -19); -+ wake_up_process(state->slot_handler_thread); -+ -+ snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); -+ state->recycle_thread = kthread_create(&recycle_func, -+ (void *)state, -+ threadname); -+ if (state->recycle_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->recycle_thread, -19); -+ wake_up_process(state->recycle_thread); -+ -+ snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id); -+ state->sync_thread = kthread_create(&sync_func, -+ (void *)state, -+ threadname); -+ if (state->sync_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->sync_thread, -20); -+ wake_up_process(state->sync_thread); -+ -+ BUG_ON(state->id >= VCHIQ_MAX_STATES); -+ vchiq_states[state->id] = state; -+ -+ /* Indicate readiness to the other side */ -+ local->initialised = 1; -+ -+ return status; -+} -+ -+/* Called from application thread when a client or server service is created. */ -+VCHIQ_SERVICE_T * -+vchiq_add_service_internal(VCHIQ_STATE_T *state, -+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, -+ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term) -+{ -+ VCHIQ_SERVICE_T *service; -+ -+ service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL); -+ if (service) { -+ service->base.fourcc = params->fourcc; -+ service->base.callback = params->callback; -+ service->base.userdata = params->userdata; -+ service->handle = VCHIQ_SERVICE_HANDLE_INVALID; -+ service->ref_count = 1; -+ service->srvstate = VCHIQ_SRVSTATE_FREE; -+ service->userdata_term = userdata_term; -+ service->localport = VCHIQ_PORT_FREE; -+ service->remoteport = VCHIQ_PORT_FREE; -+ -+ service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? -+ VCHIQ_FOURCC_INVALID : params->fourcc; -+ service->client_id = 0; -+ service->auto_close = 1; -+ service->sync = 0; -+ service->closing = 0; -+ service->trace = 0; -+ atomic_set(&service->poll_flags, 0); -+ service->version = params->version; -+ service->version_min = params->version_min; -+ service->state = state; -+ service->instance = instance; -+ service->service_use_count = 0; -+ init_bulk_queue(&service->bulk_tx); -+ init_bulk_queue(&service->bulk_rx); -+ sema_init(&service->remove_event, 0); -+ sema_init(&service->bulk_remove_event, 0); -+ mutex_init(&service->bulk_mutex); -+ memset(&service->stats, 0, sizeof(service->stats)); -+ } else { -+ vchiq_log_error(vchiq_core_log_level, -+ "Out of memory"); -+ } -+ -+ if (service) { -+ VCHIQ_SERVICE_T **pservice = NULL; -+ int i; -+ -+ /* Although it is perfectly possible to use service_spinlock -+ ** to protect the creation of services, it is overkill as it -+ ** disables interrupts while the array is searched. -+ ** The only danger is of another thread trying to create a -+ ** service - service deletion is safe. -+ ** Therefore it is preferable to use state->mutex which, -+ ** although slower to claim, doesn't block interrupts while -+ ** it is held. -+ */ -+ -+ mutex_lock(&state->mutex); -+ -+ /* Prepare to use a previously unused service */ -+ if (state->unused_service < VCHIQ_MAX_SERVICES) -+ pservice = &state->services[state->unused_service]; -+ -+ if (srvstate == VCHIQ_SRVSTATE_OPENING) { -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *srv = state->services[i]; -+ if (!srv) { -+ pservice = &state->services[i]; -+ break; -+ } -+ } -+ } else { -+ for (i = (state->unused_service - 1); i >= 0; i--) { -+ VCHIQ_SERVICE_T *srv = state->services[i]; -+ if (!srv) -+ pservice = &state->services[i]; -+ else if ((srv->public_fourcc == params->fourcc) -+ && ((srv->instance != instance) || -+ (srv->base.callback != -+ params->callback))) { -+ /* There is another server using this -+ ** fourcc which doesn't match. */ -+ pservice = NULL; -+ break; -+ } -+ } -+ } -+ -+ if (pservice) { -+ service->localport = (pservice - state->services); -+ if (!handle_seq) -+ handle_seq = VCHIQ_MAX_STATES * -+ VCHIQ_MAX_SERVICES; -+ service->handle = handle_seq | -+ (state->id * VCHIQ_MAX_SERVICES) | -+ service->localport; -+ handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; -+ *pservice = service; -+ if (pservice == &state->services[state->unused_service]) -+ state->unused_service++; -+ } -+ -+ mutex_unlock(&state->mutex); -+ -+ if (!pservice) { -+ kfree(service); -+ service = NULL; -+ } -+ } -+ -+ if (service) { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[service->localport]; -+ service_quota->slot_quota = state->default_slot_quota; -+ service_quota->message_quota = state->default_message_quota; -+ if (service_quota->slot_use_count == 0) -+ service_quota->previous_tx_index = -+ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) -+ - 1; -+ -+ /* Bring this service online */ -+ vchiq_set_service_state(service, srvstate); -+ -+ vchiq_log_info(vchiq_core_msg_log_level, -+ "%s Service %c%c%c%c SrcPort:%d", -+ (srvstate == VCHIQ_SRVSTATE_OPENING) -+ ? "Open" : "Add", -+ VCHIQ_FOURCC_AS_4CHARS(params->fourcc), -+ service->localport); -+ } -+ -+ /* Don't unlock the service - leave it with a ref_count of 1. */ -+ -+ return service; -+} -+ -+VCHIQ_STATUS_T -+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) -+{ -+ struct vchiq_open_payload payload = { -+ service->base.fourcc, -+ client_id, -+ service->version, -+ service->version_min -+ }; -+ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) }; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ service->client_id = client_id; -+ vchiq_use_service_internal(service); -+ status = queue_message(service->state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), -+ &body, 1, sizeof(payload), 1); -+ if (status == VCHIQ_SUCCESS) { -+ /* Wait for the ACK/NAK */ -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ vchiq_release_service_internal(service); -+ } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && -+ (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { -+ if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: osi - srvstate = %s (ref %d)", -+ service->state->id, -+ srvstate_names[service->srvstate], -+ service->ref_count); -+ status = VCHIQ_ERROR; -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ vchiq_release_service_internal(service); -+ } -+ } -+ return status; -+} -+ -+static void -+release_service_messages(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ int slot_last = state->remote->slot_last; -+ int i; -+ -+ /* Release any claimed messages */ -+ for (i = state->remote->slot_first; i <= slot_last; i++) { -+ VCHIQ_SLOT_INFO_T *slot_info = -+ SLOT_INFO_FROM_INDEX(state, i); -+ if (slot_info->release_count != slot_info->use_count) { -+ char *data = -+ (char *)SLOT_DATA_FROM_INDEX(state, i); -+ unsigned int pos, end; -+ -+ end = VCHIQ_SLOT_SIZE; -+ if (data == state->rx_data) -+ /* This buffer is still being read from - stop -+ ** at the current read position */ -+ end = state->rx_pos & VCHIQ_SLOT_MASK; -+ + pos = 0; + -+ while (pos < end) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)(data + pos); -+ int msgid = header->msgid; -+ int port = VCHIQ_MSG_DSTPORT(msgid); -+ if ((port == service->localport) && -+ (msgid & VCHIQ_MSGID_CLAIMED)) { -+ vchiq_log_info(vchiq_core_log_level, -+ " fsi - hdr %x", -+ (unsigned int)header); -+ release_slot(state, slot_info, header, -+ NULL); -+ } -+ pos += calc_stride(header->size); -+ if (pos > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "fsi - pos %x: header %x, " -+ "msgid %x, header->msgid %x, " -+ "header->size %x", -+ pos, (unsigned int)header, -+ msgid, header->msgid, -+ header->size); -+ WARN(1, "invalid slot position\n"); -+ } -+ } -+ } -+ } -+} -+ -+static int -+do_abort_bulks(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATUS_T status; -+ -+ /* Abort any outstanding bulk transfers */ -+ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) -+ return 0; -+ abort_outstanding_bulks(service, &service->bulk_tx); -+ abort_outstanding_bulks(service, &service->bulk_rx); -+ mutex_unlock(&service->bulk_mutex); -+ -+ status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/); -+ if (status == VCHIQ_SUCCESS) -+ status = notify_bulks(service, &service->bulk_rx, -+ 0/*!retry_poll*/); -+ return (status == VCHIQ_SUCCESS); -+} -+ -+static VCHIQ_STATUS_T -+close_service_complete(VCHIQ_SERVICE_T *service, int failstate) -+{ -+ VCHIQ_STATUS_T status; -+ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); -+ int newstate; -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPEN: -+ case VCHIQ_SRVSTATE_CLOSESENT: -+ case VCHIQ_SRVSTATE_CLOSERECVD: -+ if (is_server) { -+ if (service->auto_close) { -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ newstate = VCHIQ_SRVSTATE_LISTENING; -+ } else -+ newstate = VCHIQ_SRVSTATE_CLOSEWAIT; -+ } else -+ newstate = VCHIQ_SRVSTATE_CLOSED; -+ vchiq_set_service_state(service, newstate); -+ break; -+ case VCHIQ_SRVSTATE_LISTENING: -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "close_service_complete(%x) called in state %s", -+ service->handle, srvstate_names[service->srvstate]); -+ WARN(1, "close_service_complete in unexpected state\n"); -+ return VCHIQ_ERROR; -+ } -+ -+ status = make_service_callback(service, -+ VCHIQ_SERVICE_CLOSED, NULL, NULL); -+ -+ if (status != VCHIQ_RETRY) { -+ int uc = service->service_use_count; -+ int i; -+ /* Complete the close process */ -+ for (i = 0; i < uc; i++) -+ /* cater for cases where close is forced and the -+ ** client may not close all it's handles */ -+ vchiq_release_service_internal(service); -+ -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_CLOSED) -+ vchiq_free_service_internal(service); -+ else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) { -+ if (is_server) -+ service->closing = 0; -+ -+ up(&service->remove_event); -+ } -+ } else -+ vchiq_set_service_state(service, failstate); -+ -+ return status; -+} -+ -+/* Called by the slot handler */ -+VCHIQ_STATUS_T -+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)", -+ service->state->id, service->localport, close_recvd, -+ srvstate_names[service->srvstate]); -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_CLOSED: -+ case VCHIQ_SRVSTATE_HIDDEN: -+ case VCHIQ_SRVSTATE_LISTENING: -+ case VCHIQ_SRVSTATE_CLOSEWAIT: -+ if (close_recvd) -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_close_service_internal(1) called " -+ "in state %s", -+ srvstate_names[service->srvstate]); -+ else if (is_server) { -+ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { -+ status = VCHIQ_ERROR; -+ } else { -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ if (service->srvstate == -+ VCHIQ_SRVSTATE_CLOSEWAIT) -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ } -+ up(&service->remove_event); -+ } else -+ vchiq_free_service_internal(service); -+ break; -+ case VCHIQ_SRVSTATE_OPENING: -+ if (close_recvd) { -+ /* The open was rejected - tell the user */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_CLOSEWAIT); -+ up(&service->remove_event); -+ } else { -+ /* Shutdown mid-open - let the other side know */ -+ status = queue_message(state, service, -+ VCHIQ_MAKE_MSG -+ (VCHIQ_MSG_CLOSE, -+ service->localport, -+ VCHIQ_MSG_DSTPORT(service->remoteport)), -+ NULL, 0, 0, 0); -+ } -+ break; -+ -+ case VCHIQ_SRVSTATE_OPENSYNC: -+ mutex_lock(&state->sync_mutex); -+ /* Drop through */ -+ -+ case VCHIQ_SRVSTATE_OPEN: -+ if (state->is_master || close_recvd) { -+ if (!do_abort_bulks(service)) -+ status = VCHIQ_RETRY; -+ } -+ -+ release_service_messages(service); -+ -+ if (status == VCHIQ_SUCCESS) -+ status = queue_message(state, service, -+ VCHIQ_MAKE_MSG -+ (VCHIQ_MSG_CLOSE, -+ service->localport, -+ VCHIQ_MSG_DSTPORT(service->remoteport)), -+ NULL, 0, 0, 0); -+ -+ if (status == VCHIQ_SUCCESS) { -+ if (!close_recvd) -+ break; -+ } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { -+ mutex_unlock(&state->sync_mutex); -+ break; -+ } else -+ break; -+ -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ case VCHIQ_SRVSTATE_CLOSESENT: -+ if (!close_recvd) -+ /* This happens when a process is killed mid-close */ -+ break; -+ -+ if (!state->is_master) { -+ if (!do_abort_bulks(service)) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ } -+ -+ if (status == VCHIQ_SUCCESS) -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ case VCHIQ_SRVSTATE_CLOSERECVD: -+ if (!close_recvd && is_server) -+ /* Force into LISTENING mode */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_close_service_internal(%d) called in state %s", -+ close_recvd, srvstate_names[service->srvstate]); -+ break; -+ } -+ -+ return status; -+} -+ -+/* Called from the application process upon process death */ -+void -+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)", -+ state->id, service->localport, service->remoteport); -+ -+ mark_service_closing(service); -+ -+ /* Mark the service for removal by the slot handler */ -+ request_poll(state, service, VCHIQ_POLL_REMOVE); -+} -+ -+/* Called from the slot handler */ -+void -+vchiq_free_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)", -+ state->id, service->localport); -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPENING: -+ case VCHIQ_SRVSTATE_CLOSED: -+ case VCHIQ_SRVSTATE_HIDDEN: -+ case VCHIQ_SRVSTATE_LISTENING: -+ case VCHIQ_SRVSTATE_CLOSEWAIT: -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: fsi - (%d) in state %s", -+ state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ return; -+ } -+ -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE); -+ -+ up(&service->remove_event); -+ -+ /* Release the initial lock */ -+ unlock_service(service); -+} -+ -+VCHIQ_STATUS_T -+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ /* Find all services registered to this client and enable them. */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN) -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ unlock_service(service); -+ } -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, -+ 0, 1) == VCHIQ_RETRY) -+ return VCHIQ_RETRY; -+ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); -+ } -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) { -+ if (down_interruptible(&state->connect) != 0) -+ return VCHIQ_RETRY; -+ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); -+ up(&state->connect); -+ } -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ /* Find all services registered to this client and enable them. */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ (void)vchiq_remove_service(service->handle); -+ unlock_service(service); -+ } -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_pause_internal(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ switch (state->conn_state) { -+ case VCHIQ_CONNSTATE_CONNECTED: -+ /* Request a pause */ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING); -+ request_poll(state, NULL, 0); -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_pause_internal in state %s\n", -+ conn_state_names[state->conn_state]); -+ status = VCHIQ_ERROR; -+ VCHIQ_STATS_INC(state, error_count); -+ break; -+ } -+ -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_resume_internal(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING); -+ request_poll(state, NULL, 0); ++ txstate->residue = bcm2835_dma_desc_size_pos(d, pos); + } else { -+ status = VCHIQ_ERROR; -+ VCHIQ_STATS_INC(state, error_count); ++ txstate->residue = 0; + } + -+ return status; ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return ret; +} + -+VCHIQ_STATUS_T -+vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++static void bcm2835_dma_issue_pending(struct dma_chan *chan) +{ -+ /* Unregister the service */ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ unsigned long flags; + -+ if (!service) -+ return VCHIQ_ERROR; ++ spin_lock_irqsave(&c->vc.lock, flags); ++ if (vchan_issue_pending(&c->vc) && !c->desc) ++ bcm2835_dma_start_desc(c); + -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: close_service:%d", -+ service->state->id, service->localport); ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++} + -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) { -+ unlock_service(service); -+ return VCHIQ_ERROR; ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( ++ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, ++ size_t period_len, enum dma_transfer_direction direction, ++ unsigned long flags) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2835_desc *d; ++ dma_addr_t dev_addr; ++ unsigned int es, sync_type; ++ unsigned int frame, max_size; ++ ++ /* Grab configuration */ ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; + } + -+ mark_service_closing(service); -+ -+ if (current == service->state->slot_handler_thread) { -+ status = vchiq_close_service_internal(service, -+ 0/*!close_recvd*/); -+ BUG_ON(status == VCHIQ_RETRY); ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2835_DMA_S_DREQ; + } else { -+ /* Mark the service for termination by the slot handler */ -+ request_poll(service->state, service, VCHIQ_POLL_TERMINATE); ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2835_DMA_D_DREQ; + } + -+ while (1) { -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) -+ break; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: close_service:%d - waiting in state %s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && -+ (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->srvstate != VCHIQ_SRVSTATE_LISTENING)) -+ status = VCHIQ_ERROR; -+ -+ unlock_service(service); -+ -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ /* Unregister the service */ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (!service) -+ return VCHIQ_ERROR; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: remove_service:%d", -+ service->state->id, service->localport); -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_FREE) { -+ unlock_service(service); -+ return VCHIQ_ERROR; -+ } -+ -+ mark_service_closing(service); -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || -+ (current == service->state->slot_handler_thread)) { -+ /* Make it look like a client, because it must be removed and -+ not left in the LISTENING state. */ -+ service->public_fourcc = VCHIQ_FOURCC_INVALID; -+ -+ status = vchiq_close_service_internal(service, -+ 0/*!close_recvd*/); -+ BUG_ON(status == VCHIQ_RETRY); -+ } else { -+ /* Mark the service for removal by the slot handler */ -+ request_poll(service->state, service, VCHIQ_POLL_REMOVE); -+ } -+ while (1) { -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) -+ break; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: remove_service:%d - waiting in state %s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && -+ (service->srvstate != VCHIQ_SRVSTATE_FREE)) -+ status = VCHIQ_ERROR; -+ -+ unlock_service(service); -+ -+ return status; -+} -+ -+ -+/* This function may be called by kernel threads or user threads. -+ * User threads may receive VCHIQ_RETRY to indicate that a signal has been -+ * received and the call should be retried after being returned to user -+ * context. -+ * When called in blocking mode, the userdata field points to a bulk_waiter -+ * structure. -+ */ -+VCHIQ_STATUS_T -+vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_BULK_QUEUE_T *queue; -+ VCHIQ_BULK_T *bulk; -+ VCHIQ_STATE_T *state; -+ struct bulk_waiter *bulk_waiter = NULL; -+ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; -+ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ if (!service || -+ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || -+ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS)) -+ goto error_exit; -+ -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ bulk_waiter = (struct bulk_waiter *)userdata; -+ sema_init(&bulk_waiter->event, 0); -+ bulk_waiter->actual = 0; -+ bulk_waiter->bulk = NULL; -+ break; -+ case VCHIQ_BULK_MODE_WAITING: -+ bulk_waiter = (struct bulk_waiter *)userdata; -+ bulk = bulk_waiter->bulk; -+ goto waiting; -+ default: -+ goto error_exit; -+ } -+ -+ state = service->state; -+ -+ queue = (dir == VCHIQ_BULK_TRANSMIT) ? -+ &service->bulk_tx : &service->bulk_rx; -+ -+ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ -+ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) { -+ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); -+ do { -+ mutex_unlock(&service->bulk_mutex); -+ if (down_interruptible(&service->bulk_remove_event) -+ != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ if (mutex_lock_interruptible(&service->bulk_mutex) -+ != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ } while (queue->local_insert == queue->remove + -+ VCHIQ_NUM_SERVICE_BULKS); -+ } -+ -+ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; -+ -+ bulk->mode = mode; -+ bulk->dir = dir; -+ bulk->userdata = userdata; -+ bulk->size = size; -+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; -+ -+ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != -+ VCHIQ_SUCCESS) -+ goto unlock_error_exit; -+ -+ wmb(); -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: bt (%d->%d) %cx %x@%x %x", -+ state->id, -+ service->localport, service->remoteport, dir_char, -+ size, (unsigned int)bulk->data, (unsigned int)userdata); -+ -+ if (state->is_master) { -+ queue->local_insert++; -+ if (resolve_bulks(service, queue)) -+ request_poll(state, service, -+ (dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); -+ } else { -+ int payload[2] = { (int)bulk->data, bulk->size }; -+ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) }; -+ -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(dir_msgtype, -+ service->localport, service->remoteport), -+ &element, 1, sizeof(payload), 1); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_complete_bulk(bulk); -+ goto unlock_error_exit; -+ } -+ queue->local_insert++; -+ } -+ -+ mutex_unlock(&service->bulk_mutex); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: bt:%d %cx li=%x ri=%x p=%x", -+ state->id, -+ service->localport, dir_char, -+ queue->local_insert, queue->remote_insert, queue->process); -+ -+waiting: -+ unlock_service(service); -+ -+ status = VCHIQ_SUCCESS; -+ -+ if (bulk_waiter) { -+ bulk_waiter->bulk = bulk; -+ if (down_interruptible(&bulk_waiter->event) != 0) -+ status = VCHIQ_RETRY; -+ else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) -+ status = VCHIQ_ERROR; -+ } -+ -+ return status; -+ -+unlock_error_exit: -+ mutex_unlock(&service->bulk_mutex); -+ -+error_exit: -+ if (service) -+ unlock_service(service); -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, -+ const VCHIQ_ELEMENT_T *elements, unsigned int count) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ unsigned int size = 0; -+ unsigned int i; -+ -+ if (!service || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS)) -+ goto error_exit; -+ -+ for (i = 0; i < (unsigned int)count; i++) { -+ if (elements[i].size) { -+ if (elements[i].data == NULL) { -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ goto error_exit; -+ } -+ size += elements[i].size; -+ } -+ } -+ -+ if (size > VCHIQ_MAX_MSG_SIZE) { -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ goto error_exit; -+ } -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPEN: -+ status = queue_message(service->state, service, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, -+ service->localport, -+ service->remoteport), -+ elements, count, size, 1); -+ break; -+ case VCHIQ_SRVSTATE_OPENSYNC: -+ status = queue_message_sync(service->state, service, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, -+ service->localport, -+ service->remoteport), -+ elements, count, size, 1); ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2835_DMA_DATA_TYPE_S32; + break; + default: -+ status = VCHIQ_ERROR; -+ break; ++ return NULL; + } + -+error_exit: -+ if (service) -+ unlock_service(service); -+ -+ return status; -+} -+ -+void -+vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_STATE_T *state; -+ int slot_index; -+ -+ if (!service) -+ return; -+ -+ state = service->state; -+ remote = state->remote; -+ -+ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header); -+ -+ if ((slot_index >= remote->slot_first) && -+ (slot_index <= remote->slot_last)) { -+ int msgid = header->msgid; -+ if (msgid & VCHIQ_MSGID_CLAIMED) { -+ VCHIQ_SLOT_INFO_T *slot_info = -+ SLOT_INFO_FROM_INDEX(state, slot_index); -+ -+ release_slot(state, slot_info, header, service); -+ } -+ } else if (slot_index == remote->slot_sync) -+ release_message_sync(state, header); -+ -+ unlock_service(service); -+} -+ -+static void -+release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) -+{ -+ header->msgid = VCHIQ_MSGID_PADDING; -+ wmb(); -+ remote_event_signal(&state->remote->sync_release); -+} -+ -+VCHIQ_STATUS_T -+vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ -+ if (!service || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS) || -+ !peer_version) -+ goto exit; -+ *peer_version = service->peer_version; -+ status = VCHIQ_SUCCESS; -+ -+exit: -+ if (service) -+ unlock_service(service); -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_get_config(VCHIQ_INSTANCE_T instance, -+ int config_size, VCHIQ_CONFIG_T *pconfig) -+{ -+ VCHIQ_CONFIG_T config; -+ -+ (void)instance; -+ -+ config.max_msg_size = VCHIQ_MAX_MSG_SIZE; -+ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE; -+ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS; -+ config.max_services = VCHIQ_MAX_SERVICES; -+ config.version = VCHIQ_VERSION; -+ config.version_min = VCHIQ_VERSION_MIN; -+ -+ if (config_size > sizeof(VCHIQ_CONFIG_T)) -+ return VCHIQ_ERROR; -+ -+ memcpy(pconfig, &config, -+ min(config_size, (int)(sizeof(VCHIQ_CONFIG_T)))); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHIQ_SERVICE_OPTION_T option, int value) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ if (service) { -+ switch (option) { -+ case VCHIQ_SERVICE_OPTION_AUTOCLOSE: -+ service->auto_close = value; -+ status = VCHIQ_SUCCESS; -+ break; -+ -+ case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[ -+ service->localport]; -+ if (value == 0) -+ value = service->state->default_slot_quota; -+ if ((value >= service_quota->slot_use_count) && -+ (value < (unsigned short)~0)) { -+ service_quota->slot_quota = value; -+ if ((value >= service_quota->slot_use_count) && -+ (service_quota->message_quota >= -+ service_quota->message_use_count)) { -+ /* Signal the service that it may have -+ ** dropped below its quota */ -+ up(&service_quota->quota_event); -+ } -+ status = VCHIQ_SUCCESS; -+ } -+ } break; -+ -+ case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[ -+ service->localport]; -+ if (value == 0) -+ value = service->state->default_message_quota; -+ if ((value >= service_quota->message_use_count) && -+ (value < (unsigned short)~0)) { -+ service_quota->message_quota = value; -+ if ((value >= -+ service_quota->message_use_count) && -+ (service_quota->slot_quota >= -+ service_quota->slot_use_count)) -+ /* Signal the service that it may have -+ ** dropped below its quota */ -+ up(&service_quota->quota_event); -+ status = VCHIQ_SUCCESS; -+ } -+ } break; -+ -+ case VCHIQ_SERVICE_OPTION_SYNCHRONOUS: -+ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || -+ (service->srvstate == -+ VCHIQ_SRVSTATE_LISTENING)) { -+ service->sync = value; -+ status = VCHIQ_SUCCESS; -+ } -+ break; -+ -+ case VCHIQ_SERVICE_OPTION_TRACE: -+ service->trace = value; -+ status = VCHIQ_SUCCESS; -+ break; -+ -+ default: -+ break; -+ } -+ unlock_service(service); -+ } -+ -+ return status; -+} -+ -+void -+vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, -+ VCHIQ_SHARED_STATE_T *shared, const char *label) -+{ -+ static const char *const debug_names[] = { -+ "", -+ "SLOT_HANDLER_COUNT", -+ "SLOT_HANDLER_LINE", -+ "PARSE_LINE", -+ "PARSE_HEADER", -+ "PARSE_MSGID", -+ "AWAIT_COMPLETION_LINE", -+ "DEQUEUE_MESSAGE_LINE", -+ "SERVICE_CALLBACK_LINE", -+ "MSG_QUEUE_FULL_COUNT", -+ "COMPLETION_QUEUE_FULL_COUNT" -+ }; -+ int i; -+ -+ char buf[80]; -+ int len; -+ len = snprintf(buf, sizeof(buf), -+ " %s: slots %d-%d tx_pos=%x recycle=%x", -+ label, shared->slot_first, shared->slot_last, -+ shared->tx_pos, shared->slot_queue_recycle); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Slots claimed:"); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ for (i = shared->slot_first; i <= shared->slot_last; i++) { -+ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i); -+ if (slot_info.use_count != slot_info.release_count) { -+ len = snprintf(buf, sizeof(buf), -+ " %d: %d/%d", i, slot_info.use_count, -+ slot_info.release_count); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+ } -+ -+ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) { -+ len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", -+ debug_names[i], shared->debug[i], shared->debug[i]); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+} -+ -+void -+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state) -+{ -+ char buf[80]; -+ int len; -+ int i; -+ -+ len = snprintf(buf, sizeof(buf), "State %d: %s", state->id, -+ conn_state_names[state->conn_state]); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " tx_pos=%x(@%x), rx_pos=%x(@%x)", -+ state->local->tx_pos, -+ (uint32_t)state->tx_data + -+ (state->local_tx_pos & VCHIQ_SLOT_MASK), -+ state->rx_pos, -+ (uint32_t)state->rx_data + -+ (state->rx_pos & VCHIQ_SLOT_MASK)); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Version: %d (min %d)", -+ VCHIQ_VERSION, VCHIQ_VERSION_MIN); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ if (VCHIQ_ENABLE_STATS) { -+ len = snprintf(buf, sizeof(buf), -+ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, " -+ "error_count=%d", -+ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, -+ state->stats.error_count); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+ -+ len = snprintf(buf, sizeof(buf), -+ " Slots: %d available (%d data), %d recyclable, %d stalls " -+ "(%d data)", -+ ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - -+ state->local_tx_pos) / VCHIQ_SLOT_SIZE, -+ state->data_quota - state->data_use_count, -+ state->local->slot_queue_recycle - state->slot_queue_available, -+ state->stats.slot_stalls, state->stats.data_stalls); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ vchiq_dump_platform_state(dump_context); -+ -+ vchiq_dump_shared_state(dump_context, state, state->local, "Local"); -+ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote"); -+ -+ vchiq_dump_platform_instances(dump_context); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = find_service_by_port(state, i); -+ -+ if (service) { -+ vchiq_dump_service_state(dump_context, service); -+ unlock_service(service); -+ } -+ } -+} -+ -+void -+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) -+{ -+ char buf[80]; -+ int len; -+ -+ len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)", -+ service->localport, srvstate_names[service->srvstate], -+ service->ref_count - 1); /*Don't include the lock just taken*/ -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_FREE) { -+ char remoteport[30]; -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[service->localport]; -+ int fourcc = service->base.fourcc; -+ int tx_pending, rx_pending; -+ if (service->remoteport != VCHIQ_PORT_FREE) { -+ int len2 = snprintf(remoteport, sizeof(remoteport), -+ "%d", service->remoteport); -+ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) -+ snprintf(remoteport + len2, -+ sizeof(remoteport) - len2, -+ " (client %x)", service->client_id); -+ } else -+ strcpy(remoteport, "n/a"); -+ -+ len += snprintf(buf + len, sizeof(buf) - len, -+ " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)", -+ VCHIQ_FOURCC_AS_4CHARS(fourcc), -+ remoteport, -+ service_quota->message_use_count, -+ service_quota->message_quota, -+ service_quota->slot_use_count, -+ service_quota->slot_quota); -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ tx_pending = service->bulk_tx.local_insert - -+ service->bulk_tx.remote_insert; -+ -+ rx_pending = service->bulk_rx.local_insert - -+ service->bulk_rx.remote_insert; -+ -+ len = snprintf(buf, sizeof(buf), -+ " Bulk: tx_pending=%d (size %d)," -+ " rx_pending=%d (size %d)", -+ tx_pending, -+ tx_pending ? service->bulk_tx.bulks[ -+ BULK_INDEX(service->bulk_tx.remove)].size : 0, -+ rx_pending, -+ rx_pending ? service->bulk_rx.bulks[ -+ BULK_INDEX(service->bulk_rx.remove)].size : 0); -+ -+ if (VCHIQ_ENABLE_STATS) { -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Ctrl: tx_count=%d, tx_bytes=%llu, " -+ "rx_count=%d, rx_bytes=%llu", -+ service->stats.ctrl_tx_count, -+ service->stats.ctrl_tx_bytes, -+ service->stats.ctrl_rx_count, -+ service->stats.ctrl_rx_bytes); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Bulk: tx_count=%d, tx_bytes=%llu, " -+ "rx_count=%d, rx_bytes=%llu", -+ service->stats.bulk_tx_count, -+ service->stats.bulk_tx_bytes, -+ service->stats.bulk_rx_count, -+ service->stats.bulk_rx_bytes); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " %d quota stalls, %d slot stalls, " -+ "%d bulk stalls, %d aborted, %d errors", -+ service->stats.quota_stalls, -+ service->stats.slot_stalls, -+ service->stats.bulk_stalls, -+ service->stats.bulk_aborted_count, -+ service->stats.error_count); -+ } -+ } -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_FREE) -+ vchiq_dump_platform_service_state(dump_context, service); -+} -+ -+ -+void -+vchiq_loud_error_header(void) -+{ -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, "====="); -+} -+ -+void -+vchiq_loud_error_footer(void) -+{ -+ vchiq_log_error(vchiq_core_log_level, "====="); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+} -+ -+ -+VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, -+ size_t numBytes) -+{ -+ const uint8_t *mem = (const uint8_t *)voidMem; -+ size_t offset; -+ char lineBuf[100]; -+ char *s; -+ -+ while (numBytes > 0) { -+ s = lineBuf; -+ -+ for (offset = 0; offset < 16; offset++) { -+ if (offset < numBytes) -+ s += snprintf(s, 4, "%02x ", mem[offset]); -+ else -+ s += snprintf(s, 4, " "); -+ } -+ -+ for (offset = 0; offset < 16; offset++) { -+ if (offset < numBytes) { -+ uint8_t ch = mem[offset]; -+ -+ if ((ch < ' ') || (ch > '~')) -+ ch = '.'; -+ *s++ = (char)ch; -+ } -+ } -+ *s++ = '\0'; -+ -+ if ((label != NULL) && (*label != '\0')) -+ vchiq_log_trace(VCHIQ_LOG_TRACE, -+ "%s: %08x: %s", label, addr, lineBuf); -+ else -+ vchiq_log_trace(VCHIQ_LOG_TRACE, -+ "%08x: %s", addr, lineBuf); -+ -+ addr += 16; -+ mem += 16; -+ if (numBytes > 16) -+ numBytes -= 16; -+ else -+ numBytes = 0; -+ } -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -new file mode 100644 -index 0000000..1b27917 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -@@ -0,0 +1,711 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CORE_H -+#define VCHIQ_CORE_H -+ -+#include -+#include -+#include -+ -+#include "vchiq_cfg.h" -+ -+#include "vchiq.h" -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+#define VCHIQ_LOG_DEFAULT 4 -+#define VCHIQ_LOG_ERROR 3 -+#define VCHIQ_LOG_WARNING 4 -+#define VCHIQ_LOG_INFO 6 -+#define VCHIQ_LOG_TRACE 7 -+ -+#define VCHIQ_LOG_PREFIX KERN_INFO "vchiq: " -+ -+#ifndef vchiq_log_error -+#define vchiq_log_error(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_ERROR) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_warning -+#define vchiq_log_warning(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_WARNING) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_info -+#define vchiq_log_info(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_INFO) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_trace -+#define vchiq_log_trace(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_TRACE) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+ -+#define vchiq_loud_error(...) \ -+ vchiq_log_error(vchiq_core_log_level, "===== " __VA_ARGS__) -+ -+#ifndef vchiq_static_assert -+#define vchiq_static_assert(cond) __attribute__((unused)) \ -+ extern int vchiq_static_assert[(cond) ? 1 : -1] -+#endif -+ -+#define IS_POW2(x) (x && ((x & (x - 1)) == 0)) -+ -+/* Ensure that the slot size and maximum number of slots are powers of 2 */ -+vchiq_static_assert(IS_POW2(VCHIQ_SLOT_SIZE)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); -+ -+#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) -+#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) -+#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \ -+ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) -+ -+#define VCHIQ_MSG_PADDING 0 /* - */ -+#define VCHIQ_MSG_CONNECT 1 /* - */ -+#define VCHIQ_MSG_OPEN 2 /* + (srcport, -), fourcc, client_id */ -+#define VCHIQ_MSG_OPENACK 3 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_CLOSE 4 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_DATA 5 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_BULK_RX 6 /* + (srcport, dstport), data, size */ -+#define VCHIQ_MSG_BULK_TX 7 /* + (srcport, dstport), data, size */ -+#define VCHIQ_MSG_BULK_RX_DONE 8 /* + (srcport, dstport), actual */ -+#define VCHIQ_MSG_BULK_TX_DONE 9 /* + (srcport, dstport), actual */ -+#define VCHIQ_MSG_PAUSE 10 /* - */ -+#define VCHIQ_MSG_RESUME 11 /* - */ -+#define VCHIQ_MSG_REMOTE_USE 12 /* - */ -+#define VCHIQ_MSG_REMOTE_RELEASE 13 /* - */ -+#define VCHIQ_MSG_REMOTE_USE_ACTIVE 14 /* - */ -+ -+#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) -+#define VCHIQ_PORT_FREE 0x1000 -+#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE) -+#define VCHIQ_MAKE_MSG(type, srcport, dstport) \ -+ ((type<<24) | (srcport<<12) | (dstport<<0)) -+#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24) -+#define VCHIQ_MSG_SRCPORT(msgid) \ -+ (unsigned short)(((unsigned int)msgid >> 12) & 0xfff) -+#define VCHIQ_MSG_DSTPORT(msgid) \ -+ ((unsigned short)msgid & 0xfff) -+ -+#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \ -+ ((fourcc) >> 24) & 0xff, \ -+ ((fourcc) >> 16) & 0xff, \ -+ ((fourcc) >> 8) & 0xff, \ -+ (fourcc) & 0xff -+ -+/* Ensure the fields are wide enough */ -+vchiq_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) -+ == 0); -+vchiq_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0, VCHIQ_PORT_MAX, 0)) == 0); -+vchiq_static_assert((unsigned int)VCHIQ_PORT_MAX < -+ (unsigned int)VCHIQ_PORT_FREE); -+ -+#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING, 0, 0) -+#define VCHIQ_MSGID_CLAIMED 0x40000000 -+ -+#define VCHIQ_FOURCC_INVALID 0x00000000 -+#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID) -+ -+#define VCHIQ_BULK_ACTUAL_ABORTED -1 -+ -+typedef uint32_t BITSET_T; -+ -+vchiq_static_assert((sizeof(BITSET_T) * 8) == 32); -+ -+#define BITSET_SIZE(b) ((b + 31) >> 5) -+#define BITSET_WORD(b) (b >> 5) -+#define BITSET_BIT(b) (1 << (b & 31)) -+#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs)) -+#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b)) -+#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b)) -+#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b)) -+ -+#if VCHIQ_ENABLE_STATS -+#define VCHIQ_STATS_INC(state, stat) (state->stats. stat++) -+#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat++) -+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) \ -+ (service->stats. stat += addend) -+#else -+#define VCHIQ_STATS_INC(state, stat) ((void)0) -+#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0) -+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0) -+#endif -+ -+enum { -+ DEBUG_ENTRIES, -+#if VCHIQ_ENABLE_DEBUG -+ DEBUG_SLOT_HANDLER_COUNT, -+ DEBUG_SLOT_HANDLER_LINE, -+ DEBUG_PARSE_LINE, -+ DEBUG_PARSE_HEADER, -+ DEBUG_PARSE_MSGID, -+ DEBUG_AWAIT_COMPLETION_LINE, -+ DEBUG_DEQUEUE_MESSAGE_LINE, -+ DEBUG_SERVICE_CALLBACK_LINE, -+ DEBUG_MSG_QUEUE_FULL_COUNT, -+ DEBUG_COMPLETION_QUEUE_FULL_COUNT, -+#endif -+ DEBUG_MAX -+}; -+ -+#if VCHIQ_ENABLE_DEBUG -+ -+#define DEBUG_INITIALISE(local) int *debug_ptr = (local)->debug; -+#define DEBUG_TRACE(d) \ -+ do { debug_ptr[DEBUG_ ## d] = __LINE__; dsb(); } while (0) -+#define DEBUG_VALUE(d, v) \ -+ do { debug_ptr[DEBUG_ ## d] = (v); dsb(); } while (0) -+#define DEBUG_COUNT(d) \ -+ do { debug_ptr[DEBUG_ ## d]++; dsb(); } while (0) -+ -+#else /* VCHIQ_ENABLE_DEBUG */ -+ -+#define DEBUG_INITIALISE(local) -+#define DEBUG_TRACE(d) -+#define DEBUG_VALUE(d, v) -+#define DEBUG_COUNT(d) -+ -+#endif /* VCHIQ_ENABLE_DEBUG */ -+ -+typedef enum { -+ VCHIQ_CONNSTATE_DISCONNECTED, -+ VCHIQ_CONNSTATE_CONNECTING, -+ VCHIQ_CONNSTATE_CONNECTED, -+ VCHIQ_CONNSTATE_PAUSING, -+ VCHIQ_CONNSTATE_PAUSE_SENT, -+ VCHIQ_CONNSTATE_PAUSED, -+ VCHIQ_CONNSTATE_RESUMING, -+ VCHIQ_CONNSTATE_PAUSE_TIMEOUT, -+ VCHIQ_CONNSTATE_RESUME_TIMEOUT -+} VCHIQ_CONNSTATE_T; -+ -+enum { -+ VCHIQ_SRVSTATE_FREE, -+ VCHIQ_SRVSTATE_HIDDEN, -+ VCHIQ_SRVSTATE_LISTENING, -+ VCHIQ_SRVSTATE_OPENING, -+ VCHIQ_SRVSTATE_OPEN, -+ VCHIQ_SRVSTATE_OPENSYNC, -+ VCHIQ_SRVSTATE_CLOSESENT, -+ VCHIQ_SRVSTATE_CLOSERECVD, -+ VCHIQ_SRVSTATE_CLOSEWAIT, -+ VCHIQ_SRVSTATE_CLOSED -+}; -+ -+enum { -+ VCHIQ_POLL_TERMINATE, -+ VCHIQ_POLL_REMOVE, -+ VCHIQ_POLL_TXNOTIFY, -+ VCHIQ_POLL_RXNOTIFY, -+ VCHIQ_POLL_COUNT -+}; -+ -+typedef enum { -+ VCHIQ_BULK_TRANSMIT, -+ VCHIQ_BULK_RECEIVE -+} VCHIQ_BULK_DIR_T; -+ -+typedef void (*VCHIQ_USERDATA_TERM_T)(void *userdata); -+ -+typedef struct vchiq_bulk_struct { -+ short mode; -+ short dir; -+ void *userdata; -+ VCHI_MEM_HANDLE_T handle; -+ void *data; -+ int size; -+ void *remote_data; -+ int remote_size; -+ int actual; -+} VCHIQ_BULK_T; -+ -+typedef struct vchiq_bulk_queue_struct { -+ int local_insert; /* Where to insert the next local bulk */ -+ int remote_insert; /* Where to insert the next remote bulk (master) */ -+ int process; /* Bulk to transfer next */ -+ int remote_notify; /* Bulk to notify the remote client of next (mstr) */ -+ int remove; /* Bulk to notify the local client of, and remove, -+ ** next */ -+ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS]; -+} VCHIQ_BULK_QUEUE_T; -+ -+typedef struct remote_event_struct { -+ int armed; -+ int fired; -+ struct semaphore *event; -+} REMOTE_EVENT_T; -+ -+typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T; -+ -+typedef struct vchiq_state_struct VCHIQ_STATE_T; -+ -+typedef struct vchiq_slot_struct { -+ char data[VCHIQ_SLOT_SIZE]; -+} VCHIQ_SLOT_T; -+ -+typedef struct vchiq_slot_info_struct { -+ /* Use two counters rather than one to avoid the need for a mutex. */ -+ short use_count; -+ short release_count; -+} VCHIQ_SLOT_INFO_T; -+ -+typedef struct vchiq_service_struct { -+ VCHIQ_SERVICE_BASE_T base; -+ VCHIQ_SERVICE_HANDLE_T handle; -+ unsigned int ref_count; -+ int srvstate; -+ VCHIQ_USERDATA_TERM_T userdata_term; -+ unsigned int localport; -+ unsigned int remoteport; -+ int public_fourcc; -+ int client_id; -+ char auto_close; -+ char sync; -+ char closing; -+ char trace; -+ atomic_t poll_flags; -+ short version; -+ short version_min; -+ short peer_version; -+ -+ VCHIQ_STATE_T *state; -+ VCHIQ_INSTANCE_T instance; -+ -+ int service_use_count; -+ -+ VCHIQ_BULK_QUEUE_T bulk_tx; -+ VCHIQ_BULK_QUEUE_T bulk_rx; -+ -+ struct semaphore remove_event; -+ struct semaphore bulk_remove_event; -+ struct mutex bulk_mutex; -+ -+ struct service_stats_struct { -+ int quota_stalls; -+ int slot_stalls; -+ int bulk_stalls; -+ int error_count; -+ int ctrl_tx_count; -+ int ctrl_rx_count; -+ int bulk_tx_count; -+ int bulk_rx_count; -+ int bulk_aborted_count; -+ uint64_t ctrl_tx_bytes; -+ uint64_t ctrl_rx_bytes; -+ uint64_t bulk_tx_bytes; -+ uint64_t bulk_rx_bytes; -+ } stats; -+} VCHIQ_SERVICE_T; -+ -+/* The quota information is outside VCHIQ_SERVICE_T so that it can be -+ statically allocated, since for accounting reasons a service's slot -+ usage is carried over between users of the same port number. -+ */ -+typedef struct vchiq_service_quota_struct { -+ unsigned short slot_quota; -+ unsigned short slot_use_count; -+ unsigned short message_quota; -+ unsigned short message_use_count; -+ struct semaphore quota_event; -+ int previous_tx_index; -+} VCHIQ_SERVICE_QUOTA_T; -+ -+typedef struct vchiq_shared_state_struct { -+ -+ /* A non-zero value here indicates that the content is valid. */ -+ int initialised; -+ -+ /* The first and last (inclusive) slots allocated to the owner. */ -+ int slot_first; -+ int slot_last; -+ -+ /* The slot allocated to synchronous messages from the owner. */ -+ int slot_sync; -+ -+ /* Signalling this event indicates that owner's slot handler thread -+ ** should run. */ -+ REMOTE_EVENT_T trigger; -+ -+ /* Indicates the byte position within the stream where the next message -+ ** will be written. The least significant bits are an index into the -+ ** slot. The next bits are the index of the slot in slot_queue. */ -+ int tx_pos; -+ -+ /* This event should be signalled when a slot is recycled. */ -+ REMOTE_EVENT_T recycle; -+ -+ /* The slot_queue index where the next recycled slot will be written. */ -+ int slot_queue_recycle; -+ -+ /* This event should be signalled when a synchronous message is sent. */ -+ REMOTE_EVENT_T sync_trigger; -+ -+ /* This event should be signalled when a synchronous message has been -+ ** released. */ -+ REMOTE_EVENT_T sync_release; -+ -+ /* A circular buffer of slot indexes. */ -+ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE]; -+ -+ /* Debugging state */ -+ int debug[DEBUG_MAX]; -+} VCHIQ_SHARED_STATE_T; -+ -+typedef struct vchiq_slot_zero_struct { -+ int magic; -+ short version; -+ short version_min; -+ int slot_zero_size; -+ int slot_size; -+ int max_slots; -+ int max_slots_per_side; -+ int platform_data[2]; -+ VCHIQ_SHARED_STATE_T master; -+ VCHIQ_SHARED_STATE_T slave; -+ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS]; -+} VCHIQ_SLOT_ZERO_T; -+ -+struct vchiq_state_struct { -+ int id; -+ int initialised; -+ VCHIQ_CONNSTATE_T conn_state; -+ int is_master; -+ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_SLOT_T *slot_data; -+ -+ unsigned short default_slot_quota; -+ unsigned short default_message_quota; -+ -+ /* Event indicating connect message received */ -+ struct semaphore connect; -+ -+ /* Mutex protecting services */ -+ struct mutex mutex; -+ VCHIQ_INSTANCE_T *instance; -+ -+ /* Processes incoming messages */ -+ struct task_struct *slot_handler_thread; -+ -+ /* Processes recycled slots */ -+ struct task_struct *recycle_thread; -+ -+ /* Processes synchronous messages */ -+ struct task_struct *sync_thread; -+ -+ /* Local implementation of the trigger remote event */ -+ struct semaphore trigger_event; -+ -+ /* Local implementation of the recycle remote event */ -+ struct semaphore recycle_event; -+ -+ /* Local implementation of the sync trigger remote event */ -+ struct semaphore sync_trigger_event; -+ -+ /* Local implementation of the sync release remote event */ -+ struct semaphore sync_release_event; -+ -+ char *tx_data; -+ char *rx_data; -+ VCHIQ_SLOT_INFO_T *rx_info; -+ -+ struct mutex slot_mutex; -+ -+ struct mutex recycle_mutex; -+ -+ struct mutex sync_mutex; -+ -+ struct mutex bulk_transfer_mutex; -+ -+ /* Indicates the byte position within the stream from where the next -+ ** message will be read. The least significant bits are an index into -+ ** the slot.The next bits are the index of the slot in -+ ** remote->slot_queue. */ -+ int rx_pos; -+ -+ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read -+ from remote->tx_pos. */ -+ int local_tx_pos; -+ -+ /* The slot_queue index of the slot to become available next. */ -+ int slot_queue_available; -+ -+ /* A flag to indicate if any poll has been requested */ -+ int poll_needed; -+ -+ /* Ths index of the previous slot used for data messages. */ -+ int previous_data_index; -+ -+ /* The number of slots occupied by data messages. */ -+ unsigned short data_use_count; -+ -+ /* The maximum number of slots to be occupied by data messages. */ -+ unsigned short data_quota; -+ -+ /* An array of bit sets indicating which services must be polled. */ -+ atomic_t poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; -+ -+ /* The number of the first unused service */ -+ int unused_service; -+ -+ /* Signalled when a free slot becomes available. */ -+ struct semaphore slot_available_event; -+ -+ struct semaphore slot_remove_event; -+ -+ /* Signalled when a free data slot becomes available. */ -+ struct semaphore data_quota_event; -+ -+ /* Incremented when there are bulk transfers which cannot be processed -+ * whilst paused and must be processed on resume */ -+ int deferred_bulks; -+ -+ struct state_stats_struct { -+ int slot_stalls; -+ int data_stalls; -+ int ctrl_tx_count; -+ int ctrl_rx_count; -+ int error_count; -+ } stats; -+ -+ VCHIQ_SERVICE_T * services[VCHIQ_MAX_SERVICES]; -+ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; -+ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; -+ -+ VCHIQ_PLATFORM_STATE_T platform_state; -+}; -+ -+struct bulk_waiter { -+ VCHIQ_BULK_T *bulk; -+ struct semaphore event; -+ int actual; -+}; -+ -+extern spinlock_t bulk_waiter_spinlock; -+ -+extern int vchiq_core_log_level; -+extern int vchiq_core_msg_log_level; -+extern int vchiq_sync_log_level; -+ -+extern VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; -+ -+extern const char * -+get_conn_state_name(VCHIQ_CONNSTATE_T conn_state); -+ -+extern VCHIQ_SLOT_ZERO_T * -+vchiq_init_slots(void *mem_base, int mem_size); -+ -+extern VCHIQ_STATUS_T -+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, -+ int is_master); -+ -+extern VCHIQ_STATUS_T -+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_SERVICE_T * -+vchiq_add_service_internal(VCHIQ_STATE_T *state, -+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, -+ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term); -+ -+extern VCHIQ_STATUS_T -+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id); -+ -+extern VCHIQ_STATUS_T -+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd); -+ -+extern void -+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_free_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_STATUS_T -+vchiq_pause_internal(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_resume_internal(VCHIQ_STATE_T *state); -+ -+extern void -+remote_event_pollall(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir); -+ -+extern void -+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_loud_error_header(void); -+ -+extern void -+vchiq_loud_error_footer(void); -+ -+extern void -+request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type); -+ -+static inline VCHIQ_SERVICE_T * -+handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & -+ (VCHIQ_MAX_STATES - 1)]; -+ if (!state) ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) + return NULL; + -+ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; -+} ++ d->dir = direction; + -+extern VCHIQ_SERVICE_T * -+find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+find_service_by_port(VCHIQ_STATE_T *state, int localport); -+ -+extern VCHIQ_SERVICE_T * -+find_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, -+ int *pidx); -+ -+extern void -+lock_service(VCHIQ_SERVICE_T *service); -+ -+extern void -+unlock_service(VCHIQ_SERVICE_T *service); -+ -+/* The following functions are called from vchiq_core, and external -+** implementations must be provided. */ -+ -+extern VCHIQ_STATUS_T -+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir); -+ -+extern void -+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk); -+ -+extern void -+vchiq_complete_bulk(VCHIQ_BULK_T *bulk); -+ -+extern VCHIQ_STATUS_T -+vchiq_copy_from_user(void *dst, const void *src, int size); -+ -+extern void -+remote_event_signal(REMOTE_EVENT_T *event); -+ -+void -+vchiq_platform_check_suspend(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_paused(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_resume(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_resumed(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump(void *dump_context, const char *str, int len); -+ -+extern void -+vchiq_dump_platform_state(void *dump_context); -+ -+extern void -+vchiq_dump_platform_instances(void *dump_context); -+ -+extern void -+vchiq_dump_platform_service_state(void *dump_context, -+ VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_use_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_release_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_on_remote_use(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_on_remote_release(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_init_state(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_on_remote_use_active(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_use(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_release(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_use_active(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, -+ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate); -+ -+extern void -+vchiq_platform_handle_timeout(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate); -+ -+ -+extern void -+vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, -+ size_t numBytes); -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c -new file mode 100644 -index 0000000..7e03213 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c -@@ -0,0 +1,383 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+#include -+#include "vchiq_core.h" -+#include "vchiq_arm.h" -+#include "vchiq_debugfs.h" -+ -+#ifdef CONFIG_DEBUG_FS -+ -+/**************************************************************************** -+* -+* log category entries -+* -+***************************************************************************/ -+#define DEBUGFS_WRITE_BUF_SIZE 256 -+ -+#define VCHIQ_LOG_ERROR_STR "error" -+#define VCHIQ_LOG_WARNING_STR "warning" -+#define VCHIQ_LOG_INFO_STR "info" -+#define VCHIQ_LOG_TRACE_STR "trace" -+ -+ -+/* Top-level debug info */ -+struct vchiq_debugfs_info { -+ /* Global 'vchiq' debugfs entry used by all instances */ -+ struct dentry *vchiq_cfg_dir; -+ -+ /* one entry per client process */ -+ struct dentry *clients; -+ -+ /* log categories */ -+ struct dentry *log_categories; -+}; -+ -+static struct vchiq_debugfs_info debugfs_info; -+ -+/* Log category debugfs entries */ -+struct vchiq_debugfs_log_entry { -+ const char *name; -+ int *plevel; -+ struct dentry *dir; -+}; -+ -+static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { -+ { "core", &vchiq_core_log_level }, -+ { "msg", &vchiq_core_msg_log_level }, -+ { "sync", &vchiq_sync_log_level }, -+ { "susp", &vchiq_susp_log_level }, -+ { "arm", &vchiq_arm_log_level }, -+}; -+static int n_log_entries = -+ sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); -+ -+ -+static struct dentry *vchiq_clients_top(void); -+static struct dentry *vchiq_debugfs_top(void); -+ -+static int debugfs_log_show(struct seq_file *f, void *offset) -+{ -+ int *levp = f->private; -+ char *log_value = NULL; -+ -+ switch (*levp) { -+ case VCHIQ_LOG_ERROR: -+ log_value = VCHIQ_LOG_ERROR_STR; -+ break; -+ case VCHIQ_LOG_WARNING: -+ log_value = VCHIQ_LOG_WARNING_STR; -+ break; -+ case VCHIQ_LOG_INFO: -+ log_value = VCHIQ_LOG_INFO_STR; -+ break; -+ case VCHIQ_LOG_TRACE: -+ log_value = VCHIQ_LOG_TRACE_STR; -+ break; -+ default: -+ break; -+ } -+ -+ seq_printf(f, "%s\n", log_value ? log_value : "(null)"); -+ -+ return 0; -+} -+ -+static int debugfs_log_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_log_show, inode->i_private); -+} -+ -+static int debugfs_log_write(struct file *file, -+ const char __user *buffer, -+ size_t count, loff_t *ppos) -+{ -+ struct seq_file *f = (struct seq_file *)file->private_data; -+ int *levp = f->private; -+ char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1]; -+ -+ memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1); -+ if (count >= DEBUGFS_WRITE_BUF_SIZE) -+ count = DEBUGFS_WRITE_BUF_SIZE; -+ -+ if (copy_from_user(kbuf, buffer, count) != 0) -+ return -EFAULT; -+ kbuf[count - 1] = 0; -+ -+ if (strncmp("error", kbuf, strlen("error")) == 0) -+ *levp = VCHIQ_LOG_ERROR; -+ else if (strncmp("warning", kbuf, strlen("warning")) == 0) -+ *levp = VCHIQ_LOG_WARNING; -+ else if (strncmp("info", kbuf, strlen("info")) == 0) -+ *levp = VCHIQ_LOG_INFO; -+ else if (strncmp("trace", kbuf, strlen("trace")) == 0) -+ *levp = VCHIQ_LOG_TRACE; ++ if (c->ch >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; + else -+ *levp = VCHIQ_LOG_DEFAULT; ++ max_size = MAX_NORMAL_TRANSFER; ++ period_len = min(period_len, max_size); + -+ *ppos += count; ++ d->frames = (buf_len-1) / period_len + 1; + -+ return count; -+} ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); + -+static const struct file_operations debugfs_log_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_log_open, -+ .write = debugfs_log_write, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } + -+/* create an entry under /vchiq/log for each log category */ -+static int vchiq_debugfs_create_log_entries(struct dentry *top) -+{ -+ struct dentry *dir; -+ size_t i; -+ int ret = 0; -+ dir = debugfs_create_dir("log", vchiq_debugfs_top()); -+ if (!dir) -+ return -ENOMEM; -+ debugfs_info.log_categories = dir; ++ /* ++ * Iterate over all frames, create a control block ++ * for each frame and link them together. ++ */ ++ for (frame = 0; frame < d->frames; frame++) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[frame]; + -+ for (i = 0; i < n_log_entries; i++) { -+ void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; -+ dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, -+ 0644, -+ debugfs_info.log_categories, -+ levp, -+ &debugfs_log_fops); -+ if (!dir) { -+ ret = -ENOMEM; -+ break; ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2835_DMA_D_INC; ++ control_block->src = dev_addr; ++ control_block->dst = buf_addr + frame * period_len; ++ } else { ++ control_block->info = BCM2835_DMA_S_INC; ++ control_block->src = buf_addr + frame * period_len; ++ control_block->dst = dev_addr; + } + -+ vchiq_debugfs_log_entries[i].dir = dir; -+ } -+ return ret; -+} ++ /* Enable interrupt */ ++ control_block->info |= BCM2835_DMA_INT_EN; + -+static int debugfs_usecount_show(struct seq_file *f, void *offset) -+{ -+ VCHIQ_INSTANCE_T instance = f->private; -+ int use_count; ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; + -+ use_count = vchiq_instance_get_use_count(instance); -+ seq_printf(f, "%d\n", use_count); ++ /* Setup DREQ channel */ ++ if (c->cfg.slave_id != 0) ++ control_block->info |= ++ BCM2835_DMA_PER_MAP(c->cfg.slave_id); + -+ return 0; -+} -+ -+static int debugfs_usecount_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_usecount_show, inode->i_private); -+} -+ -+static const struct file_operations debugfs_usecount_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_usecount_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int debugfs_trace_show(struct seq_file *f, void *offset) -+{ -+ VCHIQ_INSTANCE_T instance = f->private; -+ int trace; -+ -+ trace = vchiq_instance_get_trace(instance); -+ seq_printf(f, "%s\n", trace ? "Y" : "N"); -+ -+ return 0; -+} -+ -+static int debugfs_trace_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_trace_show, inode->i_private); -+} -+ -+static int debugfs_trace_write(struct file *file, -+ const char __user *buffer, -+ size_t count, loff_t *ppos) -+{ -+ struct seq_file *f = (struct seq_file *)file->private_data; -+ VCHIQ_INSTANCE_T instance = f->private; -+ char firstchar; -+ -+ if (copy_from_user(&firstchar, buffer, 1) != 0) -+ return -EFAULT; -+ -+ switch (firstchar) { -+ case 'Y': -+ case 'y': -+ case '1': -+ vchiq_instance_set_trace(instance, 1); -+ break; -+ case 'N': -+ case 'n': -+ case '0': -+ vchiq_instance_set_trace(instance, 0); -+ break; -+ default: -+ break; -+ } -+ -+ *ppos += count; -+ -+ return count; -+} -+ -+static const struct file_operations debugfs_trace_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_trace_open, -+ .write = debugfs_trace_write, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+/* add an instance (process) to the debugfs entries */ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) -+{ -+ char pidstr[16]; -+ struct dentry *top, *use_count, *trace; -+ struct dentry *clients = vchiq_clients_top(); -+ -+ snprintf(pidstr, sizeof(pidstr), "%d", -+ vchiq_instance_get_pid(instance)); -+ -+ top = debugfs_create_dir(pidstr, clients); -+ if (!top) -+ goto fail_top; -+ -+ use_count = debugfs_create_file("use_count", -+ 0444, top, -+ instance, -+ &debugfs_usecount_fops); -+ if (!use_count) -+ goto fail_use_count; -+ -+ trace = debugfs_create_file("trace", -+ 0644, top, -+ instance, -+ &debugfs_trace_fops); -+ if (!trace) -+ goto fail_trace; -+ -+ vchiq_instance_get_debugfs_node(instance)->dentry = top; -+ -+ return 0; -+ -+fail_trace: -+ debugfs_remove(use_count); -+fail_use_count: -+ debugfs_remove(top); -+fail_top: -+ return -ENOMEM; -+} -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); -+ debugfs_remove_recursive(node->dentry); -+} -+ -+ -+int vchiq_debugfs_init(void) -+{ -+ BUG_ON(debugfs_info.vchiq_cfg_dir != NULL); -+ -+ debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL); -+ if (debugfs_info.vchiq_cfg_dir == NULL) -+ goto fail; -+ -+ debugfs_info.clients = debugfs_create_dir("clients", -+ vchiq_debugfs_top()); -+ if (!debugfs_info.clients) -+ goto fail; -+ -+ if (vchiq_debugfs_create_log_entries(vchiq_debugfs_top()) != 0) -+ goto fail; -+ -+ return 0; -+ -+fail: -+ vchiq_debugfs_deinit(); -+ vchiq_log_error(vchiq_arm_log_level, -+ "%s: failed to create debugfs directory", -+ __func__); -+ -+ return -ENOMEM; -+} -+ -+/* remove all the debugfs entries */ -+void vchiq_debugfs_deinit(void) -+{ -+ debugfs_remove_recursive(vchiq_debugfs_top()); -+} -+ -+static struct dentry *vchiq_clients_top(void) -+{ -+ return debugfs_info.clients; -+} -+ -+static struct dentry *vchiq_debugfs_top(void) -+{ -+ BUG_ON(debugfs_info.vchiq_cfg_dir == NULL); -+ return debugfs_info.vchiq_cfg_dir; -+} -+ -+#else /* CONFIG_DEBUG_FS */ -+ -+int vchiq_debugfs_init(void) -+{ -+ return 0; -+} -+ -+void vchiq_debugfs_deinit(void) -+{ -+} -+ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) -+{ -+ return 0; -+} -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) -+{ -+} -+ -+#endif /* CONFIG_DEBUG_FS */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h -new file mode 100644 -index 0000000..4d6a378 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h -@@ -0,0 +1,52 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_DEBUGFS_H -+#define VCHIQ_DEBUGFS_H -+ -+#include "vchiq_core.h" -+ -+typedef struct vchiq_debugfs_node_struct -+{ -+ struct dentry *dentry; -+} VCHIQ_DEBUGFS_NODE_T; -+ -+int vchiq_debugfs_init(void); -+ -+void vchiq_debugfs_deinit(void); -+ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance); -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance); -+ -+#endif /* VCHIQ_DEBUGFS_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion -new file mode 100644 -index 0000000..9f5b634 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion -@@ -0,0 +1,87 @@ -+#!/usr/bin/perl -w -+ -+use strict; -+ -+# -+# Generate a version from available information -+# -+ -+my $prefix = shift @ARGV; -+my $root = shift @ARGV; -+ -+ -+if ( not defined $root ) { -+ die "usage: $0 prefix root-dir\n"; -+} -+ -+if ( ! -d $root ) { -+ die "root directory $root not found\n"; -+} -+ -+my $version = "unknown"; -+my $tainted = ""; -+ -+if ( -d "$root/.git" ) { -+ # attempt to work out git version. only do so -+ # on a linux build host, as cygwin builds are -+ # already slow enough -+ -+ if ( -f "/usr/bin/git" || -f "/usr/local/bin/git" ) { -+ if (not open(F, "git --git-dir $root/.git rev-parse --verify HEAD|")) { -+ $version = "no git version"; -+ } -+ else { -+ $version = ; -+ $version =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+ $version =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ } -+ -+ if (open(G, "git --git-dir $root/.git status --porcelain|")) { -+ $tainted = ; -+ $tainted =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+ $tainted =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ if (length $tainted) { -+ $version = join ' ', $version, "(tainted)"; -+ } -+ else { -+ $version = join ' ', $version, "(clean)"; -+ } -+ } -+ } -+} -+ -+my $hostname = `hostname`; -+$hostname =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+$hostname =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ -+ -+print STDERR "Version $version\n"; -+print < -+ -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_hostname, "$hostname" ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_version, "$version" ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_time, __TIME__ ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_date, __DATE__ ); -+ -+const char *vchiq_get_build_hostname( void ) -+{ -+ return vchiq_build_hostname; -+} -+ -+const char *vchiq_get_build_version( void ) -+{ -+ return vchiq_build_version; -+} -+ -+const char *vchiq_get_build_date( void ) -+{ -+ return vchiq_build_date; -+} -+ -+const char *vchiq_get_build_time( void ) -+{ -+ return vchiq_build_time; -+} -+EOF -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h -new file mode 100644 -index 0000000..8067bbe ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h -@@ -0,0 +1,189 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_IF_H -+#define VCHIQ_IF_H -+ -+#include "interface/vchi/vchi_mh.h" -+ -+#define VCHIQ_SERVICE_HANDLE_INVALID 0 -+ -+#define VCHIQ_SLOT_SIZE 4096 -+#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) -+#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ -+ -+#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) \ -+ (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) -+#define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) -+#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) -+ -+typedef enum { -+ VCHIQ_SERVICE_OPENED, /* service, -, - */ -+ VCHIQ_SERVICE_CLOSED, /* service, -, - */ -+ VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */ -+ VCHIQ_BULK_TRANSMIT_DONE, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */ -+} VCHIQ_REASON_T; -+ -+typedef enum { -+ VCHIQ_ERROR = -1, -+ VCHIQ_SUCCESS = 0, -+ VCHIQ_RETRY = 1 -+} VCHIQ_STATUS_T; -+ -+typedef enum { -+ VCHIQ_BULK_MODE_CALLBACK, -+ VCHIQ_BULK_MODE_BLOCKING, -+ VCHIQ_BULK_MODE_NOCALLBACK, -+ VCHIQ_BULK_MODE_WAITING /* Reserved for internal use */ -+} VCHIQ_BULK_MODE_T; -+ -+typedef enum { -+ VCHIQ_SERVICE_OPTION_AUTOCLOSE, -+ VCHIQ_SERVICE_OPTION_SLOT_QUOTA, -+ VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, -+ VCHIQ_SERVICE_OPTION_SYNCHRONOUS, -+ VCHIQ_SERVICE_OPTION_TRACE -+} VCHIQ_SERVICE_OPTION_T; -+ -+typedef struct vchiq_header_struct { -+ /* The message identifier - opaque to applications. */ -+ int msgid; -+ -+ /* Size of message data. */ -+ unsigned int size; -+ -+ char data[0]; /* message */ -+} VCHIQ_HEADER_T; -+ -+typedef struct { -+ const void *data; -+ unsigned int size; -+} VCHIQ_ELEMENT_T; -+ -+typedef unsigned int VCHIQ_SERVICE_HANDLE_T; -+ -+typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, -+ VCHIQ_SERVICE_HANDLE_T, void *); -+ -+typedef struct vchiq_service_base_struct { -+ int fourcc; -+ VCHIQ_CALLBACK_T callback; -+ void *userdata; -+} VCHIQ_SERVICE_BASE_T; -+ -+typedef struct vchiq_service_params_struct { -+ int fourcc; -+ VCHIQ_CALLBACK_T callback; -+ void *userdata; -+ short version; /* Increment for non-trivial changes */ -+ short version_min; /* Update for incompatible changes */ -+} VCHIQ_SERVICE_PARAMS_T; -+ -+typedef struct vchiq_config_struct { -+ unsigned int max_msg_size; -+ unsigned int bulk_threshold; /* The message size above which it -+ is better to use a bulk transfer -+ (<= max_msg_size) */ -+ unsigned int max_outstanding_bulks; -+ unsigned int max_services; -+ short version; /* The version of VCHIQ */ -+ short version_min; /* The minimum compatible version of VCHIQ */ -+} VCHIQ_CONFIG_T; -+ -+typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; -+typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void *cb_arg); -+ -+extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); -+extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); -+extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); -+extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *pservice); -+extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *pservice); -+extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_use_service_no_resume( -+ VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); -+ -+extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, -+ const VCHIQ_ELEMENT_T *elements, unsigned int count); -+extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, -+ VCHIQ_HEADER_T *header); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, -+ const void *data, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, -+ void *data, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle( -+ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, -+ const void *offset, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle( -+ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, -+ void *offset, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, -+ const void *data, unsigned int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, -+ void *data, unsigned int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, -+ VCHI_MEM_HANDLE_T handle, const void *offset, unsigned int size, -+ void *userdata, VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, -+ VCHI_MEM_HANDLE_T handle, void *offset, unsigned int size, -+ void *userdata, VCHIQ_BULK_MODE_T mode); -+extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); -+extern void *vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T service); -+extern int vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, -+ int config_size, VCHIQ_CONFIG_T *pconfig); -+extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, -+ VCHIQ_SERVICE_OPTION_T option, int value); -+ -+extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, -+ VCHIQ_REMOTE_USE_CALLBACK_T callback, void *cb_arg); -+extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_STATUS_T vchiq_dump_phys_mem(VCHIQ_SERVICE_HANDLE_T service, -+ void *ptr, size_t num_bytes); -+ -+extern VCHIQ_STATUS_T vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, -+ short *peer_version); -+ -+#endif /* VCHIQ_IF_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h -new file mode 100644 -index 0000000..6137ae9 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h -@@ -0,0 +1,131 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_IOCTLS_H -+#define VCHIQ_IOCTLS_H -+ -+#include -+#include "vchiq_if.h" -+ -+#define VCHIQ_IOC_MAGIC 0xc4 -+#define VCHIQ_INVALID_HANDLE (~0) -+ -+typedef struct { -+ VCHIQ_SERVICE_PARAMS_T params; -+ int is_open; -+ int is_vchi; -+ unsigned int handle; /* OUT */ -+} VCHIQ_CREATE_SERVICE_T; -+ -+typedef struct { -+ unsigned int handle; -+ unsigned int count; -+ const VCHIQ_ELEMENT_T *elements; -+} VCHIQ_QUEUE_MESSAGE_T; -+ -+typedef struct { -+ unsigned int handle; -+ void *data; -+ unsigned int size; -+ void *userdata; -+ VCHIQ_BULK_MODE_T mode; -+} VCHIQ_QUEUE_BULK_TRANSFER_T; -+ -+typedef struct { -+ VCHIQ_REASON_T reason; -+ VCHIQ_HEADER_T *header; -+ void *service_userdata; -+ void *bulk_userdata; -+} VCHIQ_COMPLETION_DATA_T; -+ -+typedef struct { -+ unsigned int count; -+ VCHIQ_COMPLETION_DATA_T *buf; -+ unsigned int msgbufsize; -+ unsigned int msgbufcount; /* IN/OUT */ -+ void **msgbufs; -+} VCHIQ_AWAIT_COMPLETION_T; -+ -+typedef struct { -+ unsigned int handle; -+ int blocking; -+ unsigned int bufsize; -+ void *buf; -+} VCHIQ_DEQUEUE_MESSAGE_T; -+ -+typedef struct { -+ unsigned int config_size; -+ VCHIQ_CONFIG_T *pconfig; -+} VCHIQ_GET_CONFIG_T; -+ -+typedef struct { -+ unsigned int handle; -+ VCHIQ_SERVICE_OPTION_T option; -+ int value; -+} VCHIQ_SET_SERVICE_OPTION_T; -+ -+typedef struct { -+ void *virt_addr; -+ size_t num_bytes; -+} VCHIQ_DUMP_MEM_T; -+ -+#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) -+#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) -+#define VCHIQ_IOC_CREATE_SERVICE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) -+#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) -+#define VCHIQ_IOC_QUEUE_MESSAGE \ -+ _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) -+#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \ -+ _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) -+#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) -+#define VCHIQ_IOC_AWAIT_COMPLETION \ -+ _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) -+#define VCHIQ_IOC_DEQUEUE_MESSAGE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) -+#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) -+#define VCHIQ_IOC_GET_CONFIG \ -+ _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) -+#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) -+#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) -+#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) -+#define VCHIQ_IOC_SET_SERVICE_OPTION \ -+ _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) -+#define VCHIQ_IOC_DUMP_PHYS_MEM \ -+ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) -+#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) -+#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) -+#define VCHIQ_IOC_MAX 17 -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -new file mode 100644 -index 0000000..be9735f ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -0,0 +1,456 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* ---- Include Files ---------------------------------------------------- */ -+ -+#include -+#include -+#include -+ -+#include "vchiq_core.h" -+#include "vchiq_arm.h" -+ -+/* ---- Public Variables ------------------------------------------------- */ -+ -+/* ---- Private Constants and Types -------------------------------------- */ -+ -+struct bulk_waiter_node { -+ struct bulk_waiter bulk_waiter; -+ int pid; -+ struct list_head list; -+}; -+ -+struct vchiq_instance_struct { -+ VCHIQ_STATE_T *state; -+ -+ int connected; -+ -+ struct list_head bulk_waiter_list; -+ struct mutex bulk_waiter_list_mutex; -+}; -+ -+static VCHIQ_STATUS_T -+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, VCHIQ_BULK_DIR_T dir); -+ -+/**************************************************************************** -+* -+* vchiq_initialise -+* -+***************************************************************************/ -+#define VCHIQ_INIT_RETRIES 10 -+VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_STATE_T *state; -+ VCHIQ_INSTANCE_T instance = NULL; -+ int i; -+ -+ vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); -+ -+ /* VideoCore may not be ready due to boot up timing. -+ It may never be ready if kernel and firmware are mismatched, so don't block forever. */ -+ for (i=0; i0) { -+ vchiq_log_warning(vchiq_core_log_level, -+ "%s: videocore initialized after %d retries\n", __func__, i); -+ } -+ -+ instance = kzalloc(sizeof(*instance), GFP_KERNEL); -+ if (!instance) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%s: error allocating vchiq instance\n", __func__); -+ goto failed; -+ } -+ -+ instance->connected = 0; -+ instance->state = state; -+ mutex_init(&instance->bulk_waiter_list_mutex); -+ INIT_LIST_HEAD(&instance->bulk_waiter_list); -+ -+ *instanceOut = instance; -+ -+ status = VCHIQ_SUCCESS; -+ -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_initialise); -+ -+/**************************************************************************** -+* -+* vchiq_shutdown -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ if (mutex_lock_interruptible(&state->mutex) != 0) -+ return VCHIQ_RETRY; -+ -+ /* Remove all services */ -+ status = vchiq_shutdown_internal(state, instance); -+ -+ mutex_unlock(&state->mutex); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ if (status == VCHIQ_SUCCESS) { -+ struct list_head *pos, *next; -+ list_for_each_safe(pos, next, -+ &instance->bulk_waiter_list) { -+ struct bulk_waiter_node *waiter; -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ vchiq_log_info(vchiq_arm_log_level, -+ "bulk_waiter - cleaned up %x " -+ "for pid %d", -+ (unsigned int)waiter, waiter->pid); -+ kfree(waiter); -+ } -+ kfree(instance); -+ } -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_shutdown); -+ -+/**************************************************************************** -+* -+* vchiq_is_connected -+* -+***************************************************************************/ -+ -+int vchiq_is_connected(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->connected; -+} -+ -+/**************************************************************************** -+* -+* vchiq_connect -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ if (mutex_lock_interruptible(&state->mutex) != 0) { -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s: call to mutex_lock failed", __func__); -+ status = VCHIQ_RETRY; -+ goto failed; -+ } -+ status = vchiq_connect_internal(state, instance); -+ -+ if (status == VCHIQ_SUCCESS) -+ instance->connected = 1; -+ -+ mutex_unlock(&state->mutex); -+ -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_connect); -+ -+/**************************************************************************** -+* -+* vchiq_add_service -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_add_service( -+ VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *phandle) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ VCHIQ_SERVICE_T *service = NULL; -+ int srvstate; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ -+ srvstate = vchiq_is_connected(instance) -+ ? VCHIQ_SRVSTATE_LISTENING -+ : VCHIQ_SRVSTATE_HIDDEN; -+ -+ service = vchiq_add_service_internal( -+ state, -+ params, -+ srvstate, -+ instance, -+ NULL); -+ -+ if (service) { -+ *phandle = service->handle; -+ status = VCHIQ_SUCCESS; -+ } else -+ status = VCHIQ_ERROR; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_add_service); -+ -+/**************************************************************************** -+* -+* vchiq_open_service -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_open_service( -+ VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *phandle) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_STATE_T *state = instance->state; -+ VCHIQ_SERVICE_T *service = NULL; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ -+ if (!vchiq_is_connected(instance)) -+ goto failed; -+ -+ service = vchiq_add_service_internal(state, -+ params, -+ VCHIQ_SRVSTATE_OPENING, -+ instance, -+ NULL); -+ -+ if (service) { -+ status = vchiq_open_service_internal(service, current->pid); -+ if (status == VCHIQ_SUCCESS) -+ *phandle = service->handle; ++ /* Length of a frame */ ++ if (frame != d->frames-1) ++ control_block->length = period_len; + else -+ vchiq_remove_service(service->handle); ++ control_block->length = buf_len - (d->frames - 1) * period_len; ++ ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ * This function is called on cyclic DMA transfers. ++ * Therefore, wrap around at number of frames. ++ */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2835_dma_cb) ++ * ((frame + 1) % d->frames); + } + -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); ++ c->cyclic = true; + -+ return status; ++ return vchan_tx_prep(&c->vc, &d->vd, flags); +} -+EXPORT_SYMBOL(vchiq_open_service); + -+VCHIQ_STATUS_T -+vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, -+ const void *data, unsigned int size, void *userdata) ++ ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( ++ struct dma_chan *chan, struct scatterlist *sgl, ++ unsigned int sg_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) +{ -+ return vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, -+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); -+} -+EXPORT_SYMBOL(vchiq_queue_bulk_transmit); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2835_desc *d; ++ dma_addr_t dev_addr; ++ struct scatterlist *sgent; ++ unsigned int es, sync_type; ++ unsigned int i, j, splitct, max_size; + -+VCHIQ_STATUS_T -+vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, void *userdata) -+{ -+ return vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, data, size, userdata, -+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); -+} -+EXPORT_SYMBOL(vchiq_queue_bulk_receive); ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; ++ } + -+VCHIQ_STATUS_T -+vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, -+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -+{ -+ VCHIQ_STATUS_T status; ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2835_DMA_S_DREQ; ++ } else { ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2835_DMA_D_DREQ; ++ } + -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ status = vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, -+ mode, VCHIQ_BULK_TRANSMIT); -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ status = vchiq_blocking_bulk_transfer(handle, -+ (void *)data, size, VCHIQ_BULK_TRANSMIT); ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2835_DMA_DATA_TYPE_S32; + break; + default: -+ return VCHIQ_ERROR; ++ return NULL; + } + -+ return status; -+} -+EXPORT_SYMBOL(vchiq_bulk_transmit); ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) ++ return NULL; + -+VCHIQ_STATUS_T -+vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -+{ -+ VCHIQ_STATUS_T status; ++ d->dir = direction; + -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ status = vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, data, size, userdata, -+ mode, VCHIQ_BULK_RECEIVE); -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ status = vchiq_blocking_bulk_transfer(handle, -+ (void *)data, size, VCHIQ_BULK_RECEIVE); -+ break; -+ default: -+ return VCHIQ_ERROR; ++ if (c->ch >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; ++ else ++ max_size = MAX_NORMAL_TRANSFER; ++ ++ /* We store the length of the SG list in d->frames ++ taking care to account for splitting up transfers ++ too large for a LITE channel */ ++ ++ d->frames = 0; ++ for_each_sg(sgl, sgent, sg_len, i) { ++ uint32_t len = sg_dma_len(sgent); ++ d->frames += 1 + len / max_size; + } + -+ return status; -+} -+EXPORT_SYMBOL(vchiq_bulk_receive); ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); + -+static VCHIQ_STATUS_T -+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, VCHIQ_BULK_DIR_T dir) -+{ -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_SERVICE_T *service; -+ VCHIQ_STATUS_T status; -+ struct bulk_waiter_node *waiter = NULL; -+ struct list_head *pos; ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } + -+ service = find_service_by_handle(handle); -+ if (!service) -+ return VCHIQ_ERROR; ++ /* ++ * Iterate over all SG entries, create a control block ++ * for each frame and link them together. ++ */ + -+ instance = service->instance; ++ /* we count the number of times an SG entry had to be splitct ++ as a result of using a LITE channel */ ++ splitct = 0; + -+ unlock_service(service); ++ for_each_sg(sgl, sgent, sg_len, i) { ++ dma_addr_t addr = sg_dma_address(sgent); ++ uint32_t len = sg_dma_len(sgent); + -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_for_each(pos, &instance->bulk_waiter_list) { -+ if (list_entry(pos, struct bulk_waiter_node, -+ list)->pid == current->pid) { -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ break; ++ for (j = 0; j < len; j += max_size) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[i+splitct]; ++ ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2835_DMA_D_INC | ++ BCM2835_DMA_D_WIDTH | BCM2835_DMA_S_DREQ; ++ control_block->src = dev_addr; ++ control_block->dst = addr + (dma_addr_t)j; ++ } else { ++ control_block->info = BCM2835_DMA_S_INC | ++ BCM2835_DMA_S_WIDTH | BCM2835_DMA_D_DREQ; ++ control_block->src = addr + (dma_addr_t)j; ++ control_block->dst = dev_addr; ++ } ++ ++ /* Common part */ ++ control_block->info |= BCM2835_DMA_WAITS(SDHCI_BCM_DMA_WAITS); ++ control_block->info |= BCM2835_DMA_WAIT_RESP; ++ ++ /* Enable */ ++ if (i == sg_len-1 && len-j <= max_size) ++ control_block->info |= BCM2835_DMA_INT_EN; ++ ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; ++ ++ /* Setup DREQ channel */ ++ if (c->dreq != 0) ++ control_block->info |= ++ BCM2835_DMA_PER_MAP(c->dreq); ++ ++ /* Length of a frame */ ++ control_block->length = min(len-j, max_size); ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ */ ++ if (i < sg_len-1 || len-j > max_size) { ++ /* next block is the next frame. */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2835_dma_cb) * (i + splitct + 1); ++ } else { ++ /* next block is empty. */ ++ control_block->next = 0; ++ } ++ ++ if (len-j > max_size) ++ splitct++; + } + } -+ mutex_unlock(&instance->bulk_waiter_list_mutex); + -+ if (waiter) { -+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; -+ if (bulk) { -+ /* This thread has an outstanding bulk transfer. */ -+ if ((bulk->data != data) || -+ (bulk->size != size)) { -+ /* This is not a retry of the previous one. -+ ** Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); ++ c->cyclic = false; ++ ++ return vchan_tx_prep(&c->vc, &d->vd, flags); ++} ++ ++static int bcm2835_dma_slave_config(struct dma_chan *chan, ++ struct dma_slave_config *cfg) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ if ((cfg->direction == DMA_DEV_TO_MEM && ++ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ (cfg->direction == DMA_MEM_TO_DEV && ++ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ !is_slave_direction(cfg->direction)) { ++ return -EINVAL; ++ } ++ ++ c->cfg = *cfg; ++ if (!c->dreq) ++ c->dreq = cfg->slave_id; ++ ++ return 0; ++} ++ ++static int bcm2835_dma_terminate_all(struct dma_chan *chan) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); ++ unsigned long flags; ++ int timeout = 10000; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Prevent this channel being scheduled */ ++ spin_lock(&d->lock); ++ list_del_init(&c->node); ++ spin_unlock(&d->lock); ++ ++ /* ++ * Stop DMA activity: we assume the callback will not be called ++ * after bcm_dma_abort() returns (even if it does, it will see ++ * c->desc is NULL and exit.) ++ */ ++ if (c->desc) { ++ c->desc = NULL; ++ bcm2835_dma_abort(c->chan_base); ++ ++ /* Wait for stopping */ ++ while (--timeout) { ++ if (!(readl(c->chan_base + BCM2835_DMA_CS) & ++ BCM2835_DMA_ACTIVE)) ++ break; ++ ++ cpu_relax(); ++ } ++ ++ if (!timeout) ++ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); ++ } ++ ++ vchan_get_all_descriptors(&c->vc, &head); ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ vchan_dma_desc_free_list(&c->vc, &head); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_ARCH_BCM2835 ++static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) ++{ ++ struct bcm2835_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2835_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++#endif ++ ++static int bcm2708_dma_chan_init(struct bcm2835_dmadev *d, ++ void __iomem *chan_base, int chan_id, int irq) ++{ ++ struct bcm2835_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2835_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ c->chan_base = chan_base; ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++ ++ ++static void bcm2835_dma_free(struct bcm2835_dmadev *od) ++{ ++ struct bcm2835_chan *c, *next; ++ ++ list_for_each_entry_safe(c, next, &od->ddev.channels, ++ vc.chan.device_node) { ++ list_del(&c->vc.chan.device_node); ++ tasklet_kill(&c->vc.task); ++ } ++} ++ ++static const struct of_device_id bcm2835_dma_of_match[] = { ++ { .compatible = "brcm,bcm2835-dma", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); ++ ++static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, ++ struct of_dma *ofdma) ++{ ++ struct bcm2835_dmadev *d = ofdma->of_dma_data; ++ struct dma_chan *chan; ++ ++ chan = dma_get_any_slave_channel(&d->ddev); ++ if (!chan) ++ return NULL; ++ ++ /* Set DREQ from param */ ++ to_bcm2835_dma_chan(chan)->dreq = spec->args[0]; ++ ++ return chan; ++} ++ ++static int bcm2835_dma_probe(struct platform_device *pdev) ++{ ++ struct bcm2835_dmadev *od; ++#ifdef CONFIG_ARCH_BCM2835 ++ struct resource *res; ++ void __iomem *base; ++ uint32_t chans_available; ++#endif ++ int rc; ++ int i; ++ int irq; ++ ++ ++ if (!pdev->dev.dma_mask) ++ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ++ ++ /* If CONFIG_ARCH_BCM2835 is selected, device tree is used */ ++ /* hence the difference between probing */ ++ ++#ifndef CONFIG_ARCH_BCM2835 ++ ++ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_terminate_all = bcm2835_dma_terminate_all; ++ od->ddev.device_config = bcm2835_dma_slave_config; ++ od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ for (i = 0; i < 5; i++) { ++ void __iomem *chan_base; ++ int chan_id; ++ ++ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_LITE, ++ &chan_base, ++ &irq); ++ ++ if (chan_id < 0) ++ break; ++ ++ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); ++ if (rc) ++ goto err_no_dma; ++ } ++ ++ if (pdev->dev.of_node) { ++ rc = of_dma_controller_register(pdev->dev.of_node, ++ bcm2835_dma_xlate, od); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "Failed to register DMA controller\n"); ++ goto err_no_dma; ++ } ++ } ++ ++#else ++ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ od->base = base; ++ ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_terminate_all = bcm2835_dma_terminate_all; ++ od->ddev.device_config = bcm2835_dma_slave_config; ++ od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ ++ /* Request DMA channel mask from device tree */ ++ if (of_property_read_u32(pdev->dev.of_node, ++ "brcm,dma-channel-mask", ++ &chans_available)) { ++ dev_err(&pdev->dev, "Failed to get channel mask\n"); ++ rc = -EINVAL; ++ goto err_no_dma; ++ } ++ ++ ++ /* ++ * Do not use the FIQ and BULK channels, ++ * because they are used by the GPU. ++ */ ++ chans_available &= ~(BCM2835_DMA_FIQ_MASK | BCM2835_DMA_BULK_MASK); ++ ++ ++ for (i = 0; i < pdev->num_resources; i++) { ++ irq = platform_get_irq(pdev, i); ++ if (irq < 0) ++ break; ++ ++ if (chans_available & (1 << i)) { ++ rc = bcm2835_dma_chan_init(od, i, irq); ++ if (rc) ++ goto err_no_dma; ++ } ++ } ++ ++ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); ++ ++ /* Device-tree DMA controller registration */ ++ rc = of_dma_controller_register(pdev->dev.of_node, ++ bcm2835_dma_xlate, od); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to register DMA controller\n"); ++ goto err_no_dma; ++ } ++#endif ++ ++ rc = dma_async_device_register(&od->ddev); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "Failed to register slave DMA engine device: %d\n", rc); ++ goto err_no_dma; ++ } ++ ++ dev_info(&pdev->dev, "Load BCM2835 DMA engine driver\n"); ++ ++ return 0; ++ ++err_no_dma: ++ bcm2835_dma_free(od); ++ return rc; ++} ++ ++static int bcm2835_dma_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_dmadev *od = platform_get_drvdata(pdev); ++ ++ dma_async_device_unregister(&od->ddev); ++ bcm2835_dma_free(od); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2835_dma_driver = { ++ .probe = bcm2835_dma_probe, ++ .remove = bcm2835_dma_remove, ++ .driver = { ++ .name = "bcm2708-dmaengine", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(bcm2835_dma_of_match), ++ }, ++}; ++ ++module_platform_driver(bcm2835_dma_driver); ++ ++MODULE_ALIAS("platform:bcm2835-dma"); ++MODULE_DESCRIPTION("BCM2835 DMA engine driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_AUTHOR("Gellert Weisz "); ++MODULE_LICENSE("GPL v2"); + +From 22939895c156c8da951a53e50165247c8f68846c Mon Sep 17 00:00:00 2001 +From: gellert +Date: Fri, 15 Aug 2014 16:35:06 +0100 +Subject: [PATCH 008/104] MMC: added alternative MMC driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mmc: Disable CMD23 transfers on all cards + +Pending wire-level investigation of these types of transfers +and associated errors on bcm2835-mmc, disable for now. Fallback of +CMD18/CMD25 transfers will be used automatically by the MMC layer. + +Reported/Tested-by: Gellert Weisz + +mmc: bcm2835-mmc: enable DT support for all architectures + +Both ARCH_BCM2835 and ARCH_BCM270x are built with OF now. +Enable Device Tree support for all architectures. + +Signed-off-by: Noralf Trønnes + +mmc: bcm2835-mmc: fix probe error handling + +Probe error handling is broken in several places. +Simplify error handling by using device managed functions. +Replace pr_{err,info} with dev_{err,info}. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/mach-bcm2708/bcm2708.c | 31 + + drivers/mmc/core/quirks.c | 4 + + drivers/mmc/host/Kconfig | 58 ++ + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/bcm2835-mmc.c | 1506 +++++++++++++++++++++++++++++++++++++++ + 5 files changed, 1600 insertions(+) + create mode 100644 drivers/mmc/host/bcm2835-mmc.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 5d8657b..d1308ed 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -399,6 +399,34 @@ static struct platform_device bcm2708_systemtimer_device = { + }, + }; + ++#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ ++static struct resource bcm2835_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2835_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2835_emmc_device = { ++ .name = "mmc-bcm2835", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), ++ .resource = bcm2835_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2835_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++#endif /* CONFIG_MMC_BCM2835 */ ++ + static struct resource bcm2708_powerman_resources[] = { + [0] = { + .start = PM_BASE, +@@ -524,6 +552,9 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_uart1_device); + bcm_register_device(&bcm2708_powerman_device); + ++#ifdef CONFIG_MMC_BCM2835 ++ bcm_register_device(&bcm2835_emmc_device); ++#endif + bcm2708_init_led(); + + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { +diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c +index dd1d1e0..f472082 100644 +--- a/drivers/mmc/core/quirks.c ++++ b/drivers/mmc/core/quirks.c +@@ -95,5 +95,9 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) + f->vendor_fixup(card, f->data); + } + } ++ /* SDHCI on BCM2708 - bug causes a certain sequence of CMD23 operations to fail. ++ * Disable this flag for all cards (fall-back to CMD25/CMD18 multi-block transfers). ++ */ ++ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; + } + EXPORT_SYMBOL(mmc_fixup_device); +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 61ac63a..b4928ec 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -4,6 +4,35 @@ + + comment "MMC/SD/SDIO Host Controller Drivers" + ++config MMC_BCM2835 ++ tristate "MMC support on BCM2835" ++ depends on MACH_BCM2708 ++ help ++ This selects the MMC Interface on BCM2835. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_DMA ++ bool "DMA support on BCM2835 Arasan controller" ++ depends on MMC_BCM2835 ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_PIO_DMA_BARRIER ++ int "Block count limit for PIO transfers" ++ depends on MMC_BCM2835 && MMC_BCM2835_DMA ++ range 0 256 ++ default 2 ++ help ++ The inclusive limit in bytes under which PIO will be used instead of DMA ++ ++ If unsure, say 2 here. ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +@@ -328,6 +357,35 @@ config MMC_SDHCI_ST + If you have a controller with this interface, say Y or M here. + If unsure, say N. + ++config MMC_BCM2835 ++ tristate "MMC support on BCM2835" ++ depends on (MACH_BCM2708 || MACH_BCM2709) ++ help ++ This selects the MMC Interface on BCM2835. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_DMA ++ bool "DMA support on BCM2835 Arasan controller" ++ depends on MMC_BCM2835 ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_PIO_DMA_BARRIER ++ int "Block count limit for PIO transfers" ++ depends on MMC_BCM2835 && MMC_BCM2835_DMA ++ range 0 256 ++ default 2 ++ help ++ The inclusive limit in bytes under which PIO will be used instead of DMA ++ ++ If unsure, say 2 here. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 6a7cfe0..9d41de9 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o + obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o + obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o + obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o ++obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o +diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c +new file mode 100644 +index 0000000..28b00d3 +--- /dev/null ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -0,0 +1,1506 @@ ++/* ++ * BCM2835 MMC host driver. ++ * ++ * Author: Gellert Weisz ++ * Copyright 2014 ++ * ++ * Based on ++ * sdhci-bcm2708.c by Broadcom ++ * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko ++ * sdhci.c and sdhci-pci.c by Pierre Ossman ++ * ++ * 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 it will be useful, but WITHOUT ++ * 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 . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdhci.h" ++ ++ ++#define DRIVER_NAME "mmc-bcm2835" ++ ++#define DBG(f, x...) \ ++pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) ++ ++#ifndef CONFIG_MMC_BCM2835_DMA ++ #define FORCE_PIO ++#endif ++ ++ ++/* the inclusive limit in bytes under which PIO will be used instead of DMA */ ++#ifdef CONFIG_MMC_BCM2835_PIO_DMA_BARRIER ++#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_PIO_DMA_BARRIER ++#else ++#define PIO_DMA_BARRIER 00 ++#endif ++ ++#define MIN_FREQ 400000 ++#define TIMEOUT_VAL 0xE ++#define BCM2835_SDHCI_WRITE_DELAY(f) (((2 * 1000000) / f) + 1) ++ ++#ifndef BCM2708_PERI_BASE ++ #define BCM2708_PERI_BASE 0x20000000 ++#endif ++ ++/* FIXME: Needs IOMMU support */ ++#define BCM2835_VCMMU_SHIFT (0x7E000000 - BCM2708_PERI_BASE) ++ ++ ++struct bcm2835_host { ++ spinlock_t lock; ++ ++ void __iomem *ioaddr; ++ u32 phys_addr; ++ ++ struct mmc_host *mmc; ++ ++ u32 timeout; ++ ++ int clock; /* Current clock speed */ ++ u8 pwr; /* Current voltage */ ++ ++ unsigned int max_clk; /* Max possible freq */ ++ unsigned int timeout_clk; /* Timeout freq (KHz) */ ++ unsigned int clk_mul; /* Clock Muliplier value */ ++ ++ struct tasklet_struct finish_tasklet; /* Tasklet structures */ ++ ++ struct timer_list timer; /* Timer for timeouts */ ++ ++ struct sg_mapping_iter sg_miter; /* SG state for PIO */ ++ unsigned int blocks; /* remaining PIO blocks */ ++ ++ int irq; /* Device IRQ */ ++ ++ ++ u32 ier; /* cached registers */ ++ ++ struct mmc_request *mrq; /* Current request */ ++ struct mmc_command *cmd; /* Current command */ ++ struct mmc_data *data; /* Current data request */ ++ unsigned int data_early:1; /* Data finished before cmd */ ++ ++ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ ++ ++ u32 thread_isr; ++ ++ u32 shadow; ++ ++ /*DMA part*/ ++ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ ++ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ ++ struct dma_async_tx_descriptor *tx_desc; /* descriptor */ ++ ++ bool have_dma; ++ bool use_dma; ++ /*end of DMA part*/ ++ ++ int max_delay; /* maximum length of time spent waiting */ ++ ++ int flags; /* Host attributes */ ++#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ ++#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ ++#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ ++#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ ++#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ ++}; ++ ++ ++static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); ++} ++ ++static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++} ++ ++static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) ++{ ++ return readl(host->ioaddr + reg); ++} ++ ++static inline void bcm2835_mmc_writew(struct bcm2835_host *host, u16 val, int reg) ++{ ++ u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow : ++ bcm2835_mmc_readl(host, reg & ~3); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 mask = 0xffff << word_shift; ++ u32 newval = (oldval & ~mask) | (val << word_shift); ++ ++ if (reg == SDHCI_TRANSFER_MODE) ++ host->shadow = newval; ++ else ++ bcm2835_mmc_writel(host, newval, reg & ~3); ++ ++} ++ ++static inline void bcm2835_mmc_writeb(struct bcm2835_host *host, u8 val, int reg) ++{ ++ u32 oldval = bcm2835_mmc_readl(host, reg & ~3); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 mask = 0xff << byte_shift; ++ u32 newval = (oldval & ~mask) | (val << byte_shift); ++ ++ bcm2835_mmc_writel(host, newval, reg & ~3); ++} ++ ++ ++static inline u16 bcm2835_mmc_readw(struct bcm2835_host *host, int reg) ++{ ++ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 word = (val >> word_shift) & 0xffff; ++ ++ return word; ++} ++ ++static inline u8 bcm2835_mmc_readb(struct bcm2835_host *host, int reg) ++{ ++ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 byte = (val >> byte_shift) & 0xff; ++ ++ return byte; ++} ++ ++static void bcm2835_mmc_unsignal_irqs(struct bcm2835_host *host, u32 clear) ++{ ++ u32 ier; ++ ++ ier = bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE); ++ ier &= ~clear; ++ /* change which requests generate IRQs - makes no difference to ++ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ ++ bcm2835_mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++ ++static void bcm2835_mmc_dumpregs(struct bcm2835_host *host) ++{ ++ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", ++ mmc_hostname(host->mmc)); ++ ++ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_DMA_ADDRESS), ++ bcm2835_mmc_readw(host, SDHCI_HOST_VERSION)); ++ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_BLOCK_SIZE), ++ bcm2835_mmc_readw(host, SDHCI_BLOCK_COUNT)); ++ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_ARGUMENT), ++ bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE)); ++ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE), ++ bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL)); ++ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_POWER_CONTROL), ++ bcm2835_mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL)); ++ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_WAKE_UP_CONTROL), ++ bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)); ++ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_TIMEOUT_CONTROL), ++ bcm2835_mmc_readl(host, SDHCI_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_INT_ENABLE), ++ bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE)); ++ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_ACMD12_ERR), ++ bcm2835_mmc_readw(host, SDHCI_SLOT_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES), ++ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES_1)); ++ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_COMMAND), ++ bcm2835_mmc_readl(host, SDHCI_MAX_CURRENT)); ++ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2)); ++ ++ pr_debug(DRIVER_NAME ": ===========================================\n"); ++} ++ ++ ++static void bcm2835_mmc_reset(struct bcm2835_host *host, u8 mask) ++{ ++ unsigned long timeout; ++ ++ bcm2835_mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET); ++ ++ if (mask & SDHCI_RESET_ALL) ++ host->clock = 0; ++ ++ /* Wait max 100 ms */ ++ timeout = 100; ++ ++ /* hw clears the bit when it's done */ ++ while (bcm2835_mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Reset 0x%x never completed.\n", ++ mmc_hostname(host->mmc), (int)mask); ++ bcm2835_mmc_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (100-timeout > 10 && 100-timeout > host->max_delay) { ++ host->max_delay = 100-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++} ++ ++static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); ++ ++static void bcm2835_mmc_init(struct bcm2835_host *host, int soft) ++{ ++ if (soft) ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); ++ else ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | ++ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | ++ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | ++ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | ++ SDHCI_INT_RESPONSE; ++ ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ ++ if (soft) { ++ /* force clock reconfiguration */ ++ host->clock = 0; ++ bcm2835_mmc_set_ios(host->mmc, &host->mmc->ios); ++ } ++} ++ ++ ++ ++static void bcm2835_mmc_finish_data(struct bcm2835_host *host); ++ ++static void bcm2835_mmc_dma_complete(void *param) ++{ ++ struct bcm2835_host *host = param; ++ struct dma_chan *dma_chan; ++ unsigned long flags; ++ u32 dir_data; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { ++ /* otherwise handled in SDHCI IRQ */ ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_mmc_finish_data(host); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_bcm2835_mmc_read_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ ++ u32 uninitialized_var(scratch); ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ if (chunk == 0) { ++ scratch = bcm2835_mmc_readl(host, SDHCI_BUFFER); ++ chunk = 4; ++ } ++ ++ *buf = scratch & 0xFF; ++ ++ buf++; ++ scratch >>= 8; ++ chunk--; ++ len--; ++ } ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++static void bcm2835_bcm2835_mmc_write_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ u32 scratch; ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ chunk = 0; ++ scratch = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ scratch |= (u32)*buf << (chunk * 8); ++ ++ buf++; ++ chunk++; ++ len--; ++ ++ if ((chunk == 4) || ((len == 0) && (blksize == 0))) { ++ mmc_raw_writel(host, scratch, SDHCI_BUFFER); ++ chunk = 0; ++ scratch = 0; + } + } + } + -+ if (!waiter) { -+ waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); -+ if (!waiter) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%s - out of memory", __func__); -+ return VCHIQ_ERROR; -+ } -+ } ++ sg_miter_stop(&host->sg_miter); + -+ status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, -+ data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, -+ dir); -+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || -+ !waiter->bulk_waiter.bulk) { -+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; -+ if (bulk) { -+ /* Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); -+ } -+ kfree(waiter); ++ local_irq_restore(flags); ++} ++ ++ ++static void bcm2835_mmc_transfer_pio(struct bcm2835_host *host) ++{ ++ u32 mask; ++ ++ BUG_ON(!host->data); ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) ++ mask = SDHCI_DATA_AVAILABLE; ++ else ++ mask = SDHCI_SPACE_AVAILABLE; ++ ++ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ ++ if (host->data->flags & MMC_DATA_READ) ++ bcm2835_bcm2835_mmc_read_block_pio(host); ++ else ++ bcm2835_bcm2835_mmc_write_block_pio(host); ++ ++ host->blocks--; ++ ++ /* QUIRK used in sdhci.c removes the 'if' */ ++ /* but it seems this is unnecessary */ ++ if (host->blocks == 0) ++ break; ++ ++ ++ } ++} ++ ++ ++static void bcm2835_mmc_transfer_dma(struct bcm2835_host *host) ++{ ++ u32 len, dir_data, dir_slave; ++ struct dma_async_tx_descriptor *desc = NULL; ++ struct dma_chan *dma_chan; ++ ++ ++ WARN_ON(!host->data); ++ ++ if (!host->data) ++ return; ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) { ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ dir_slave = DMA_DEV_TO_MEM; + } else { -+ waiter->pid = current->pid; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_add(&waiter->list, &instance->bulk_waiter_list); -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ vchiq_log_info(vchiq_arm_log_level, -+ "saved bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dir_slave = DMA_MEM_TO_DEV; ++ } ++ ++ BUG_ON(!dma_chan->device); ++ BUG_ON(!dma_chan->device->dev); ++ BUG_ON(!host->data->sg); ++ ++ len = dma_map_sg(dma_chan->device->dev, host->data->sg, ++ host->data->sg_len, dir_data); ++ if (len > 0) { ++ desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg, ++ len, dir_slave, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ } else { ++ dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); ++ } ++ if (desc) { ++ bcm2835_mmc_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ host->tx_desc = desc; ++ desc->callback = bcm2835_mmc_dma_complete; ++ desc->callback_param = host; ++ dmaengine_submit(desc); ++ dma_async_issue_pending(dma_chan); + } + -+ return status; +} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h -new file mode 100644 -index 0000000..d02e776 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h -@@ -0,0 +1,71 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ + -+#ifndef VCHIQ_MEMDRV_H -+#define VCHIQ_MEMDRV_H + -+/* ---- Include Files ----------------------------------------------------- */ + -+#include -+#include "vchiq_if.h" ++static void bcm2835_mmc_set_transfer_irqs(struct bcm2835_host *host) ++{ ++ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; ++ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; + -+/* ---- Constants and Types ---------------------------------------------- */ ++ if (host->use_dma) ++ host->ier = (host->ier & ~pio_irqs) | dma_irqs; ++ else ++ host->ier = (host->ier & ~dma_irqs) | pio_irqs; + -+typedef struct { -+ void *armSharedMemVirt; -+ dma_addr_t armSharedMemPhys; -+ size_t armSharedMemSize; ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++} + -+ void *vcSharedMemVirt; -+ dma_addr_t vcSharedMemPhys; -+ size_t vcSharedMemSize; -+} VCHIQ_SHARED_MEM_INFO_T; + -+/* ---- Variable Externs ------------------------------------------------- */ ++static void bcm2835_mmc_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ u8 count; ++ struct mmc_data *data = cmd->data; + -+/* ---- Function Prototypes ---------------------------------------------- */ ++ WARN_ON(host->data); + -+void vchiq_get_shared_mem_info(VCHIQ_SHARED_MEM_INFO_T *info); ++ if (data || (cmd->flags & MMC_RSP_BUSY)) { ++ count = TIMEOUT_VAL; ++ bcm2835_mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL); ++ } + -+VCHIQ_STATUS_T vchiq_memdrv_initialise(void); ++ if (!data) ++ return; + -+VCHIQ_STATUS_T vchiq_userdrv_create_instance( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ /* Sanity checks */ ++ BUG_ON(data->blksz * data->blocks > 524288); ++ BUG_ON(data->blksz > host->mmc->max_blk_size); ++ BUG_ON(data->blocks > 65535); + -+VCHIQ_STATUS_T vchiq_userdrv_suspend( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ host->data = data; ++ host->data_early = 0; ++ host->data->bytes_xfered = 0; + -+VCHIQ_STATUS_T vchiq_userdrv_resume( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); + ++ if (!(host->flags & SDHCI_REQ_USE_DMA)) { ++ int flags; ++ ++ flags = SG_MITER_ATOMIC; ++ if (host->data->flags & MMC_DATA_READ) ++ flags |= SG_MITER_TO_SG; ++ else ++ flags |= SG_MITER_FROM_SG; ++ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); ++ host->blocks = data->blocks; ++ } ++ ++ host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER; ++ ++ bcm2835_mmc_set_transfer_irqs(host); ++ ++ /* Set the DMA boundary value and block size */ ++ bcm2835_mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, ++ data->blksz), SDHCI_BLOCK_SIZE); ++ bcm2835_mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT); ++ ++ BUG_ON(!host->data); ++} ++ ++static void bcm2835_mmc_set_transfer_mode(struct bcm2835_host *host, ++ struct mmc_command *cmd) ++{ ++ u16 mode; ++ struct mmc_data *data = cmd->data; ++ ++ if (data == NULL) { ++ /* clear Auto CMD settings for no data CMDs */ ++ mode = bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE); ++ bcm2835_mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | ++ SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); ++ return; ++ } ++ ++ WARN_ON(!host->data); ++ ++ mode = SDHCI_TRNS_BLK_CNT_EN; ++ ++ if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) { ++ mode |= SDHCI_TRNS_MULTI; ++ ++ /* ++ * If we are sending CMD23, CMD12 never gets sent ++ * on successful completion (so no Auto-CMD12). ++ */ ++ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) ++ mode |= SDHCI_TRNS_AUTO_CMD12; ++ else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { ++ mode |= SDHCI_TRNS_AUTO_CMD23; ++ bcm2835_mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); ++ } ++ } ++ ++ if (data->flags & MMC_DATA_READ) ++ mode |= SDHCI_TRNS_READ; ++ if (host->flags & SDHCI_REQ_USE_DMA) ++ mode |= SDHCI_TRNS_DMA; ++ ++ bcm2835_mmc_writew(host, mode, SDHCI_TRANSFER_MODE); ++} ++ ++void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ int flags; ++ u32 mask; ++ unsigned long timeout; ++ ++ WARN_ON(host->cmd); ++ ++ /* Wait max 10 ms */ ++ timeout = 1000; ++ ++ mask = SDHCI_CMD_INHIBIT; ++ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) ++ mask |= SDHCI_DATA_INHIBIT; ++ ++ /* We shouldn't wait for data inihibit for stop commands, even ++ though they might use busy signaling */ ++ if (host->mrq->data && (cmd == host->mrq->data->stop)) ++ mask &= ~SDHCI_DATA_INHIBIT; ++ ++ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Controller never released inhibit bit(s).\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ timeout--; ++ udelay(10); ++ } ++ ++ if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) { ++ host->max_delay = (1000-timeout)/100; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ timeout = jiffies; ++#ifdef CONFIG_ARCH_BCM2835 ++ if (!cmd->data && cmd->busy_timeout > 9000) ++ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; ++ else +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h -new file mode 100644 -index 0000000..54a3ece ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h -@@ -0,0 +1,58 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ ++ timeout += 10 * HZ; ++ mod_timer(&host->timer, timeout); + -+#ifndef VCHIQ_PAGELIST_H -+#define VCHIQ_PAGELIST_H ++ host->cmd = cmd; + -+#ifndef PAGE_SIZE -+#define PAGE_SIZE 4096 -+#endif -+#define CACHE_LINE_SIZE 32 -+#define PAGELIST_WRITE 0 -+#define PAGELIST_READ 1 -+#define PAGELIST_READ_WITH_FRAGMENTS 2 ++ bcm2835_mmc_prepare_data(host, cmd); + -+typedef struct pagelist_struct { -+ unsigned long length; -+ unsigned short type; -+ unsigned short offset; -+ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following -+ pages at consecutive addresses. */ -+} PAGELIST_T; ++ bcm2835_mmc_writel(host, cmd->arg, SDHCI_ARGUMENT); + -+typedef struct fragments_struct { -+ char headbuf[CACHE_LINE_SIZE]; -+ char tailbuf[CACHE_LINE_SIZE]; -+} FRAGMENTS_T; ++ bcm2835_mmc_set_transfer_mode(host, cmd); + -+#endif /* VCHIQ_PAGELIST_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -new file mode 100644 -index 0000000..72eacdaf ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -0,0 +1,853 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include -+#include -+ -+#include "interface/vchi/vchi.h" -+#include "vchiq.h" -+#include "vchiq_core.h" -+ -+#include "vchiq_util.h" -+ -+#include -+ -+#define vchiq_status_to_vchi(status) ((int32_t)status) -+ -+typedef struct { -+ VCHIQ_SERVICE_HANDLE_T handle; -+ -+ VCHIU_QUEUE_T queue; -+ -+ VCHI_CALLBACK_T callback; -+ void *callback_param; -+} SHIM_SERVICE_T; -+ -+/* ---------------------------------------------------------------------- -+ * return pointer to the mphi message driver function table -+ * -------------------------------------------------------------------- */ -+const VCHI_MESSAGE_DRIVER_T * -+vchi_mphi_message_driver_func_table(void) -+{ -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------------- -+ * return a pointer to the 'single' connection driver fops -+ * -------------------------------------------------------------------- */ -+const VCHI_CONNECTION_API_T * -+single_get_func_table(void) -+{ -+ return NULL; -+} -+ -+VCHI_CONNECTION_T *vchi_create_connection( -+ const VCHI_CONNECTION_API_T *function_table, -+ const VCHI_MESSAGE_DRIVER_T *low_level) -+{ -+ (void)function_table; -+ (void)low_level; -+ return NULL; -+} -+ -+/*********************************************************** -+ * Name: vchi_msg_peek -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle, -+ * void **data, -+ * uint32_t *msg_size, -+ -+ -+ * VCHI_FLAGS_T flags -+ * -+ * Description: Routine to return a pointer to the current message (to allow in -+ * place processing). The message can be removed using -+ * vchi_msg_remove when you're finished -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_peek(VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_peek(&service->queue); -+ -+ *data = header->data; -+ *msg_size = header->size; -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_peek); -+ -+/*********************************************************** -+ * Name: vchi_msg_remove -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle, -+ * -+ * Description: Routine to remove a message (after it has been read with -+ * vchi_msg_peek) -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_remove(VCHI_SERVICE_HANDLE_T handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ vchiq_release_message(service->handle, header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_remove); -+ -+/*********************************************************** -+ * Name: vchi_msg_queue -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * const void *data, -+ * uint32_t data_size, -+ * VCHI_FLAGS_T flags, -+ * void *msg_handle, -+ * -+ * Description: Thin wrapper to queue a message onto a connection -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_ELEMENT_T element = {data, data_size}; -+ VCHIQ_STATUS_T status; -+ -+ (void)msg_handle; -+ -+ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); -+ -+ status = vchiq_queue_message(service->handle, &element, 1); -+ -+ /* vchiq_queue_message() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_queue_message(service->handle, &element, 1); ++ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { ++ pr_err("%s: Unsupported response type!\n", ++ mmc_hostname(host->mmc)); ++ cmd->error = -EINVAL; ++ tasklet_schedule(&host->finish_tasklet); ++ return; + } + -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_msg_queue); ++ if (!(cmd->flags & MMC_RSP_PRESENT)) ++ flags = SDHCI_CMD_RESP_NONE; ++ else if (cmd->flags & MMC_RSP_136) ++ flags = SDHCI_CMD_RESP_LONG; ++ else if (cmd->flags & MMC_RSP_BUSY) ++ flags = SDHCI_CMD_RESP_SHORT_BUSY; ++ else ++ flags = SDHCI_CMD_RESP_SHORT; + -+/*********************************************************** -+ * Name: vchi_bulk_queue_receive -+ * -+ * Arguments: VCHI_BULK_HANDLE_T handle, -+ * void *data_dst, -+ * const uint32_t data_size, -+ * VCHI_FLAGS_T flags -+ * void *bulk_handle -+ * -+ * Description: Routine to setup a rcv buffer -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_bulk_queue_receive(VCHI_SERVICE_HANDLE_T handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_BULK_MODE_T mode; -+ VCHIQ_STATUS_T status; ++ if (cmd->flags & MMC_RSP_CRC) ++ flags |= SDHCI_CMD_CRC; ++ if (cmd->flags & MMC_RSP_OPCODE) ++ flags |= SDHCI_CMD_INDEX; + -+ switch ((int)flags) { -+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE -+ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ WARN_ON(!service->callback); -+ mode = VCHIQ_BULK_MODE_CALLBACK; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: -+ mode = VCHIQ_BULK_MODE_BLOCKING; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ case VCHI_FLAGS_NONE: -+ mode = VCHIQ_BULK_MODE_NOCALLBACK; -+ break; -+ default: -+ WARN(1, "unsupported message\n"); -+ return vchiq_status_to_vchi(VCHIQ_ERROR); -+ } ++ if (cmd->data) ++ flags |= SDHCI_CMD_DATA; + -+ status = vchiq_bulk_receive(service->handle, data_dst, data_size, -+ bulk_handle, mode); -+ -+ /* vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_bulk_receive(service->handle, data_dst, -+ data_size, bulk_handle, mode); -+ } -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_bulk_queue_receive); -+ -+/*********************************************************** -+ * Name: vchi_bulk_queue_transmit -+ * -+ * Arguments: VCHI_BULK_HANDLE_T handle, -+ * const void *data_src, -+ * uint32_t data_size, -+ * VCHI_FLAGS_T flags, -+ * void *bulk_handle -+ * -+ * Description: Routine to transmit some data -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_bulk_queue_transmit(VCHI_SERVICE_HANDLE_T handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_BULK_MODE_T mode; -+ VCHIQ_STATUS_T status; -+ -+ switch ((int)flags) { -+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE -+ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ WARN_ON(!service->callback); -+ mode = VCHIQ_BULK_MODE_CALLBACK; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: -+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: -+ mode = VCHIQ_BULK_MODE_BLOCKING; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ case VCHI_FLAGS_NONE: -+ mode = VCHIQ_BULK_MODE_NOCALLBACK; -+ break; -+ default: -+ WARN(1, "unsupported message\n"); -+ return vchiq_status_to_vchi(VCHIQ_ERROR); -+ } -+ -+ status = vchiq_bulk_transmit(service->handle, data_src, data_size, -+ bulk_handle, mode); -+ -+ /* vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_bulk_transmit(service->handle, data_src, -+ data_size, bulk_handle, mode); -+ } -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_bulk_queue_transmit); -+ -+/*********************************************************** -+ * Name: vchi_msg_dequeue -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * void *data, -+ * uint32_t max_data_size_to_read, -+ * uint32_t *actual_msg_size -+ * VCHI_FLAGS_T flags -+ * -+ * Description: Routine to dequeue a message into the supplied buffer -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_dequeue(VCHI_SERVICE_HANDLE_T handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ memcpy(data, header->data, header->size < max_data_size_to_read ? -+ header->size : max_data_size_to_read); -+ -+ *actual_msg_size = header->size; -+ -+ vchiq_release_message(service->handle, header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_dequeue); -+ -+/*********************************************************** -+ * Name: vchi_msg_queuev -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * VCHI_MSG_VECTOR_T *vector, -+ * uint32_t count, -+ * VCHI_FLAGS_T flags, -+ * void *msg_handle -+ * -+ * Description: Thin wrapper to queue a message onto a connection -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+ -+vchiq_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); -+vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == -+ offsetof(VCHIQ_ELEMENT_T, data)); -+vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == -+ offsetof(VCHIQ_ELEMENT_T, size)); -+ -+int32_t vchi_msg_queuev(VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ -+ (void)msg_handle; -+ -+ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); -+ -+ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, -+ (const VCHIQ_ELEMENT_T *)vector, count)); -+} -+EXPORT_SYMBOL(vchi_msg_queuev); -+ -+/*********************************************************** -+ * Name: vchi_held_msg_release -+ * -+ * Arguments: VCHI_HELD_MSG_T *message -+ * -+ * Description: Routine to release a held message (after it has been read with -+ * vchi_msg_hold) -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) -+{ -+ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, -+ (VCHIQ_HEADER_T *)message->message); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_held_msg_release); -+ -+/*********************************************************** -+ * Name: vchi_msg_hold -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * void **data, -+ * uint32_t *msg_size, -+ * VCHI_FLAGS_T flags, -+ * VCHI_HELD_MSG_T *message_handle -+ * -+ * Description: Routine to return a pointer to the current message (to allow -+ * in place processing). The message is dequeued - don't forget -+ * to release the message using vchi_held_msg_release when you're -+ * finished. -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags, -+ VCHI_HELD_MSG_T *message_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ *data = header->data; -+ *msg_size = header->size; -+ -+ message_handle->service = -+ (struct opaque_vchi_service_t *)service->handle; -+ message_handle->message = header; -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_hold); -+ -+/*********************************************************** -+ * Name: vchi_initialise -+ * -+ * Arguments: VCHI_INSTANCE_T *instance_handle -+ * -+ * Description: Initialises the hardware but does not transmit anything -+ * When run as a Host App this will be called twice hence the need -+ * to malloc the state information -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+ -+int32_t vchi_initialise(VCHI_INSTANCE_T *instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_STATUS_T status; -+ -+ status = vchiq_initialise(&instance); -+ -+ *instance_handle = (VCHI_INSTANCE_T)instance; -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_initialise); -+ -+/*********************************************************** -+ * Name: vchi_connect -+ * -+ * Arguments: VCHI_CONNECTION_T **connections -+ * const uint32_t num_connections -+ * VCHI_INSTANCE_T instance_handle) -+ * -+ * Description: Starts the command service on each connection, -+ * causing INIT messages to be pinged back and forth -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+int32_t vchi_connect(VCHI_CONNECTION_T **connections, -+ const uint32_t num_connections, -+ VCHI_INSTANCE_T instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ -+ (void)connections; -+ (void)num_connections; -+ -+ return vchiq_connect(instance); -+} -+EXPORT_SYMBOL(vchi_connect); -+ -+ -+/*********************************************************** -+ * Name: vchi_disconnect -+ * -+ * Arguments: VCHI_INSTANCE_T instance_handle -+ * -+ * Description: Stops the command service on each connection, -+ * causing DE-INIT messages to be pinged back and forth -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ return vchiq_status_to_vchi(vchiq_shutdown(instance)); -+} -+EXPORT_SYMBOL(vchi_disconnect); -+ -+ -+/*********************************************************** -+ * Name: vchi_service_open -+ * Name: vchi_service_create -+ * -+ * Arguments: VCHI_INSTANCE_T *instance_handle -+ * SERVICE_CREATION_T *setup, -+ * VCHI_SERVICE_HANDLE_T *handle -+ * -+ * Description: Routine to open a service -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+ -+static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) -+{ -+ SHIM_SERVICE_T *service = -+ (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); -+ -+ if (!service->callback) -+ goto release; -+ -+ switch (reason) { -+ case VCHIQ_MESSAGE_AVAILABLE: -+ vchiu_queue_push(&service->queue, header); -+ -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_MSG_AVAILABLE, NULL); -+ -+ goto done; -+ break; -+ -+ case VCHIQ_BULK_TRANSMIT_DONE: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_SENT, bulk_user); -+ break; -+ -+ case VCHIQ_BULK_RECEIVE_DONE: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_RECEIVED, bulk_user); -+ break; -+ -+ case VCHIQ_SERVICE_CLOSED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_SERVICE_CLOSED, NULL); -+ break; -+ -+ case VCHIQ_SERVICE_OPENED: -+ /* No equivalent VCHI reason */ -+ break; -+ -+ case VCHIQ_BULK_TRANSMIT_ABORTED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, -+ bulk_user); -+ break; -+ -+ case VCHIQ_BULK_RECEIVE_ABORTED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, -+ bulk_user); -+ break; -+ -+ default: -+ WARN(1, "not supported\n"); -+ break; -+ } -+ -+release: -+ vchiq_release_message(service->handle, header); -+done: -+ return VCHIQ_SUCCESS; ++ bcm2835_mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); +} + -+static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, -+ SERVICE_CREATION_T *setup) ++ ++static void bcm2835_mmc_finish_data(struct bcm2835_host *host) +{ -+ SHIM_SERVICE_T *service = kzalloc(sizeof(SHIM_SERVICE_T), GFP_KERNEL); ++ struct mmc_data *data; + -+ (void)instance; ++ BUG_ON(!host->data); + -+ if (service) { -+ if (vchiu_queue_init(&service->queue, 64)) { -+ service->callback = setup->callback; -+ service->callback_param = setup->callback_param; ++ data = host->data; ++ host->data = NULL; ++ ++ if (data->error) ++ data->bytes_xfered = 0; ++ else ++ data->bytes_xfered = data->blksz * data->blocks; ++ ++ /* ++ * Need to send CMD12 if - ++ * a) open-ended multiblock transfer (no CMD23) ++ * b) error in multiblock transfer ++ */ ++ if (data->stop && ++ (data->error || ++ !host->mrq->sbc)) { ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (data->error) { ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); ++ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ bcm2835_mmc_send_command(host, data->stop); ++ } else ++ tasklet_schedule(&host->finish_tasklet); ++} ++ ++static void bcm2835_mmc_finish_command(struct bcm2835_host *host) ++{ ++ int i; ++ ++ BUG_ON(host->cmd == NULL); ++ ++ if (host->cmd->flags & MMC_RSP_PRESENT) { ++ if (host->cmd->flags & MMC_RSP_136) { ++ /* CRC is stripped so we need to do some shifting. */ ++ for (i = 0; i < 4; i++) { ++ host->cmd->resp[i] = bcm2835_mmc_readl(host, ++ SDHCI_RESPONSE + (3-i)*4) << 8; ++ if (i != 3) ++ host->cmd->resp[i] |= ++ bcm2835_mmc_readb(host, ++ SDHCI_RESPONSE + (3-i)*4-1); ++ } + } else { -+ kfree(service); -+ service = NULL; ++ host->cmd->resp[0] = bcm2835_mmc_readl(host, SDHCI_RESPONSE); + } + } + -+ return service; -+} ++ host->cmd->error = 0; + -+static void service_free(SHIM_SERVICE_T *service) -+{ -+ if (service) { -+ vchiu_queue_delete(&service->queue); -+ kfree(service); ++ /* Finished CMD23, now send actual command. */ ++ if (host->cmd == host->mrq->sbc) { ++ host->cmd = NULL; ++ bcm2835_mmc_send_command(host, host->mrq->cmd); ++ ++ if (host->mrq->cmd->data && host->use_dma) { ++ /* DMA transfer starts now, PIO starts after interrupt */ ++ bcm2835_mmc_transfer_dma(host); ++ } ++ } else { ++ ++ /* Processed actual command. */ ++ if (host->data && host->data_early) ++ bcm2835_mmc_finish_data(host); ++ ++ if (!host->cmd->data) ++ tasklet_schedule(&host->finish_tasklet); ++ ++ host->cmd = NULL; + } +} + -+int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle) ++ ++static void bcm2835_mmc_timeout_timer(unsigned long data) +{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ if (service) { -+ VCHIQ_SERVICE_PARAMS_T params; -+ VCHIQ_STATUS_T status; ++ struct bcm2835_host *host; ++ unsigned long flags; + -+ memset(¶ms, 0, sizeof(params)); -+ params.fourcc = setup->service_id; -+ params.callback = shim_callback; -+ params.userdata = service; -+ params.version = setup->version.version; -+ params.version_min = setup->version.version_min; ++ host = (struct bcm2835_host *)data; + -+ status = vchiq_open_service(instance, ¶ms, -+ &service->handle); -+ if (status != VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->mrq) { ++ pr_err("%s: Timeout waiting for hardware interrupt.\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ ++ if (host->data) { ++ host->data->error = -ETIMEDOUT; ++ bcm2835_mmc_finish_data(host); ++ } else { ++ if (host->cmd) ++ host->cmd->error = -ETIMEDOUT; ++ else ++ host->mrq->cmd->error = -ETIMEDOUT; ++ ++ tasklet_schedule(&host->finish_tasklet); + } + } + -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ -+ return (service != NULL) ? 0 : -1; ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); +} -+EXPORT_SYMBOL(vchi_service_open); + -+int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle) ++ ++static void bcm2835_mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) +{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ if (service) { -+ VCHIQ_SERVICE_PARAMS_T params; -+ VCHIQ_STATUS_T status; ++ if (!(host->flags & SDHCI_DEVICE_DEAD)) { ++ if (enable) ++ host->ier |= SDHCI_INT_CARD_INT; ++ else ++ host->ier &= ~SDHCI_INT_CARD_INT; + -+ memset(¶ms, 0, sizeof(params)); -+ params.fourcc = setup->service_id; -+ params.callback = shim_callback; -+ params.userdata = service; -+ params.version = setup->version.version; -+ params.version_min = setup->version.version_min; -+ status = vchiq_add_service(instance, ¶ms, &service->handle); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ mmiowb(); ++ } ++} + -+ if (status != VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; -+ } ++static void bcm2835_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (enable) ++ host->flags |= SDHCI_SDIO_IRQ_ENABLED; ++ else ++ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; ++ ++ bcm2835_mmc_enable_sdio_irq_nolock(host, enable); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_mmc_cmd_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->cmd) { ++ pr_err("%s: Got command interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_mmc_dumpregs(host); ++ return; + } + -+ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ if (intmask & SDHCI_INT_TIMEOUT) ++ host->cmd->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | ++ SDHCI_INT_INDEX)) { ++ host->cmd->error = -EILSEQ; ++ } ++ ++ if (host->cmd->error) { ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_RESPONSE) ++ bcm2835_mmc_finish_command(host); + -+ return (service != NULL) ? 0 : -1; +} -+EXPORT_SYMBOL(vchi_service_create); + -+int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) ++static void bcm2835_mmc_data_irq(struct bcm2835_host *host, u32 intmask) +{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) { -+ VCHIQ_STATUS_T status = vchiq_close_service(service->handle); -+ if (status == VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ struct dma_chan *dma_chan; ++ u32 dir_data; ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->data) { ++ /* ++ * The "data complete" interrupt is also used to ++ * indicate that a busy state has ended. See comment ++ * above in sdhci_cmd_irq(). ++ */ ++ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { ++ if (intmask & SDHCI_INT_DATA_END) { ++ bcm2835_mmc_finish_command(host); ++ return; ++ } + } + -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_close); ++ pr_debug("%s: Got data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_mmc_dumpregs(host); + -+int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) { -+ VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); -+ if (status == VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_DATA_TIMEOUT) ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & SDHCI_INT_DATA_END_BIT) ++ host->data->error = -EILSEQ; ++ else if ((intmask & SDHCI_INT_DATA_CRC) && ++ SDHCI_GET_CMD(bcm2835_mmc_readw(host, SDHCI_COMMAND)) ++ != MMC_BUS_TEST_R) ++ host->data->error = -EILSEQ; ++ ++ if (host->use_dma) { ++ if (host->data->flags & MMC_DATA_WRITE) { ++ /* IRQ handled here */ ++ ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_mmc_finish_data(host); + } + -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_destroy); ++ } else { ++ if (host->data->error) ++ bcm2835_mmc_finish_data(host); ++ else { ++ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) ++ bcm2835_mmc_transfer_pio(host); + -+int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_SERVICE_OPTION_T option, -+ int value) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_SERVICE_OPTION_T vchiq_option; -+ switch (option) { -+ case VCHI_SERVICE_OPTION_TRACE: -+ vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; -+ break; -+ default: -+ service = NULL; -+ break; -+ } -+ if (service) { -+ VCHIQ_STATUS_T status = -+ vchiq_set_service_option(service->handle, -+ vchiq_option, -+ value); -+ -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_set_option); -+ -+int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if(service) -+ { -+ VCHIQ_STATUS_T status = vchiq_get_peer_version(service->handle, peer_version); -+ ret = vchiq_status_to_vchi( status ); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_get_peer_version); -+ -+/* ---------------------------------------------------------------------- -+ * read a uint32_t from buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+uint32_t -+vchi_readbuf_uint32(const void *_ptr) -+{ -+ const unsigned char *ptr = _ptr; -+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); -+} -+ -+/* ---------------------------------------------------------------------- -+ * write a uint32_t to buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+void -+vchi_writebuf_uint32(void *_ptr, uint32_t value) -+{ -+ unsigned char *ptr = _ptr; -+ ptr[0] = (unsigned char)((value >> 0) & 0xFF); -+ ptr[1] = (unsigned char)((value >> 8) & 0xFF); -+ ptr[2] = (unsigned char)((value >> 16) & 0xFF); -+ ptr[3] = (unsigned char)((value >> 24) & 0xFF); -+} -+ -+/* ---------------------------------------------------------------------- -+ * read a uint16_t from buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+uint16_t -+vchi_readbuf_uint16(const void *_ptr) -+{ -+ const unsigned char *ptr = _ptr; -+ return ptr[0] | (ptr[1] << 8); -+} -+ -+/* ---------------------------------------------------------------------- -+ * write a uint16_t into the buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+void -+vchi_writebuf_uint16(void *_ptr, uint16_t value) -+{ -+ unsigned char *ptr = _ptr; -+ ptr[0] = (value >> 0) & 0xFF; -+ ptr[1] = (value >> 8) & 0xFF; -+} -+ -+/*********************************************************** -+ * Name: vchi_service_use -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle -+ * -+ * Description: Routine to increment refcount on a service -+ * -+ * Returns: void -+ * -+ ***********************************************************/ -+int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) -+ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_use); -+ -+/*********************************************************** -+ * Name: vchi_service_release -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle -+ * -+ * Description: Routine to decrement refcount on a service -+ * -+ * Returns: void -+ * -+ ***********************************************************/ -+int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) -+ ret = vchiq_status_to_vchi( -+ vchiq_release_service(service->handle)); -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_release); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -new file mode 100644 -index 0000000..c2eefef ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -@@ -0,0 +1,151 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_util.h" -+ -+static inline int is_pow2(int i) -+{ -+ return i && !(i & (i - 1)); -+} -+ -+int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) -+{ -+ WARN_ON(!is_pow2(size)); -+ -+ queue->size = size; -+ queue->read = 0; -+ queue->write = 0; -+ -+ sema_init(&queue->pop, 0); -+ sema_init(&queue->push, 0); -+ -+ queue->storage = kzalloc(size * sizeof(VCHIQ_HEADER_T *), GFP_KERNEL); -+ if (queue->storage == NULL) { -+ vchiu_queue_delete(queue); -+ return 0; -+ } -+ return 1; -+} -+ -+void vchiu_queue_delete(VCHIU_QUEUE_T *queue) -+{ -+ if (queue->storage != NULL) -+ kfree(queue->storage); -+} -+ -+int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) -+{ -+ return queue->read == queue->write; -+} -+ -+int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) -+{ -+ return queue->write == queue->read + queue->size; -+} -+ -+void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) -+{ -+ while (queue->write == queue->read + queue->size) { -+ if (down_interruptible(&queue->pop) != 0) { -+ flush_signals(current); ++ if (intmask & SDHCI_INT_DATA_END) { ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ host->data_early = 1; ++ } else { ++ bcm2835_mmc_finish_data(host); ++ } ++ } + } + } -+ -+ /* -+ * Write to queue->storage must be visible after read from -+ * queue->read -+ */ -+ smp_mb(); -+ -+ queue->storage[queue->write & (queue->size - 1)] = header; -+ -+ /* -+ * Write to queue->storage must be visible before write to -+ * queue->write -+ */ -+ smp_wmb(); -+ -+ queue->write++; -+ -+ up(&queue->push); +} + -+VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) ++ ++static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) +{ -+ while (queue->write == queue->read) { -+ if (down_interruptible(&queue->push) != 0) { -+ flush_signals(current); -+ } -+ } -+ -+ up(&queue->push); // We haven't removed anything from the queue. -+ -+ /* -+ * Read from queue->storage must be visible after read from -+ * queue->write -+ */ -+ smp_rmb(); -+ -+ return queue->storage[queue->read & (queue->size - 1)]; -+} -+ -+VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) -+{ -+ VCHIQ_HEADER_T *header; -+ -+ while (queue->write == queue->read) { -+ if (down_interruptible(&queue->push) != 0) { -+ flush_signals(current); -+ } -+ } -+ -+ /* -+ * Read from queue->storage must be visible after read from -+ * queue->write -+ */ -+ smp_rmb(); -+ -+ header = queue->storage[queue->read & (queue->size - 1)]; -+ -+ /* -+ * Read from queue->storage must be visible before write to -+ * queue->read -+ */ -+ smp_mb(); -+ -+ queue->read++; -+ -+ up(&queue->pop); -+ -+ return header; -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h -new file mode 100644 -index 0000000..f4d0b66 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h -@@ -0,0 +1,81 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_UTIL_H -+#define VCHIQ_UTIL_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include /* for time_t */ -+#include -+#include -+ -+#include "vchiq_if.h" -+ -+typedef struct { -+ int size; -+ int read; -+ int write; -+ -+ struct semaphore pop; -+ struct semaphore push; -+ -+ VCHIQ_HEADER_T **storage; -+} VCHIU_QUEUE_T; -+ -+extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); -+extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); -+ -+extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); -+extern int vchiu_queue_is_full(VCHIU_QUEUE_T *queue); -+ -+extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); -+ -+extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); -+extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); -+ ++ irqreturn_t result = IRQ_NONE; ++ struct bcm2835_host *host = dev_id; ++ u32 intmask, mask, unexpected = 0; ++ int max_loops = 16; ++#ifndef CONFIG_ARCH_BCM2835 ++ int cardint = 0; +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -new file mode 100644 -index 0000000..b6bfa21 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -@@ -0,0 +1,59 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include "vchiq_build_info.h" -+#include + -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_hostname, "dc4-arm-01" ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)" ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_time, __TIME__ ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_date, __DATE__ ); ++ spin_lock(&host->lock); + -+const char *vchiq_get_build_hostname( void ) -+{ -+ return vchiq_build_hostname; -+} ++ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); + -+const char *vchiq_get_build_version( void ) -+{ -+ return vchiq_build_version; -+} ++ if (!intmask || intmask == 0xffffffff) { ++ result = IRQ_NONE; ++ goto out; ++ } + -+const char *vchiq_get_build_date( void ) -+{ -+ return vchiq_build_date; -+} -+ -+const char *vchiq_get_build_time( void ) -+{ -+ return vchiq_build_time; -+} - -From 2e067b9b144d119a1b0b930dae88d2f1b29dd497 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 12 May 2014 15:12:02 +0100 -Subject: [PATCH 012/172] vchiq: Avoid high load when blocked and unkillable - -vchiq: Include SIGSTOP and SIGCONT in list of signals not-masked by vchiq to allow gdb to work ---- - .../interface/vchiq_arm/vchiq_2835_arm.c | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 1 + - .../interface/vchiq_arm/vchiq_connected.c | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 1 + - .../interface/vchiq_arm/vchiq_kern_lib.c | 1 + - .../interface/vchiq_arm/vchiq_killable.h | 69 ++++++++++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_util.c | 1 + - 7 files changed, 75 insertions(+) - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -index b3bdaa2..7e7b09f 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -@@ -56,6 +56,7 @@ - #include "vchiq_arm.h" - #include "vchiq_2835.h" - #include "vchiq_connected.h" -+#include "vchiq_killable.h" - - #define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -index 2596818..3e328ab 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -50,6 +50,7 @@ - #include "vchiq_ioctl.h" - #include "vchiq_arm.h" - #include "vchiq_debugfs.h" -+#include "vchiq_killable.h" - - #define DEVICE_NAME "vchiq" - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -index 65f4b52..5efc62f 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -@@ -33,6 +33,7 @@ - - #include "vchiq_connected.h" - #include "vchiq_core.h" -+#include "vchiq_killable.h" - #include - #include - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -index f962027..14a4155 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -32,6 +32,7 @@ - */ - - #include "vchiq_core.h" -+#include "vchiq_killable.h" - - #define VCHIQ_SLOT_HANDLER_STACK 8192 - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -index be9735f..5a4182e 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -39,6 +39,7 @@ - - #include "vchiq_core.h" - #include "vchiq_arm.h" -+#include "vchiq_killable.h" - - /* ---- Public Variables ------------------------------------------------- */ - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h -new file mode 100644 -index 0000000..335446e ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h -@@ -0,0 +1,69 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_KILLABLE_H -+#define VCHIQ_KILLABLE_H -+ -+#include -+#include -+ -+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT)) -+ -+static inline int __must_check down_interruptible_killable(struct semaphore *sem) -+{ -+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ -+ int ret; -+ sigset_t blocked, oldset; -+ siginitsetinv(&blocked, SHUTDOWN_SIGS); -+ sigprocmask(SIG_SETMASK, &blocked, &oldset); -+ ret = down_interruptible(sem); -+ sigprocmask(SIG_SETMASK, &oldset, NULL); -+ return ret; -+} -+#define down_interruptible down_interruptible_killable ++ do { ++ /* Clear selected interrupts. */ ++ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_BUS_POWER); ++ bcm2835_mmc_writel(host, mask, SDHCI_INT_STATUS); + + -+static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock) -+{ -+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ -+ int ret; -+ sigset_t blocked, oldset; -+ siginitsetinv(&blocked, SHUTDOWN_SIGS); -+ sigprocmask(SIG_SETMASK, &blocked, &oldset); -+ ret = mutex_lock_interruptible(lock); -+ sigprocmask(SIG_SETMASK, &oldset, NULL); -+ return ret; -+} -+#define mutex_lock_interruptible mutex_lock_interruptible_killable ++ if (intmask & SDHCI_INT_CMD_MASK) ++ bcm2835_mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + ++ if (intmask & SDHCI_INT_DATA_MASK) ++ bcm2835_mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK); ++ ++ if (intmask & SDHCI_INT_BUS_POWER) ++ pr_err("%s: Card is consuming too much power!\n", ++ mmc_hostname(host->mmc)); ++ ++ if (intmask & SDHCI_INT_CARD_INT) { ++#ifndef CONFIG_ARCH_BCM2835 ++ cardint = 1; ++#else ++ bcm2835_mmc_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHCI_INT_CARD_INT; ++ result = IRQ_WAKE_THREAD; +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -index c2eefef..05e7979 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -@@ -32,6 +32,7 @@ - */ - - #include "vchiq_util.h" -+#include "vchiq_killable.h" - - static inline int is_pow2(int i) - { ++ } ++ ++ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | ++ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | ++ SDHCI_INT_CARD_INT); ++ ++ if (intmask) { ++ unexpected |= intmask; ++ bcm2835_mmc_writel(host, intmask, SDHCI_INT_STATUS); ++ } ++ ++ if (result == IRQ_NONE) ++ result = IRQ_HANDLED; ++ ++ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); ++ } while (intmask && --max_loops); ++out: ++ spin_unlock(&host->lock); ++ ++ if (unexpected) { ++ pr_err("%s: Unexpected interrupt 0x%08x.\n", ++ mmc_hostname(host->mmc), unexpected); ++ bcm2835_mmc_dumpregs(host); ++ } ++ ++#ifndef CONFIG_ARCH_BCM2835 ++ if (cardint) ++ mmc_signal_sdio_irq(host->mmc); ++#endif ++ ++ return result; ++} ++ ++#ifdef CONFIG_ARCH_BCM2835 ++static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) ++{ ++ struct bcm2835_host *host = dev_id; ++ unsigned long flags; ++ u32 isr; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ isr = host->thread_isr; ++ host->thread_isr = 0; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (isr & SDHCI_INT_CARD_INT) { ++ sdio_run_irqs(host->mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) ++ bcm2835_mmc_enable_sdio_irq_nolock(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ return isr ? IRQ_HANDLED : IRQ_NONE; ++} ++#endif ++ ++ ++ ++void bcm2835_mmc_set_clock(struct bcm2835_host *host, unsigned int clock) ++{ ++ int div = 0; /* Initialized for compiler warning */ ++ int real_div = div, clk_mul = 1; ++ u16 clk = 0; ++ unsigned long timeout; ++ ++ ++ host->mmc->actual_clock = 0; ++ ++ bcm2835_mmc_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ return; ++ ++ /* Version 3.00 divisors must be a multiple of 2. */ ++ if (host->max_clk <= clock) ++ div = 1; ++ else { ++ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; ++ div += 2) { ++ if ((host->max_clk / div) <= clock) ++ break; ++ } ++ } ++ ++ real_div = div; ++ div >>= 1; ++ ++ if (real_div) ++ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; ++ ++ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; ++ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) ++ << SDHCI_DIVIDER_HI_SHIFT; ++ clk |= SDHCI_CLOCK_INT_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Wait max 20 ms */ ++ timeout = 20; ++ while (!((clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ pr_err("%s: Internal clock never " ++ "stabilised.\n", mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (20-timeout > 10 && 20-timeout > host->max_delay) { ++ host->max_delay = 20-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ clk |= SDHCI_CLOCK_CARD_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void bcm2835_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = mmc_priv(mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) ++ bcm2835_mmc_send_command(host, mrq->sbc); ++ else ++ bcm2835_mmc_send_command(host, mrq->cmd); ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (!(mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) && mrq->cmd->data && host->use_dma) { ++ /* DMA transfer starts now, PIO starts after interrupt */ ++ bcm2835_mmc_transfer_dma(host); ++ } ++} ++ ++ ++static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ u8 ctrl; ++ u16 clk, ctrl_2; ++ ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_mmc_set_clock(host, ios->clock); ++ host->clock = ios->clock; ++ } ++ ++ if (host->pwr != SDHCI_POWER_330) { ++ host->pwr = SDHCI_POWER_330; ++ bcm2835_mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL); ++ } ++ ++ ctrl = bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL); ++ ++ /* set bus width */ ++ ctrl &= ~SDHCI_CTRL_8BITBUS; ++ if (ios->bus_width == MMC_BUS_WIDTH_4) ++ ctrl |= SDHCI_CTRL_4BITBUS; ++ else ++ ctrl &= ~SDHCI_CTRL_4BITBUS; ++ ++ ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */ ++ ++ ++ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ /* ++ * We only need to set Driver Strength if the ++ * preset value enable is not set. ++ */ ++ ctrl_2 = bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; ++ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; ++ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; ++ ++ bcm2835_mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); ++ ++ /* Reset SD Clock Enable */ ++ clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL); ++ clk &= ~SDHCI_CLOCK_CARD_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Re-enable SD Clock */ ++ bcm2835_mmc_set_clock(host, host->clock); ++ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++static struct mmc_host_ops bcm2835_ops = { ++ .request = bcm2835_mmc_request, ++ .set_ios = bcm2835_mmc_set_ios, ++ .enable_sdio_irq = bcm2835_mmc_enable_sdio_irq, ++}; ++ ++ ++static void bcm2835_mmc_tasklet_finish(unsigned long param) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ struct mmc_request *mrq; ++ ++ host = (struct bcm2835_host *)param; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ /* ++ * If this tasklet gets rescheduled while running, it will ++ * be run again afterwards but without any active request. ++ */ ++ if (!host->mrq) { ++ spin_unlock_irqrestore(&host->lock, flags); ++ return; ++ } ++ ++ del_timer(&host->timer); ++ ++ mrq = host->mrq; ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (!(host->flags & SDHCI_DEVICE_DEAD) && ++ ((mrq->cmd && mrq->cmd->error) || ++ (mrq->data && (mrq->data->error || ++ (mrq->data->stop && mrq->data->stop->error))))) { ++ ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); ++ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++ ++int bcm2835_mmc_add_host(struct bcm2835_host *host) ++{ ++ struct mmc_host *mmc = host->mmc; ++ struct device *dev = mmc->parent; ++#ifndef FORCE_PIO ++ struct dma_slave_config cfg; ++#endif ++ int ret; ++ ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ host->clk_mul = 0; ++ ++ mmc->f_max = host->max_clk; ++ mmc->f_max = host->max_clk; ++ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; ++ ++ /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ ++ host->timeout_clk = mmc->f_max / 1000; ++#ifdef CONFIG_ARCH_BCM2835 ++ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; ++#endif ++ /* host controller capabilities */ ++ mmc->caps = MMC_CAP_CMD23 | MMC_CAP_ERASE | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | ++ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_4_BIT_DATA; ++ ++ host->flags = SDHCI_AUTO_CMD23; ++ ++ spin_lock_init(&host->lock); ++ ++#ifdef FORCE_PIO ++ dev_info(dev, "Forcing PIO mode\n"); ++ host->have_dma = false; ++#else ++ if (IS_ERR_OR_NULL(host->dma_chan_tx) || ++ IS_ERR_OR_NULL(host->dma_chan_rx)) { ++ dev_err(dev, "%s: Unable to initialise DMA channels. Falling back to PIO\n", ++ DRIVER_NAME); ++ host->have_dma = false; ++ } else { ++ dev_info(dev, "DMA channels allocated"); ++ host->have_dma = true; ++ ++ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.slave_id = 11; /* DREQ channel */ ++ ++ cfg.direction = DMA_MEM_TO_DEV; ++ cfg.src_addr = 0; ++ cfg.dst_addr = host->phys_addr + SDHCI_BUFFER; ++ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->phys_addr + SDHCI_BUFFER; ++ cfg.dst_addr = 0; ++ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ } ++#endif ++ mmc->max_segs = 128; ++ mmc->max_req_size = 524288; ++ mmc->max_seg_size = mmc->max_req_size; ++ mmc->max_blk_size = 512; ++ mmc->max_blk_count = 65535; ++ ++ /* report supported voltage ranges */ ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ tasklet_init(&host->finish_tasklet, ++ bcm2835_mmc_tasklet_finish, (unsigned long)host); ++ ++ setup_timer(&host->timer, bcm2835_mmc_timeout_timer, (unsigned long)host); ++ init_waitqueue_head(&host->buf_ready_int); ++ ++ bcm2835_mmc_init(host, 0); ++#ifndef CONFIG_ARCH_BCM2835 ++ ret = devm_request_irq(dev, host->irq, bcm2835_mmc_irq, 0, ++ mmc_hostname(mmc), host); ++#else ++ ret = devm_request_threaded_irq(dev, host->irq, bcm2835_mmc_irq, ++ bcm2835_mmc_thread_irq, IRQF_SHARED, ++ mmc_hostname(mmc), host); ++#endif ++ if (ret) { ++ dev_err(dev, "Failed to request IRQ %d: %d\n", host->irq, ret); ++ goto untasklet; ++ } ++ ++ mmiowb(); ++ mmc_add_host(mmc); ++ ++ return 0; ++ ++untasklet: ++ tasklet_kill(&host->finish_tasklet); ++ ++ return ret; ++} ++ ++static int bcm2835_mmc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct clk *clk; ++ struct resource *iomem; ++ struct bcm2835_host *host; ++ struct mmc_host *mmc; ++ int ret; ++ ++ mmc = mmc_alloc_host(sizeof(*host), dev); ++ if (!mmc) ++ return -ENOMEM; ++ ++ mmc->ops = &bcm2835_ops; ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->timeout = msecs_to_jiffies(1000); ++ spin_lock_init(&host->lock); ++ ++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->ioaddr = devm_ioremap_resource(dev, iomem); ++ if (IS_ERR(host->ioaddr)) { ++ ret = PTR_ERR(host->ioaddr); ++ goto err; ++ } ++ ++ host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; ++ ++#ifndef FORCE_PIO ++ if (node) { ++ host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); ++ host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); ++ } else { ++ dma_cap_mask_t mask; ++ ++ dma_cap_zero(mask); ++ /* we don't care about the channel, any would work */ ++ dma_cap_set(DMA_SLAVE, mask); ++ host->dma_chan_tx = dma_request_channel(mask, NULL, NULL); ++ host->dma_chan_rx = dma_request_channel(mask, NULL, NULL); ++ } ++#endif ++ clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "could not get clk\n"); ++ ret = PTR_ERR(clk); ++ goto err; ++ } ++ ++ host->max_clk = clk_get_rate(clk); ++ ++ host->irq = platform_get_irq(pdev, 0); ++ if (host->irq <= 0) { ++ dev_err(dev, "get IRQ failed\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (node) ++ mmc_of_parse(mmc); ++ else ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ ++ ret = bcm2835_mmc_add_host(host); ++ if (ret) ++ goto err; ++ ++ platform_set_drvdata(pdev, host); ++ ++ return 0; ++err: ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++static int bcm2835_mmc_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_host *host = platform_get_drvdata(pdev); ++ unsigned long flags; ++ int dead; ++ u32 scratch; ++ ++ dead = 0; ++ scratch = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); ++ if (scratch == (u32)-1) ++ dead = 1; ++ ++ ++ if (dead) { ++ spin_lock_irqsave(&host->lock, flags); ++ ++ host->flags |= SDHCI_DEVICE_DEAD; ++ ++ if (host->mrq) { ++ pr_err("%s: Controller removed during " ++ " transfer!\n", mmc_hostname(host->mmc)); ++ ++ host->mrq->cmd->error = -ENOMEDIUM; ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ mmc_remove_host(host->mmc); ++ ++ if (!dead) ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ free_irq(host->irq, host); ++ ++ del_timer_sync(&host->timer); ++ ++ tasklet_kill(&host->finish_tasklet); ++ ++ mmc_free_host(host->mmc); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++ ++static const struct of_device_id bcm2835_mmc_match[] = { ++ { .compatible = "brcm,bcm2835-mmc" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_mmc_match); ++ ++ ++ ++static struct platform_driver bcm2835_mmc_driver = { ++ .probe = bcm2835_mmc_probe, ++ .remove = bcm2835_mmc_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_mmc_match, ++ }, ++}; ++module_platform_driver(bcm2835_mmc_driver); ++ ++MODULE_ALIAS("platform:mmc-bcm2835"); ++MODULE_DESCRIPTION("BCM2835 SDHCI driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Gellert Weisz"); -From 38a6237db4d225352e14513dde856a7767e51c76 Mon Sep 17 00:00:00 2001 +From 3c364191bcd2eb70a61271316a7348e7f471caea Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:31:47 +0100 -Subject: [PATCH 013/172] cma: Add vc_cma driver to enable use of CMA +Subject: [PATCH 009/104] cma: Add vc_cma driver to enable use of CMA Signed-off-by: popcornmix + +vc_cma: Make the vc_cma area the default contiguous DMA area --- drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/broadcom/Kconfig | 15 + drivers/char/broadcom/Makefile | 1 + drivers/char/broadcom/vc_cma/Makefile | 14 + - drivers/char/broadcom/vc_cma/vc_cma.c | 1143 +++++++++++++++++++++++++++++++++ - drivers/misc/Makefile | 2 +- + drivers/char/broadcom/vc_cma/vc_cma.c | 1193 +++++++++++++++++++++++++++++++++ include/linux/broadcom/vc_cma.h | 29 + - 8 files changed, 1206 insertions(+), 1 deletion(-) + 7 files changed, 1255 insertions(+) create mode 100644 drivers/char/broadcom/Kconfig create mode 100644 drivers/char/broadcom/Makefile create mode 100644 drivers/char/broadcom/vc_cma/Makefile @@ -80312,10 +80603,10 @@ Signed-off-by: popcornmix create mode 100644 include/linux/broadcom/vc_cma.h diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig -index efefd12..aebd7ca 100644 +index a4af8221..59e4d63 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig -@@ -581,6 +581,8 @@ config DEVPORT +@@ -590,6 +590,8 @@ config DEVPORT source "drivers/s390/char/Kconfig" @@ -80383,10 +80674,10 @@ index 0000000..6a94de4 +vc-cma-objs := vc_cma.o diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c new file mode 100644 -index 0000000..a635f9f +index 0000000..07b31a7 --- /dev/null +++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -0,0 +1,1143 @@ +@@ -0,0 +1,1193 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * @@ -80447,6 +80738,8 @@ index 0000000..a635f9f +#define LOG_DBG(fmt, ...) \ + if (vc_cma_debug) \ + printk(KERN_INFO fmt "\n", ##__VA_ARGS__) ++#define LOG_INFO(fmt, ...) \ ++ printk(KERN_INFO fmt "\n", ##__VA_ARGS__) +#define LOG_ERR(fmt, ...) \ + printk(KERN_ERR fmt "\n", ##__VA_ARGS__) + @@ -80509,6 +80802,10 @@ index 0000000..a635f9f +unsigned int vc_cma_chunks_used; +unsigned int vc_cma_chunks_reserved; + ++ ++void *vc_cma_dma_alloc; ++unsigned int vc_cma_dma_size; ++ +static int in_loud_error; + +unsigned int vc_cma_reserve_total; @@ -80587,8 +80884,17 @@ index 0000000..a635f9f + * size from the end of memory + */ + if (vc_cma_size) { -+ if (dma_declare_contiguous(NULL /*&vc_cma_device.dev*/, vc_cma_size, ++ if (dma_declare_contiguous(&vc_cma_device.dev, vc_cma_size, + vc_cma_base, 0) == 0) { ++ if (!dev_get_cma_area(NULL)) { ++ /* There is no default CMA area - make this ++ the default */ ++ struct cma *vc_cma_area = dev_get_cma_area( ++ &vc_cma_device.dev); ++ dma_contiguous_set_default(vc_cma_area); ++ LOG_INFO("vc_cma_reserve - using vc_cma as " ++ "the default contiguous DMA area"); ++ } + } else { + LOG_ERR("vc_cma: dma_declare_contiguous(%x,%x) failed", + vc_cma_size, (unsigned int)vc_cma_base); @@ -80698,6 +81004,9 @@ index 0000000..a635f9f + seq_printf(m, " PID %5d: %d bytes\n", user->pid, + user->reserve); + } ++ seq_printf(m, " dma_alloc : %p (%d pages)\n", ++ vc_cma_dma_alloc ? page_address(vc_cma_dma_alloc) : 0, ++ vc_cma_dma_size); + + seq_printf(m, "\n"); + @@ -80737,31 +81046,33 @@ index 0000000..a635f9f +#define FREE_STR "free" +#define DEBUG_STR "debug" +#define RESERVE_STR "reserve" ++#define DMA_ALLOC_STR "dma_alloc" ++#define DMA_FREE_STR "dma_free" + if (strncmp(input_str, ALLOC_STR, strlen(ALLOC_STR)) == 0) { -+ int size; ++ int alloc_size; + char *p = input_str + strlen(ALLOC_STR); + + while (*p == ' ') + p++; -+ size = memparse(p, NULL); -+ LOG_ERR("/proc/vc-cma: alloc %d", size); -+ if (size) ++ alloc_size = memparse(p, NULL); ++ LOG_INFO("/proc/vc-cma: alloc %d", alloc_size); ++ if (alloc_size) + send_vc_msg(VC_CMA_MSG_REQUEST_FREE, -+ size / VC_CMA_CHUNK_SIZE, 0); ++ alloc_size / VC_CMA_CHUNK_SIZE, 0); + else + LOG_ERR("invalid size '%s'", p); + rc = size; + } else if (strncmp(input_str, FREE_STR, strlen(FREE_STR)) == 0) { -+ int size; ++ int alloc_size; + char *p = input_str + strlen(FREE_STR); + + while (*p == ' ') + p++; -+ size = memparse(p, NULL); -+ LOG_ERR("/proc/vc-cma: free %d", size); -+ if (size) ++ alloc_size = memparse(p, NULL); ++ LOG_INFO("/proc/vc-cma: free %d", alloc_size); ++ if (alloc_size) + send_vc_msg(VC_CMA_MSG_REQUEST_ALLOC, -+ size / VC_CMA_CHUNK_SIZE, 0); ++ alloc_size / VC_CMA_CHUNK_SIZE, 0); + else + LOG_ERR("invalid size '%s'", p); + rc = size; @@ -80773,18 +81084,46 @@ index 0000000..a635f9f + vc_cma_debug = 1; + else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) + vc_cma_debug = 0; -+ LOG_ERR("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); ++ LOG_INFO("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); + rc = size; + } else if (strncmp(input_str, RESERVE_STR, strlen(RESERVE_STR)) == 0) { -+ int size; ++ int alloc_size; + int reserved; + char *p = input_str + strlen(RESERVE_STR); + while (*p == ' ') + p++; -+ size = memparse(p, NULL); ++ alloc_size = memparse(p, NULL); + -+ reserved = vc_cma_set_reserve(size, current->tgid); ++ reserved = vc_cma_set_reserve(alloc_size, current->tgid); + rc = (reserved >= 0) ? size : reserved; ++ } else if (strncmp(input_str, DMA_ALLOC_STR, strlen(DMA_ALLOC_STR)) == 0) { ++ int alloc_size; ++ char *p = input_str + strlen(DMA_ALLOC_STR); ++ while (*p == ' ') ++ p++; ++ alloc_size = memparse(p, NULL); ++ ++ if (vc_cma_dma_alloc) { ++ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, ++ vc_cma_dma_size); ++ vc_cma_dma_alloc = NULL; ++ vc_cma_dma_size = 0; ++ } ++ vc_cma_dma_alloc = dma_alloc_from_contiguous(NULL, alloc_size, 0); ++ vc_cma_dma_size = (vc_cma_dma_alloc ? alloc_size : 0); ++ if (vc_cma_dma_alloc) ++ LOG_INFO("dma_alloc(%d pages) -> %p", alloc_size, page_address(vc_cma_dma_alloc)); ++ else ++ LOG_ERR("dma_alloc(%d pages) failed", alloc_size); ++ rc = size; ++ } else if (strncmp(input_str, DMA_FREE_STR, strlen(DMA_FREE_STR)) == 0) { ++ if (vc_cma_dma_alloc) { ++ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, ++ vc_cma_dma_size); ++ vc_cma_dma_alloc = NULL; ++ vc_cma_dma_size = 0; ++ } ++ rc = size; + } + +out: @@ -80915,7 +81254,7 @@ index 0000000..a635f9f + uint8_t *chunk_addr; + size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; + -+ chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, + PAGES_PER_CHUNK, + VC_CMA_CHUNK_ORDER); + if (!chunk) @@ -80931,13 +81270,14 @@ index 0000000..a635f9f + BUG_ON(((page_to_phys(chunk) - vc_cma_base) % + VC_CMA_CHUNK_SIZE) != 0); + if (chunk_num >= vc_cma_chunks) { ++ phys_addr_t _pa = vc_cma_base + vc_cma_size - 1; + LOG_ERR("%s: ===============================", + __func__); -+ LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " ++ LOG_ERR("%s: chunk phys %x, vc_cma %pa-%pa - " + "bad SPARSEMEM configuration?", + __func__, (unsigned int)page_to_phys(chunk), -+ vc_cma_base, vc_cma_base + vc_cma_size - 1); -+ LOG_ERR("%s: dev->cma_area = %p\n", __func__, ++ &vc_cma_base, &_pa); ++ LOG_ERR("%s: dev->cma_area = %p", __func__, + (void*)0/*vc_cma_device.dev.cma_area*/); + LOG_ERR("%s: ===============================", + __func__); @@ -81061,13 +81401,14 @@ index 0000000..a635f9f + } + + if (!dma_release_from_contiguous -+ (NULL /*&vc_cma_device.dev*/, page, ++ (&vc_cma_device.dev, page, + PAGES_PER_CHUNK)) { ++ phys_addr_t _pa = page_to_phys(page); + LOG_ERR + ("CMA_MSG_FREE - failed to " -+ "release chunk %d (phys %x, " ++ "release chunk %d (phys %pa, " + "page %x)", chunk_num, -+ page_to_phys(page), ++ &_pa, + (unsigned int)page); + } + vc_cma_chunks_used--; @@ -81412,12 +81753,12 @@ index 0000000..a635f9f + if (!check_cma_config()) + goto out_release; + -+ printk(KERN_INFO "vc-cma: Videocore CMA driver\n"); -+ printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); -+ printk(KERN_INFO "vc-cma: vc_cma_size = 0x%08x (%u MiB)\n", -+ vc_cma_size, vc_cma_size / (1024 * 1024)); -+ printk(KERN_INFO "vc-cma: vc_cma_initial = 0x%08x (%u MiB)\n", -+ vc_cma_initial, vc_cma_initial / (1024 * 1024)); ++ LOG_INFO("vc-cma: Videocore CMA driver"); ++ LOG_INFO("vc-cma: vc_cma_base = %pa", &vc_cma_base); ++ LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", ++ vc_cma_size, vc_cma_size / (1024 * 1024)); ++ LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", ++ vc_cma_initial, vc_cma_initial / (1024 * 1024)); + + vc_cma_base_page = phys_to_page(vc_cma_base); + @@ -81427,7 +81768,7 @@ index 0000000..a635f9f + for (vc_cma_chunks_used = 0; + vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { + struct page *chunk; -+ chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, + PAGES_PER_CHUNK, + VC_CMA_CHUNK_ORDER); + if (!chunk) @@ -81530,19 +81871,6 @@ index 0000000..a635f9f +module_exit(vc_cma_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom Corporation"); -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index c085ede..43d2ac9 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -51,7 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ - obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ - obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o - obj-$(CONFIG_SRAM) += sram.o --obj-y += vc04_services/ -+obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/ - obj-y += mic/ - obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ diff --git a/include/linux/broadcom/vc_cma.h b/include/linux/broadcom/vc_cma.h new file mode 100644 index 0000000..5325832 @@ -81579,10 +81907,10 @@ index 0000000..5325832 + +#endif /* VC_CMA_H */ -From 3d1f88f9fc8393b6c8e34c991dac0dbc58ae3f4d Mon Sep 17 00:00:00 2001 +From 228e8e7cf4c46585562ff24e2244540e5e6d649a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 26 Mar 2012 22:15:50 +0100 -Subject: [PATCH 014/172] bcm2708: alsa sound driver +Subject: [PATCH 010/104] bcm2708: alsa sound driver Signed-off-by: popcornmix @@ -81605,19 +81933,16 @@ alsa: reduce severity of expected warning message snd-bcm2708: Fix dmesg spam for non-error case --- - arch/arm/configs/bcmrpi_defconfig | 20 + - arch/arm/mach-bcm2708/bcm2708.c | 54 ++ - .../interface/vchiq_arm/vchiq_kern_lib.c | 7 +- - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 12 +- - sound/arm/Kconfig | 7 + - sound/arm/Makefile | 5 + - sound/arm/bcm2835-ctl.c | 323 ++++++++ - sound/arm/bcm2835-pcm.c | 552 +++++++++++++ - sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++ - sound/arm/bcm2835.c | 420 ++++++++++ - sound/arm/bcm2835.h | 167 ++++ - sound/arm/vc_vchi_audioserv_defs.h | 116 +++ - 12 files changed, 2578 insertions(+), 7 deletions(-) + arch/arm/mach-bcm2708/bcm2708.c | 54 +++ + sound/arm/Kconfig | 7 + + sound/arm/Makefile | 5 + + sound/arm/bcm2835-ctl.c | 323 +++++++++++++ + sound/arm/bcm2835-pcm.c | 552 +++++++++++++++++++++++ + sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++++++++++++++++++ + sound/arm/bcm2835.c | 420 +++++++++++++++++ + sound/arm/bcm2835.h | 167 +++++++ + sound/arm/vc_vchi_audioserv_defs.h | 116 +++++ + 9 files changed, 2546 insertions(+) create mode 100755 sound/arm/bcm2835-ctl.c create mode 100755 sound/arm/bcm2835-pcm.c create mode 100755 sound/arm/bcm2835-vchiq.c @@ -81625,42 +81950,11 @@ snd-bcm2708: Fix dmesg spam for non-error case create mode 100755 sound/arm/bcm2835.h create mode 100644 sound/arm/vc_vchi_audioserv_defs.h -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 8950e53..63cb6a4 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -202,6 +202,26 @@ CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set - # CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=m -+CONFIG_SND_SEQUENCER=m -+CONFIG_SND_SEQ_DUMMY=m -+CONFIG_SND_MIXER_OSS=m -+CONFIG_SND_PCM_OSS=m -+CONFIG_SND_SEQUENCER_OSS=y -+CONFIG_SND_HRTIMER=m -+CONFIG_SND_DUMMY=m -+CONFIG_SND_ALOOP=m -+CONFIG_SND_VIRMIDI=m -+CONFIG_SND_MTPAV=m -+CONFIG_SND_SERIAL_U16550=m -+CONFIG_SND_MPU401=m -+CONFIG_SND_BCM2835=m -+CONFIG_SND_USB_AUDIO=m -+CONFIG_SND_USB_UA101=m -+CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_6FIRE=m -+CONFIG_SOUND_PRIME=m - CONFIG_HID_A4TECH=m - CONFIG_HID_ACRUX=m - CONFIG_HID_APPLE=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 7503649..f3dccae 100644 +index d1308ed..f54e3e9b 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -403,6 +403,58 @@ struct platform_device bcm2708_powerman_device = { +@@ -447,6 +447,58 @@ struct platform_device bcm2708_powerman_device = { .coherent_dma_mask = 0xffffffffUL}, }; @@ -81719,87 +82013,17 @@ index 7503649..f3dccae 100644 int __init bcm_register_device(struct platform_device *pdev) { int ret; -@@ -508,6 +560,8 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_powerman_device); - +@@ -556,6 +608,8 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2835_emmc_device); + #endif bcm2708_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -index 5a4182e..25e7011 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -288,11 +288,12 @@ VCHIQ_STATUS_T vchiq_open_service( - NULL); - - if (service) { -+ *phandle = service->handle; - status = vchiq_open_service_internal(service, current->pid); -- if (status == VCHIQ_SUCCESS) -- *phandle = service->handle; -- else -+ if (status != VCHIQ_SUCCESS) { - vchiq_remove_service(service->handle); -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ } - } - - failed: -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -index 72eacdaf..c26eff7 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -634,6 +634,9 @@ int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, - { - VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; - SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ - if (service) { - VCHIQ_SERVICE_PARAMS_T params; - VCHIQ_STATUS_T status; -@@ -650,11 +653,10 @@ int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, - if (status != VCHIQ_SUCCESS) { - service_free(service); - service = NULL; -+ *handle = NULL; - } - } - -- *handle = (VCHI_SERVICE_HANDLE_T)service; -- - return (service != NULL) ? 0 : -1; - } - EXPORT_SYMBOL(vchi_service_open); -@@ -665,6 +667,9 @@ int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, - { - VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; - SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ - if (service) { - VCHIQ_SERVICE_PARAMS_T params; - VCHIQ_STATUS_T status; -@@ -680,11 +685,10 @@ int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, - if (status != VCHIQ_SUCCESS) { - service_free(service); - service = NULL; -+ *handle = NULL; - } - } - -- *handle = (VCHI_SERVICE_HANDLE_T)service; -- - return (service != NULL) ? 0 : -1; - } - EXPORT_SYMBOL(vchi_service_create); diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig -index 885683a..f7ceafd 100644 +index 885683a..ada7ba2 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -39,5 +39,12 @@ config SND_PXA2XX_AC97 @@ -81808,7 +82032,7 @@ index 885683a..f7ceafd 100644 +config SND_BCM2835 + tristate "BCM2835 ALSA driver" -+ depends on ARCH_BCM2708 && BCM2708_VCHIQ && SND ++ depends on (ARCH_BCM2708 || ARCH_BCM2709) && BCM2708_VCHIQ && SND + select SND_PCM + help + Say Y or M if you want to support BCM2835 Alsa pcm card driver @@ -84345,57 +84569,1763 @@ index 0000000..af3e6eb + +#endif // _VC_AUDIO_DEFS_H_ -From 440a7ce5f52fd43466a0d25132a3bc855c224be9 Mon Sep 17 00:00:00 2001 +From d613f62318f4535aa6449f69dbac27b44feefd93 Mon Sep 17 00:00:00 2001 From: popcornmix -Date: Wed, 3 Jul 2013 00:51:55 +0100 -Subject: [PATCH 015/172] Add hwrng (hardware random number generator) driver +Date: Tue, 2 Jul 2013 23:42:01 +0100 +Subject: [PATCH 011/104] bcm2708 vchiq driver +Signed-off-by: popcornmix + +vchiq: create_pagelist copes with vmalloc memory + +Signed-off-by: Daniel Stone + +vchiq: fix the shim message release + +Signed-off-by: Daniel Stone + +vchiq: export additional symbols + +Signed-off-by: Daniel Stone + +VCHIQ: Make service closure fully synchronous (drv) + +This is one half of a two-part patch, the other half of which is to +the vchiq_lib user library. With these patches, calls to +vchiq_close_service and vchiq_remove_service won't return until any +associated callbacks have been delivered to the callback thread. + +VCHIQ: Add per-service tracing + +The new service option VCHIQ_SERVICE_OPTION_TRACE is a boolean that +toggles tracing for the specified service. + +This commit also introduces vchi_service_set_option and the associated +option VCHI_SERVICE_OPTION_TRACE. + +vchiq: Make the synchronous-CLOSE logic more tolerant + +vchiq: Move logging control into debugfs + +vchiq: Take care of a corner case tickled by VCSM + +Closing a connection that isn't fully open requires care, since one +side does not know the other side's port number. Code was present to +handle the case where a CLOSE is sent immediately after an OPEN, i.e. +before the OPENACK has been received, but this was incorrectly being +used when an OPEN from a client using port 0 was rejected. + +(In the observed failure, the host was attempting to use the VCSM +service, which isn't present in the 'cutdown' firmware. The failure +was intermittent because sometimes the keepalive service would +grab port 0.) + +This case can be distinguished because the client's remoteport will +still be VCHIQ_PORT_FREE, and the srvstate will be OPENING. Either +condition is sufficient to differentiate it from the special case +described above. + +vchiq: Avoid high load when blocked and unkillable + +vchiq: Include SIGSTOP and SIGCONT in list of signals not-masked by vchiq to allow gdb to work + +vchiq_arm: Complete support for SYNCHRONOUS mode + +vchiq: Remove inline from suspend/resume --- - drivers/char/hw_random/Kconfig | 11 ++++ - drivers/char/hw_random/Makefile | 1 + - drivers/char/hw_random/bcm2708-rng.c | 118 +++++++++++++++++++++++++++++++++++ - 3 files changed, 130 insertions(+) - create mode 100755 drivers/char/hw_random/bcm2708-rng.c + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/vc04_services/Kconfig | 9 + + drivers/misc/vc04_services/Makefile | 14 + + .../interface/vchi/connections/connection.h | 328 ++ + .../interface/vchi/message_drivers/message.h | 204 + + drivers/misc/vc04_services/interface/vchi/vchi.h | 378 ++ + .../misc/vc04_services/interface/vchi/vchi_cfg.h | 224 ++ + .../interface/vchi/vchi_cfg_internal.h | 71 + + .../vc04_services/interface/vchi/vchi_common.h | 175 + + .../misc/vc04_services/interface/vchi/vchi_mh.h | 42 + + .../misc/vc04_services/interface/vchiq_arm/vchiq.h | 40 + + .../vc04_services/interface/vchiq_arm/vchiq_2835.h | 42 + + .../interface/vchiq_arm/vchiq_2835_arm.c | 562 +++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2884 ++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 223 ++ + .../interface/vchiq_arm/vchiq_build_info.h | 37 + + .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 69 + + .../interface/vchiq_arm/vchiq_connected.c | 120 + + .../interface/vchiq_arm/vchiq_connected.h | 50 + + .../vc04_services/interface/vchiq_arm/vchiq_core.c | 3934 ++++++++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_core.h | 712 ++++ + .../interface/vchiq_arm/vchiq_debugfs.c | 383 ++ + .../interface/vchiq_arm/vchiq_debugfs.h | 52 + + .../interface/vchiq_arm/vchiq_genversion | 87 + + .../vc04_services/interface/vchiq_arm/vchiq_if.h | 189 + + .../interface/vchiq_arm/vchiq_ioctl.h | 131 + + .../interface/vchiq_arm/vchiq_kern_lib.c | 458 +++ + .../interface/vchiq_arm/vchiq_killable.h | 69 + + .../interface/vchiq_arm/vchiq_memdrv.h | 71 + + .../interface/vchiq_arm/vchiq_pagelist.h | 58 + + .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 860 +++++ + .../vc04_services/interface/vchiq_arm/vchiq_util.c | 152 + + .../vc04_services/interface/vchiq_arm/vchiq_util.h | 81 + + .../interface/vchiq_arm/vchiq_version.c | 59 + + 35 files changed, 12770 insertions(+) + create mode 100644 drivers/misc/vc04_services/Kconfig + create mode 100644 drivers/misc/vc04_services/Makefile + create mode 100644 drivers/misc/vc04_services/interface/vchi/connections/connection.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/message_drivers/message.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_common.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig -index de57b38..2a24bec 100644 ---- a/drivers/char/hw_random/Kconfig -+++ b/drivers/char/hw_random/Kconfig -@@ -320,6 +320,17 @@ config HW_RANDOM_TPM - - If unsure, say Y. - -+config HW_RANDOM_BCM2708 -+ tristate "BCM2708 generic true random number generator support" -+ depends on HW_RANDOM && ARCH_BCM2708 -+ ---help--- -+ This driver provides the kernel-side support for the BCM2708 hardware. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called bcm2708-rng. -+ -+ If unsure, say N. -+ - config HW_RANDOM_MSM - tristate "Qualcomm SoCs Random Number Generator support" - depends on HW_RANDOM && ARCH_QCOM -diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile -index 0b4cd57..78b019c 100644 ---- a/drivers/char/hw_random/Makefile -+++ b/drivers/char/hw_random/Makefile -@@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o - obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o - obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o - obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o -+obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o - obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o - obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o -diff --git a/drivers/char/hw_random/bcm2708-rng.c b/drivers/char/hw_random/bcm2708-rng.c -new file mode 100755 -index 0000000..340f004 +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 006242c..4e6f46c3 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -524,6 +524,7 @@ source "drivers/misc/carma/Kconfig" + source "drivers/misc/altera-stapl/Kconfig" + source "drivers/misc/mei/Kconfig" + source "drivers/misc/vmw_vmci/Kconfig" ++source "drivers/misc/vc04_services/Kconfig" + source "drivers/misc/mic/Kconfig" + source "drivers/misc/genwqe/Kconfig" + source "drivers/misc/echo/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 7d5c4cd..43d2ac9 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ + obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ + obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o + obj-$(CONFIG_SRAM) += sram.o ++obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/ + obj-y += mic/ + obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ +diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig +new file mode 100644 +index 0000000..b94e6cd --- /dev/null -+++ b/drivers/char/hw_random/bcm2708-rng.c -@@ -0,0 +1,118 @@ ++++ b/drivers/misc/vc04_services/Kconfig +@@ -0,0 +1,9 @@ ++config BCM2708_VCHIQ ++ tristate "Videocore VCHIQ" ++ depends on MACH_BCM2708 || MACH_BCM2709 ++ default y ++ help ++ Kernel to VideoCore communication interface for the ++ BCM2708 family of products. ++ Defaults to Y when the Broadcom Videocore services ++ are included in the build, N otherwise. +diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile +new file mode 100644 +index 0000000..8d038fe +--- /dev/null ++++ b/drivers/misc/vc04_services/Makefile +@@ -0,0 +1,14 @@ ++obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o ++ ++vchiq-objs := \ ++ interface/vchiq_arm/vchiq_core.o \ ++ interface/vchiq_arm/vchiq_arm.o \ ++ interface/vchiq_arm/vchiq_kern_lib.o \ ++ interface/vchiq_arm/vchiq_2835_arm.o \ ++ interface/vchiq_arm/vchiq_debugfs.o \ ++ interface/vchiq_arm/vchiq_shim.o \ ++ interface/vchiq_arm/vchiq_util.o \ ++ interface/vchiq_arm/vchiq_connected.o \ ++ ++ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 ++ +diff --git a/drivers/misc/vc04_services/interface/vchi/connections/connection.h b/drivers/misc/vc04_services/interface/vchi/connections/connection.h +new file mode 100644 +index 0000000..fef6ac3 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/connections/connection.h +@@ -0,0 +1,328 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef CONNECTION_H_ ++#define CONNECTION_H_ ++ ++#include ++#include ++#include ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/message_drivers/message.h" ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++// Opaque handle for a connection / service pair ++typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; ++ ++// opaque handle to the connection state information ++typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; ++ ++typedef struct vchi_connection_t VCHI_CONNECTION_T; ++ ++ ++/****************************************************************************** ++ API ++ *****************************************************************************/ ++ ++// Routine to init a connection with a particular low level driver ++typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, ++ const VCHI_MESSAGE_DRIVER_T * driver ); ++ ++// Routine to control CRC enabling at a connection level ++typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ VCHI_CRC_CONTROL_T control ); ++ ++// Routine to create a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ int32_t service_id, ++ uint32_t rx_fifo_size, ++ uint32_t tx_fifo_size, ++ int server, ++ VCHI_CALLBACK_T callback, ++ void *callback_param, ++ int32_t want_crc, ++ int32_t want_unaligned_bulk_rx, ++ int32_t want_unaligned_bulk_tx, ++ VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); ++ ++// Routine to close a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); ++ ++// Routine to queue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) message queueing ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to dequeue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to peek at a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to hold a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ void **message_handle ); ++ ++// Routine to initialise a received message iterator ++typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to release a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle ); ++ ++// Routine to get info on a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle, ++ void **data, ++ int32_t *msg_size, ++ uint32_t *tx_timestamp, ++ uint32_t *rx_timestamp ); ++ ++// Routine to check whether the iterator has a next message ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ const VCHI_MSG_ITER_T *iter ); ++ ++// Routine to advance the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Routine to remove the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter ); ++ ++// Routine to hold the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **msg_handle ); ++ ++// Routine to transmit bulk data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to receive data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to report if a server is available ++typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t peer_flags ); ++ ++// Routine to report the number of RX slots available ++typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Routine to report the RX slot size ++typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, ++ int32_t service, ++ uint32_t length, ++ MESSAGE_TX_CHANNEL_T channel, ++ uint32_t channel_params, ++ uint32_t data_length, ++ uint32_t data_offset); ++ ++// Callback to inform a service that a Xon or Xoff message has been received ++typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t xoff); ++ ++// Callback to inform a service that a server available reply message has been received ++typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, uint32_t flags); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); ++ ++// Callback with all the connection info you require ++typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); ++ ++// Callback to inform of a disconnect ++typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); ++ ++// Callback to inform of a power control request ++typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, int32_t enable); ++ ++// allocate memory suitably aligned for this connection ++typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); ++ ++// free memory allocated by buffer_allocate ++typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); ++ ++ ++/****************************************************************************** ++ System driver struct ++ *****************************************************************************/ ++ ++struct opaque_vchi_connection_api_t ++{ ++ // Routine to init the connection ++ VCHI_CONNECTION_INIT_T init; ++ ++ // Connection-level CRC control ++ VCHI_CONNECTION_CRC_CONTROL_T crc_control; ++ ++ // Routine to connect to or create service ++ VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; ++ ++ // Routine to disconnect from a service ++ VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; ++ ++ // Routine to queue a message ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; ++ ++ // scatter-gather (vector) message queue ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; ++ ++ // Routine to dequeue a message ++ VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; ++ ++ // Routine to peek at a message ++ VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; ++ ++ // Routine to hold a message ++ VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; ++ ++ // Routine to initialise a received message iterator ++ VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; ++ ++ // Routine to release a message ++ VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; ++ ++ // Routine to get information on a held message ++ VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; ++ ++ // Routine to check for next message on iterator ++ VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; ++ ++ // Routine to get next message on iterator ++ VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; ++ ++ // Routine to remove the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; ++ ++ // Routine to hold the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; ++ ++ // Routine to transmit bulk data ++ VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; ++ ++ // Routine to receive data ++ VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; ++ ++ // Routine to report the available servers ++ VCHI_CONNECTION_SERVER_PRESENT server_present; ++ ++ // Routine to report the number of RX slots available ++ VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; ++ ++ // Routine to report the RX slot size ++ VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; ++ ++ // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++ VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; ++ ++ // Callback to inform a service that a Xon or Xoff message has been received ++ VCHI_CONNECTION_FLOW_CONTROL flow_control; ++ ++ // Callback to inform a service that a server available reply message has been received ++ VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; ++ ++ // Callback to indicate that bulk auxiliary messages have arrived ++ VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; ++ ++ // Callback to indicate that a bulk auxiliary message has been transmitted ++ VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; ++ ++ // Callback to provide information about the connection ++ VCHI_CONNECTION_INFO connection_info; ++ ++ // Callback to notify that peer has requested disconnect ++ VCHI_CONNECTION_DISCONNECT disconnect; ++ ++ // Callback to notify that peer has requested power change ++ VCHI_CONNECTION_POWER_CONTROL power_control; ++ ++ // allocate memory suitably aligned for this connection ++ VCHI_BUFFER_ALLOCATE buffer_allocate; ++ ++ // free memory allocated by buffer_allocate ++ VCHI_BUFFER_FREE buffer_free; ++ ++}; ++ ++struct vchi_connection_t { ++ const VCHI_CONNECTION_API_T *api; ++ VCHI_CONNECTION_STATE_T *state; ++#ifdef VCHI_COARSE_LOCKING ++ struct semaphore sem; ++#endif ++}; ++ ++ ++#endif /* CONNECTION_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h b/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h +new file mode 100644 +index 0000000..8b3f767 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h +@@ -0,0 +1,204 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef _VCHI_MESSAGE_H_ ++#define _VCHI_MESSAGE_H_ ++ ++#include ++#include ++#include ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vchi/vchi_common.h" ++ ++ ++typedef enum message_event_type { ++ MESSAGE_EVENT_NONE, ++ MESSAGE_EVENT_NOP, ++ MESSAGE_EVENT_MESSAGE, ++ MESSAGE_EVENT_SLOT_COMPLETE, ++ MESSAGE_EVENT_RX_BULK_PAUSED, ++ MESSAGE_EVENT_RX_BULK_COMPLETE, ++ MESSAGE_EVENT_TX_COMPLETE, ++ MESSAGE_EVENT_MSG_DISCARDED ++} MESSAGE_EVENT_TYPE_T; ++ ++typedef enum vchi_msg_flags ++{ ++ VCHI_MSG_FLAGS_NONE = 0x0, ++ VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 ++} VCHI_MSG_FLAGS_T; ++ ++typedef enum message_tx_channel ++{ ++ MESSAGE_TX_CHANNEL_MESSAGE = 0, ++ MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_TX_CHANNEL_T; ++ ++// Macros used for cycling through bulk channels ++#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++ ++typedef enum message_rx_channel ++{ ++ MESSAGE_RX_CHANNEL_MESSAGE = 0, ++ MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_RX_CHANNEL_T; ++ ++// Message receive slot information ++typedef struct rx_msg_slot_info { ++ ++ struct rx_msg_slot_info *next; ++ //struct slot_info *prev; ++#if !defined VCHI_COARSE_LOCKING ++ struct semaphore sem; ++#endif ++ ++ uint8_t *addr; // base address of slot ++ uint32_t len; // length of slot in bytes ++ ++ uint32_t write_ptr; // hardware causes this to advance ++ uint32_t read_ptr; // this module does the reading ++ int active; // is this slot in the hardware dma fifo? ++ uint32_t msgs_parsed; // count how many messages are in this slot ++ uint32_t msgs_released; // how many messages have been released ++ void *state; // connection state information ++ uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services ++} RX_MSG_SLOTINFO_T; ++ ++// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. ++// In particular, it mustn't use addr and len - they're the client buffer, but the message ++// driver will be tasked with sending the aligned core section. ++typedef struct rx_bulk_slotinfo_t { ++ struct rx_bulk_slotinfo_t *next; ++ ++ struct semaphore *blocking; ++ ++ // needed by DMA ++ void *addr; ++ uint32_t len; ++ ++ // needed for the callback ++ void *service; ++ void *handle; ++ VCHI_FLAGS_T flags; ++} RX_BULK_SLOTINFO_T; ++ ++ ++/* ---------------------------------------------------------------------- ++ * each connection driver will have a pool of the following struct. ++ * ++ * the pool will be managed by vchi_qman_* ++ * this means there will be multiple queues (single linked lists) ++ * a given struct message_info will be on exactly one of these queues ++ * at any one time ++ * -------------------------------------------------------------------- */ ++typedef struct rx_message_info { ++ ++ struct message_info *next; ++ //struct message_info *prev; ++ ++ uint8_t *addr; ++ uint32_t len; ++ RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message ++ uint32_t tx_timestamp; ++ uint32_t rx_timestamp; ++ ++} RX_MESSAGE_INFO_T; ++ ++typedef struct { ++ MESSAGE_EVENT_TYPE_T type; ++ ++ struct { ++ // for messages ++ void *addr; // address of message ++ uint16_t slot_delta; // whether this message indicated slot delta ++ uint32_t len; // length of message ++ RX_MSG_SLOTINFO_T *slot; // slot this message is in ++ int32_t service; // service id this message is destined for ++ uint32_t tx_timestamp; // timestamp from the header ++ uint32_t rx_timestamp; // timestamp when we parsed it ++ } message; ++ ++ // FIXME: cleanup slot reporting... ++ RX_MSG_SLOTINFO_T *rx_msg; ++ RX_BULK_SLOTINFO_T *rx_bulk; ++ void *tx_handle; ++ MESSAGE_TX_CHANNEL_T tx_channel; ++ ++} MESSAGE_EVENT_T; ++ ++ ++// callbacks ++typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); ++ ++typedef struct { ++ VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; ++} VCHI_MESSAGE_DRIVER_OPEN_T; ++ ++ ++// handle to this instance of message driver (as returned by ->open) ++typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; ++ ++struct opaque_vchi_message_driver_t { ++ VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); ++ int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, int32_t enable ); ++ int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message ++ int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) ++ int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) ++ void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver ++ int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, int32_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void ++ *address, uint32_t length_avail, uint32_t max_total_length, int32_t pad_to_fill, int32_t allow_partial ); ++ ++ int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); ++ int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); ++ void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); ++ void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); ++ int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ ++ int32_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); ++ void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); ++ void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); ++}; ++ ++ ++#endif // _VCHI_MESSAGE_H_ ++ ++/****************************** End of file ***********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi.h b/drivers/misc/vc04_services/interface/vchi/vchi.h +new file mode 100644 +index 0000000..1b17e98 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi.h +@@ -0,0 +1,378 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_H_ ++#define VCHI_H_ ++ ++#include "interface/vchi/vchi_cfg.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/connections/connection.h" ++#include "vchi_mh.h" ++ ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) ++ ++#ifdef USE_VCHIQ_ARM ++#define VCHI_BULK_ALIGNED(x) 1 ++#else ++#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) ++#endif ++ ++struct vchi_version { ++ uint32_t version; ++ uint32_t version_min; ++}; ++#define VCHI_VERSION(v_) { v_, v_ } ++#define VCHI_VERSION_EX(v_, m_) { v_, m_ } ++ ++typedef enum ++{ ++ VCHI_VEC_POINTER, ++ VCHI_VEC_HANDLE, ++ VCHI_VEC_LIST ++} VCHI_MSG_VECTOR_TYPE_T; ++ ++typedef struct vchi_msg_vector_ex { ++ ++ VCHI_MSG_VECTOR_TYPE_T type; ++ union ++ { ++ // a memory handle ++ struct ++ { ++ VCHI_MEM_HANDLE_T handle; ++ uint32_t offset; ++ int32_t vec_len; ++ } handle; ++ ++ // an ordinary data pointer ++ struct ++ { ++ const void *vec_base; ++ int32_t vec_len; ++ } ptr; ++ ++ // a nested vector list ++ struct ++ { ++ struct vchi_msg_vector_ex *vec; ++ uint32_t vec_len; ++ } list; ++ } u; ++} VCHI_MSG_VECTOR_EX_T; ++ ++ ++// Construct an entry in a msg vector for a pointer (p) of length (l) ++#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } ++ ++// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) ++#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } ++ ++// Macros to manipulate 'FOURCC' values ++#define MAKE_FOURCC(x) ((int32_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) ++#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF ++ ++ ++// Opaque service information ++struct opaque_vchi_service_t; ++ ++// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, ++// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. ++typedef struct ++{ ++ struct opaque_vchi_service_t *service; ++ void *message; ++} VCHI_HELD_MSG_T; ++ ++ ++ ++// structure used to provide the information needed to open a server or a client ++typedef struct { ++ struct vchi_version version; ++ int32_t service_id; ++ VCHI_CONNECTION_T *connection; ++ uint32_t rx_fifo_size; ++ uint32_t tx_fifo_size; ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++ /* client intends to receive bulk transfers of ++ odd lengths or into unaligned buffers */ ++ int32_t want_unaligned_bulk_rx; ++ /* client intends to transmit bulk transfers of ++ odd lengths or out of unaligned buffers */ ++ int32_t want_unaligned_bulk_tx; ++ /* client wants to check CRCs on (bulk) xfers. ++ Only needs to be set at 1 end - will do both directions. */ ++ int32_t want_crc; ++} SERVICE_CREATION_T; ++ ++// Opaque handle for a VCHI instance ++typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; ++ ++// Opaque handle for a server or client ++typedef struct opaque_vchi_service_handle_t *VCHI_SERVICE_HANDLE_T; ++ ++// Service registration & startup ++typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); ++ ++typedef struct service_info_tag { ++ const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ ++ VCHI_SERVICE_INIT init; /* Service initialisation function */ ++ void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ ++} SERVICE_INFO_T; ++ ++/****************************************************************************** ++ Global funcs - implementation is specific to which side you are on (local / remote) ++ *****************************************************************************/ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, ++ const VCHI_MESSAGE_DRIVER_T * low_level); ++ ++ ++// Routine used to initialise the vchi on both local + remote connections ++extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); ++ ++extern int32_t vchi_exit( void ); ++ ++extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle ); ++ ++//When this is called, ensure that all services have no data pending. ++//Bulk transfers can remain 'queued' ++extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); ++ ++// Global control over bulk CRC checking ++extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, ++ VCHI_CRC_CONTROL_T control ); ++ ++// helper functions ++extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); ++extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); ++extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); ++ ++ ++/****************************************************************************** ++ Global service API ++ *****************************************************************************/ ++// Routine to create a named service ++extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle ); ++ ++// Routine to destory a service ++extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to open a named service ++extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle); ++ ++extern int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, ++ short *peer_version ); ++ ++// Routine to close a named service ++extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to increment ref count on a named service ++extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to decrement ref count on a named service ++extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to set a control option for a named service ++extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_SERVICE_OPTION_T option, ++ int value); ++ ++// Routine to send a message across a service ++extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) and send message ++int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_EX_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// legacy scatter-gather (vector) and send message, only handles pointers ++int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to receive a msg from a service ++// Dequeue is equivalent to hold, copy into client buffer, release ++extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to look at a message in place. ++// The message is not dequeued, so a subsequent call to peek or dequeue ++// will return the same message. ++extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to remove a message after it has been read in place with peek ++// The first message on the queue is dequeued. ++extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to look at a message in place. ++// The message is dequeued, so the caller is left holding it; the descriptor is ++// filled in and must be released when the user has finished with the message. ++extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, ++ void **data, // } may be NULL, as info can be ++ uint32_t *msg_size, // } obtained from HELD_MSG_T ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_descriptor ); ++ ++// Initialise an iterator to look through messages in place ++extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++/****************************************************************************** ++ Global service support API - operations on held messages and message iterators ++ *****************************************************************************/ ++ ++// Routine to get the address of a held message ++extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the size of a held message ++extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the transmit timestamp as written into the header by the peer ++extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the reception timestamp, written as we parsed the header ++extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to release a held message after it has been processed ++extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); ++ ++// Indicates whether the iterator has a next message. ++extern int32_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); ++ ++// Return the pointer and length for the next message and advance the iterator. ++extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Remove the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); ++ ++// Hold the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, ++ VCHI_HELD_MSG_T *message ); ++ ++// Return information for the next message, and hold it, advancing the iterator. ++extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, ++ void **data, // } may be NULL ++ uint32_t *msg_size, // } ++ VCHI_HELD_MSG_T *message ); ++ ++ ++/****************************************************************************** ++ Global bulk API ++ *****************************************************************************/ ++ ++// Routine to prepare interface for a transfer from the other side ++extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++// Prepare interface for a transfer from the other side into relocatable memory. ++int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_dst, ++ uint32_t offset, ++ uint32_t data_size, ++ const VCHI_FLAGS_T flags, ++ void * const bulk_handle ); ++ ++// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) ++extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++/****************************************************************************** ++ Configuration plumbing ++ *****************************************************************************/ ++ ++// function prototypes for the different mid layers (the state info gives the different physical connections) ++extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); ++ ++// declare all message drivers here ++const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_src, ++ uint32_t offset, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++#endif /* VCHI_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h +new file mode 100644 +index 0000000..26bc2d38 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h +@@ -0,0 +1,224 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_CFG_H_ ++#define VCHI_CFG_H_ ++ ++/**************************************************************************************** ++ * Defines in this first section are part of the VCHI API and may be examined by VCHI ++ * services. ++ ***************************************************************************************/ ++ ++/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_ALIGN ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_ALIGN 16 ++# endif ++#endif ++ ++/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ ++/* May be less than or greater than VCHI_BULK_ALIGN */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_GRANULARITY ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_GRANULARITY 16 ++# endif ++#endif ++ ++/* The largest possible message to be queued with vchi_msg_queue. */ ++#ifndef VCHI_MAX_MSG_SIZE ++# if defined VCHI_LOCAL_HOST_PORT ++# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? ++# else ++# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! ++# endif ++#endif ++ ++/****************************************************************************************** ++ * Defines below are system configuration options, and should not be used by VCHI services. ++ *****************************************************************************************/ ++ ++/* How many connections can we support? A localhost implementation uses 2 connections, ++ * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW ++ * driver. */ ++#ifndef VCHI_MAX_NUM_CONNECTIONS ++# define VCHI_MAX_NUM_CONNECTIONS 3 ++#endif ++ ++/* How many services can we open per connection? Extending this doesn't cost processing time, just a small ++ * amount of static memory. */ ++#ifndef VCHI_MAX_SERVICES_PER_CONNECTION ++# define VCHI_MAX_SERVICES_PER_CONNECTION 36 ++#endif ++ ++/* Adjust if using a message driver that supports more logical TX channels */ ++#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels ++#endif ++ ++/* Adjust if using a message driver that supports more logical RX channels */ ++#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI ++#endif ++ ++/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective ++ * receive queue space, less message headers. */ ++#ifndef VCHI_NUM_READ_SLOTS ++# if defined(VCHI_LOCAL_HOST_PORT) ++# define VCHI_NUM_READ_SLOTS 4 ++# else ++# define VCHI_NUM_READ_SLOTS 48 ++# endif ++#endif ++ ++/* Do we utilise overrun facility for receive message slots? Can aid peer transmit ++ * performance. Only define on VideoCore end, talking to host. ++ */ ++//#define VCHI_MSG_RX_OVERRUN ++ ++/* How many transmit slots do we use. Generally don't need many, as the hardware driver ++ * underneath VCHI will usually have its own buffering. */ ++#ifndef VCHI_NUM_WRITE_SLOTS ++# define VCHI_NUM_WRITE_SLOTS 4 ++#endif ++ ++/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, ++ * then it's taking up too much buffer space, and the peer service will be told to stop ++ * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS ++ * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency ++ * is too high. */ ++#ifndef VCHI_XOFF_THRESHOLD ++# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) ++#endif ++ ++/* After we've sent an XOFF, the peer will be told to resume transmission once the local ++ * service has dequeued/released enough messages that it's now occupying ++ * VCHI_XON_THRESHOLD slots or fewer. */ ++#ifndef VCHI_XON_THRESHOLD ++# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) ++#endif ++ ++/* A size below which a bulk transfer omits the handshake completely and always goes ++ * via the message channel, if bulk auxiliary is being sent on that service. (The user ++ * can guarantee this by enabling unaligned transmits). ++ * Not API. */ ++#ifndef VCHI_MIN_BULK_SIZE ++# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) ++#endif ++ ++/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between ++ * speed and latency; the smaller the chunk size the better change of messages and other ++ * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not ++ * break transmissions into chunks. ++ */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI ++# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) ++#endif ++ ++/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode ++ * with multiple-line frames. Only use if the receiver can cope. */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 ++# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 ++#endif ++ ++/* How many TX messages can we have pending in our transmit slots. Once exhausted, ++ * vchi_msg_queue will be blocked. */ ++#ifndef VCHI_TX_MSG_QUEUE_SIZE ++# define VCHI_TX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing ++ * will be suspended until older messages are dequeued/released. */ ++#ifndef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* Really should be able to cope if we run out of received message descriptors, by ++ * suspending parsing as the comment above says, but we don't. This sweeps the issue ++ * under the carpet. */ ++#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++# undef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++#endif ++ ++/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit ++ * will be blocked. */ ++#ifndef VCHI_TX_BULK_QUEUE_SIZE ++# define VCHI_TX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive ++ * will be blocked. */ ++#ifndef VCHI_RX_BULK_QUEUE_SIZE ++# define VCHI_RX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* A limit on how many outstanding bulk requests we expect the peer to give us. If ++ * the peer asks for more than this, VCHI will fail and assert. The number is determined ++ * by the peer's hardware - it's the number of outstanding requests that can be queued ++ * on all bulk channels. VC3's MPHI peripheral allows 16. */ ++#ifndef VCHI_MAX_PEER_BULK_REQUESTS ++# define VCHI_MAX_PEER_BULK_REQUESTS 32 ++#endif ++ ++/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 ++ * transmitter on and off. ++ */ ++/*#define VCHI_CCP2TX_MANUAL_POWER*/ ++ ++#ifndef VCHI_CCP2TX_MANUAL_POWER ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set ++ * negative for no IDLE. ++ */ ++# ifndef VCHI_CCP2TX_IDLE_TIMEOUT ++# define VCHI_CCP2TX_IDLE_TIMEOUT 5 ++# endif ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set ++ * negative for no OFF. ++ */ ++# ifndef VCHI_CCP2TX_OFF_TIMEOUT ++# define VCHI_CCP2TX_OFF_TIMEOUT 1000 ++# endif ++ ++#endif /* VCHI_CCP2TX_MANUAL_POWER */ ++ ++#endif /* VCHI_CFG_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h +new file mode 100644 +index 0000000..35dcba4 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h +@@ -0,0 +1,71 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_CFG_INTERNAL_H_ ++#define VCHI_CFG_INTERNAL_H_ ++ ++/**************************************************************************************** ++ * Control optimisation attempts. ++ ***************************************************************************************/ ++ ++// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second ++#define VCHI_COARSE_LOCKING ++ ++// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_ELIDE_BLOCK_EXIT_LOCK ++ ++// Avoid lock on non-blocking peek ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_AVOID_PEEK_LOCK ++ ++// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. ++#define VCHI_MULTIPLE_HANDLER_THREADS ++ ++// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash ++// our way through the pool of descriptors. ++#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD ++ ++// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. ++#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS ++ ++// Don't use message descriptors for TX messages that don't need them ++#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS ++ ++// Nano-locks for multiqueue ++//#define VCHI_MQUEUE_NANOLOCKS ++ ++// Lock-free(er) dequeuing ++//#define VCHI_RX_NANOLOCKS ++ ++#endif /*VCHI_CFG_INTERNAL_H_*/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h +new file mode 100644 +index 0000000..d535a72 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h +@@ -0,0 +1,175 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_COMMON_H_ ++#define VCHI_COMMON_H_ ++ ++ ++//flags used when sending messages (must be bitmapped) ++typedef enum ++{ ++ VCHI_FLAGS_NONE = 0x0, ++ VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go ++ VCHI_FLAGS_ALLOW_PARTIAL = 0x8, ++ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, ++ VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, ++ ++ VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only ++ VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only ++ VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only ++ VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only ++ VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only ++ VCHI_FLAGS_INTERNAL = 0xFF0000 ++} VCHI_FLAGS_T; ++ ++// constants for vchi_crc_control() ++typedef enum { ++ VCHI_CRC_NOTHING = -1, ++ VCHI_CRC_PER_SERVICE = 0, ++ VCHI_CRC_EVERYTHING = 1, ++} VCHI_CRC_CONTROL_T; ++ ++//callback reasons when an event occurs on a service ++typedef enum ++{ ++ VCHI_CALLBACK_REASON_MIN, ++ ++ //This indicates that there is data available ++ //handle is the msg id that was transmitted with the data ++ // When a message is received and there was no FULL message available previously, send callback ++ // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails ++ VCHI_CALLBACK_MSG_AVAILABLE, ++ VCHI_CALLBACK_MSG_SENT, ++ VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ // This indicates that a transfer from the other side has completed ++ VCHI_CALLBACK_BULK_RECEIVED, ++ //This indicates that data queued up to be sent has now gone ++ //handle is the msg id that was used when sending the data ++ VCHI_CALLBACK_BULK_SENT, ++ VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented ++ VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ VCHI_CALLBACK_SERVICE_CLOSED, ++ ++ // this side has sent XOFF to peer due to lack of data consumption by service ++ // (suggests the service may need to take some recovery action if it has ++ // been deliberately holding off consuming data) ++ VCHI_CALLBACK_SENT_XOFF, ++ VCHI_CALLBACK_SENT_XON, ++ ++ // indicates that a bulk transfer has finished reading the source buffer ++ VCHI_CALLBACK_BULK_DATA_READ, ++ ++ // power notification events (currently host side only) ++ VCHI_CALLBACK_PEER_OFF, ++ VCHI_CALLBACK_PEER_SUSPENDED, ++ VCHI_CALLBACK_PEER_ON, ++ VCHI_CALLBACK_PEER_RESUMED, ++ VCHI_CALLBACK_FORCED_POWER_OFF, ++ ++#ifdef USE_VCHIQ_ARM ++ // some extra notifications provided by vchiq_arm ++ VCHI_CALLBACK_SERVICE_OPENED, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++#endif ++ ++ VCHI_CALLBACK_REASON_MAX ++} VCHI_CALLBACK_REASON_T; ++ ++// service control options ++typedef enum ++{ ++ VCHI_SERVICE_OPTION_MIN, ++ ++ VCHI_SERVICE_OPTION_TRACE, ++ VCHI_SERVICE_OPTION_SYNCHRONOUS, ++ ++ VCHI_SERVICE_OPTION_MAX ++} VCHI_SERVICE_OPTION_T; ++ ++ ++//Callback used by all services / bulk transfers ++typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param ++ VCHI_CALLBACK_REASON_T reason, ++ void *handle ); //for transmitting msg's only ++ ++ ++ ++/* ++ * Define vector struct for scatter-gather (vector) operations ++ * Vectors can be nested - if a vector element has negative length, then ++ * the data pointer is treated as pointing to another vector array, with ++ * '-vec_len' elements. Thus to append a header onto an existing vector, ++ * you can do this: ++ * ++ * void foo(const VCHI_MSG_VECTOR_T *v, int n) ++ * { ++ * VCHI_MSG_VECTOR_T nv[2]; ++ * nv[0].vec_base = my_header; ++ * nv[0].vec_len = sizeof my_header; ++ * nv[1].vec_base = v; ++ * nv[1].vec_len = -n; ++ * ... ++ * ++ */ ++typedef struct vchi_msg_vector { ++ const void *vec_base; ++ int32_t vec_len; ++} VCHI_MSG_VECTOR_T; ++ ++// Opaque type for a connection API ++typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; ++ ++// Opaque type for a message driver ++typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; ++ ++ ++// Iterator structure for reading ahead through received message queue. Allocated by client, ++// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. ++// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - ++// will not proceed to messages received since. Behaviour is undefined if an iterator ++// is used again after messages for that service are removed/dequeued by any ++// means other than vchi_msg_iter_... calls on the iterator itself. ++typedef struct { ++ struct opaque_vchi_service_t *service; ++ void *last; ++ void *next; ++ void *remove; ++} VCHI_MSG_ITER_T; ++ ++ ++#endif // VCHI_COMMON_H_ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +new file mode 100644 +index 0000000..198bd07 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +@@ -0,0 +1,42 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_MH_H_ ++#define VCHI_MH_H_ ++ ++#include ++ ++typedef int32_t VCHI_MEM_HANDLE_T; ++#define VCHI_MEM_HANDLE_INVALID 0 ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h +new file mode 100644 +index 0000000..ad398ba +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h +@@ -0,0 +1,40 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_VCHIQ_H ++#define VCHIQ_VCHIQ_H ++ ++#include "vchiq_if.h" ++#include "vchiq_util.h" ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h +new file mode 100644 +index 0000000..7ea5c64 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h +@@ -0,0 +1,42 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_2835_H ++#define VCHIQ_2835_H ++ ++#include "vchiq_pagelist.h" ++ ++#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0 ++#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 ++ ++#endif /* VCHIQ_2835_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +new file mode 100644 +index 0000000..7e7b09f +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -0,0 +1,562 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * @@ -84430,26135 +86360,11344 @@ index 0000000..340f004 + */ + +#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#define RNG_CTRL (0x0) -+#define RNG_STATUS (0x4) -+#define RNG_DATA (0x8) -+#define RNG_FF_THRESHOLD (0xc) -+ -+/* enable rng */ -+#define RNG_RBGEN 0x1 -+/* double speed, less random mode */ -+#define RNG_RBG2X 0x2 -+ -+/* the initial numbers generated are "less random" so will be discarded */ -+#define RNG_WARMUP_COUNT 0x40000 -+ -+static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer) -+{ -+ void __iomem *rng_base = (void __iomem *)rng->priv; -+ unsigned words; -+ /* wait for a random number to be in fifo */ -+ do { -+ words = __raw_readl(rng_base + RNG_STATUS)>>24; -+ } -+ while (words == 0); -+ /* read the random number */ -+ *buffer = __raw_readl(rng_base + RNG_DATA); -+ return 4; -+} -+ -+static struct hwrng bcm2708_rng_ops = { -+ .name = "bcm2708", -+ .data_read = bcm2708_rng_data_read, -+}; -+ -+static int __init bcm2708_rng_init(void) -+{ -+ void __iomem *rng_base; -+ int err; -+ -+ /* map peripheral */ -+ rng_base = ioremap(RNG_BASE, 0x10); -+ pr_info("bcm2708_rng_init=%p\n", rng_base); -+ if (!rng_base) { -+ pr_err("bcm2708_rng_init failed to ioremap\n"); -+ return -ENOMEM; -+ } -+ bcm2708_rng_ops.priv = (unsigned long)rng_base; -+ -+ /* set warm-up count & enable */ -+ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); -+ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); -+ -+ /* register driver */ -+ err = hwrng_register(&bcm2708_rng_ops); -+ if (err) { -+ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err); -+ iounmap(rng_base); -+ } -+ return err; -+} -+ -+static void __exit bcm2708_rng_exit(void) -+{ -+ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv; -+ pr_info("bcm2708_rng_exit\n"); -+ /* disable rng hardware */ -+ __raw_writel(0, rng_base + RNG_CTRL); -+ /* unregister driver */ -+ hwrng_unregister(&bcm2708_rng_ops); -+ iounmap(rng_base); -+} -+ -+module_init(bcm2708_rng_init); -+module_exit(bcm2708_rng_exit); -+ -+MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver"); -+MODULE_LICENSE("GPL and additional rights"); - -From f543037234f72aed89e2239f62f2dce7ea94aefa Mon Sep 17 00:00:00 2001 -From: Aron Szabo -Date: Sat, 16 Jun 2012 12:15:55 +0200 -Subject: [PATCH 016/172] lirc: added support for RaspberryPi GPIO - -lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others -See: https://github.com/raspberrypi/linux/issues/525 - -lirc: Remove restriction on gpio pins that can be used with lirc - -Compute Module, for example could use different pins - -lirc_rpi: Add parameter to specify input pin pull - -Depending on the connected IR circuitry it might be desirable to change the -gpios internal pull from it pull-down default behaviour. Add a module -parameter to allow the user to set it explicitly. - -Signed-off-by: Julian Scheel - -lirc-rpi: Use the higher-level irq control functions - -This module used to access the irq_chip methods of the -gpio controller directly, rather than going through the -standard enable_irq/irq_set_irq_type functions. This -caused problems on pinctrl-bcm2835 which only implements -the irq_enable/disable methods and not irq_unmask/mask. - -lirc-rpi: Correct the interrupt usage - -1) Correct the use of enable_irq (i.e. don't call it so often) -2) Correct the shutdown sequence. -3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier ---- - drivers/staging/media/lirc/Kconfig | 6 + - drivers/staging/media/lirc/Makefile | 1 + - drivers/staging/media/lirc/lirc_rpi.c | 659 ++++++++++++++++++++++++++++++++++ - 3 files changed, 666 insertions(+) - create mode 100644 drivers/staging/media/lirc/lirc_rpi.c - -diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig -index 6879c46..77c69f2 100644 ---- a/drivers/staging/media/lirc/Kconfig -+++ b/drivers/staging/media/lirc/Kconfig -@@ -32,6 +32,12 @@ config LIRC_PARALLEL - help - Driver for Homebrew Parallel Port Receivers - -+config LIRC_RPI -+ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" -+ depends on LIRC -+ help -+ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi -+ - config LIRC_SASEM - tristate "Sasem USB IR Remote" - depends on LIRC && USB -diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile -index 5430adf..9e53cd0 100644 ---- a/drivers/staging/media/lirc/Makefile -+++ b/drivers/staging/media/lirc/Makefile -@@ -6,6 +6,7 @@ - obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o - obj-$(CONFIG_LIRC_IMON) += lirc_imon.o - obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o -+obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o - obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o - obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o - obj-$(CONFIG_LIRC_SIR) += lirc_sir.o -diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -new file mode 100644 -index 0000000..c688364 ---- /dev/null -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -0,0 +1,659 @@ -+/* -+ * lirc_rpi.c -+ * -+ * lirc_rpi - Device driver that records pulse- and pause-lengths -+ * (space-lengths) (just like the lirc_serial driver does) -+ * between GPIO interrupt events on the Raspberry Pi. -+ * Lots of code has been taken from the lirc_serial module, -+ * so I would like say thanks to the authors. -+ * -+ * Copyright (C) 2012 Aron Robert Szabo , -+ * Michael Bishop -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include ++#include +#include +#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include +#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#include ++#include + -+#define LIRC_DRIVER_NAME "lirc_rpi" -+#define RBUF_LEN 256 -+#define LIRC_TRANSMITTER_LATENCY 50 ++#include ++#include + -+#ifndef MAX_UDELAY_MS -+#define MAX_UDELAY_US 5000 -+#else -+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) -+#endif ++#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) + -+#define dprintk(fmt, args...) \ -+ do { \ -+ if (debug) \ -+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ -+ fmt, ## args); \ -+ } while (0) ++#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 ++#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x)) + -+/* module parameters */ ++#include "vchiq_arm.h" ++#include "vchiq_2835.h" ++#include "vchiq_connected.h" ++#include "vchiq_killable.h" + -+/* set the default GPIO input pin */ -+static int gpio_in_pin = 18; -+/* set the default pull behaviour for input pin */ -+static int gpio_in_pull = BCM2708_PULL_DOWN; -+/* set the default GPIO output pin */ -+static int gpio_out_pin = 17; -+/* enable debugging messages */ -+static bool debug; -+/* -1 = auto, 0 = active high, 1 = active low */ -+static int sense = -1; -+/* use softcarrier by default */ -+static bool softcarrier = 1; -+/* 0 = do not invert output, 1 = invert output */ -+static bool invert = 0; ++#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) + -+struct gpio_chip *gpiochip; -+static int irq_num; ++typedef struct vchiq_2835_state_struct { ++ int inited; ++ VCHIQ_ARM_STATE_T arm_state; ++} VCHIQ_2835_ARM_STATE_T; + -+/* forward declarations */ -+static long send_pulse(unsigned long length); -+static void send_space(long length); -+static void lirc_rpi_exit(void); ++static char *g_slot_mem; ++static int g_slot_mem_size; ++dma_addr_t g_slot_phys; ++static FRAGMENTS_T *g_fragments_base; ++static FRAGMENTS_T *g_free_fragments; ++struct semaphore g_free_fragments_sema; + -+static struct platform_device *lirc_rpi_dev; -+static struct timeval lasttv = { 0, 0 }; -+static struct lirc_buffer rbuf; -+static spinlock_t lock; ++extern int vchiq_arm_log_level; + -+/* initialized/set in init_timing_params() */ -+static unsigned int freq = 38000; -+static unsigned int duty_cycle = 50; -+static unsigned long period; -+static unsigned long pulse_width; -+static unsigned long space_width; ++static DEFINE_SEMAPHORE(g_free_fragments_mutex); + -+static void safe_udelay(unsigned long usecs) ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id); ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist); ++ ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual); ++ ++int __init ++vchiq_platform_init(VCHIQ_STATE_T *state) +{ -+ while (usecs > MAX_UDELAY_US) { -+ udelay(MAX_UDELAY_US); -+ usecs -= MAX_UDELAY_US; ++ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; ++ int frag_mem_size; ++ int err; ++ int i; ++ ++ /* Allocate space for the channels in coherent memory */ ++ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); ++ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS); ++ ++ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size, ++ &g_slot_phys, GFP_ATOMIC); ++ ++ if (!g_slot_mem) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocate channel memory"); ++ err = -ENOMEM; ++ goto failed_alloc; + } -+ udelay(usecs); -+} + -+static int init_timing_params(unsigned int new_duty_cycle, -+ unsigned int new_freq) -+{ -+ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= -+ LIRC_TRANSMITTER_LATENCY) -+ return -EINVAL; -+ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= -+ LIRC_TRANSMITTER_LATENCY) -+ return -EINVAL; -+ duty_cycle = new_duty_cycle; -+ freq = new_freq; -+ period = 1000 * 1000000L / freq; -+ pulse_width = period * duty_cycle / 100; -+ space_width = period - pulse_width; -+ dprintk("in init_timing_params, freq=%d pulse=%ld, " -+ "space=%ld\n", freq, pulse_width, space_width); -+ return 0; -+} ++ WARN_ON(((int)g_slot_mem & (PAGE_SIZE - 1)) != 0); + -+static long send_pulse_softcarrier(unsigned long length) -+{ -+ int flag; -+ unsigned long actual, target; -+ unsigned long actual_us, initial_us, target_us; -+ -+ length *= 1000; -+ -+ actual = 0; target = 0; flag = 0; -+ read_current_timer(&actual_us); -+ -+ while (actual < length) { -+ if (flag) { -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ target += space_width; -+ } else { -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); -+ target += pulse_width; -+ } -+ initial_us = actual_us; -+ target_us = actual_us + (target - actual) / 1000; -+ /* -+ * Note - we've checked in ioctl that the pulse/space -+ * widths are big enough so that d is > 0 -+ */ -+ if ((int)(target_us - actual_us) > 0) -+ udelay(target_us - actual_us); -+ read_current_timer(&actual_us); -+ actual += (actual_us - initial_us) * 1000; -+ flag = !flag; ++ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size); ++ if (!vchiq_slot_zero) { ++ err = -EINVAL; ++ goto failed_init_slots; + } -+ return (actual-length) / 1000; ++ ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = ++ (int)g_slot_phys + g_slot_mem_size; ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = ++ MAX_FRAGMENTS; ++ ++ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size); ++ g_slot_mem_size += frag_mem_size; ++ ++ g_free_fragments = g_fragments_base; ++ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { ++ *(FRAGMENTS_T **)&g_fragments_base[i] = ++ &g_fragments_base[i + 1]; ++ } ++ *(FRAGMENTS_T **)&g_fragments_base[i] = NULL; ++ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); ++ ++ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) != ++ VCHIQ_SUCCESS) { ++ err = -EINVAL; ++ goto failed_vchiq_init; ++ } ++ ++ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq, ++ IRQF_IRQPOLL, "VCHIQ doorbell", ++ state); ++ if (err < 0) { ++ vchiq_log_error(vchiq_arm_log_level, "%s: failed to register " ++ "irq=%d err=%d", __func__, ++ VCHIQ_DOORBELL_IRQ, err); ++ goto failed_request_irq; ++ } ++ ++ /* Send the base address of the slots to VideoCore */ ++ ++ dsb(); /* Ensure all writes have completed */ ++ ++ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys); ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq_init - done (slots %x, phys %x)", ++ (unsigned int)vchiq_slot_zero, g_slot_phys); ++ ++ vchiq_call_connected_callbacks(); ++ ++ return 0; ++ ++failed_request_irq: ++failed_vchiq_init: ++failed_init_slots: ++ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys); ++ ++failed_alloc: ++ return err; +} + -+static long send_pulse(unsigned long length) ++void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state) +{ -+ if (length <= 0) -+ return 0; ++ free_irq(VCHIQ_DOORBELL_IRQ, state); ++ dma_free_coherent(NULL, g_slot_mem_size, ++ g_slot_mem, g_slot_phys); ++} + -+ if (softcarrier) { -+ return send_pulse_softcarrier(length); ++ ++VCHIQ_STATUS_T ++vchiq_platform_init_state(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ state->platform_state = kzalloc(sizeof(VCHIQ_2835_ARM_STATE_T), GFP_KERNEL); ++ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 1; ++ status = vchiq_arm_init_state(state, &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state); ++ if(status != VCHIQ_SUCCESS) ++ { ++ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 0; ++ } ++ return status; ++} ++ ++VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state) ++{ ++ if(!((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited) ++ { ++ BUG(); ++ } ++ return &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state; ++} ++ ++void ++remote_event_signal(REMOTE_EVENT_T *event) ++{ ++ wmb(); ++ ++ event->fired = 1; ++ ++ dsb(); /* data barrier operation */ ++ ++ if (event->armed) { ++ /* trigger vc interrupt */ ++ ++ writel(0, __io_address(ARM_0_BELL2)); ++ } ++} ++ ++int ++vchiq_copy_from_user(void *dst, const void *src, int size) ++{ ++ if ((uint32_t)src < TASK_SIZE) { ++ return copy_from_user(dst, src, size); + } else { -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); -+ safe_udelay(length); ++ memcpy(dst, src, size); + return 0; + } +} + -+static void send_space(long length) ++VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, ++ void *offset, int size, int dir) +{ -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ if (length <= 0) -+ return; -+ safe_udelay(length); ++ PAGELIST_T *pagelist; ++ int ret; ++ ++ WARN_ON(memhandle != VCHI_MEM_HANDLE_INVALID); ++ ++ ret = create_pagelist((char __user *)offset, size, ++ (dir == VCHIQ_BULK_RECEIVE) ++ ? PAGELIST_READ ++ : PAGELIST_WRITE, ++ current, ++ &pagelist); ++ if (ret != 0) ++ return VCHIQ_ERROR; ++ ++ bulk->handle = memhandle; ++ bulk->data = VCHIQ_ARM_ADDRESS(pagelist); ++ ++ /* Store the pagelist address in remote_data, which isn't used by the ++ slave. */ ++ bulk->remote_data = pagelist; ++ ++ return VCHIQ_SUCCESS; +} + -+static void rbwrite(int l) ++void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk) +{ -+ if (lirc_buffer_full(&rbuf)) { -+ /* no new signals will be accepted */ -+ dprintk("Buffer overrun\n"); -+ return; -+ } -+ lirc_buffer_write(&rbuf, (void *)&l); ++ if (bulk && bulk->remote_data && bulk->actual) ++ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual); +} + -+static void frbwrite(int l) ++void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk) +{ -+ /* simple noise filter */ -+ static int pulse, space; -+ static unsigned int ptr; -+ -+ if (ptr > 0 && (l & PULSE_BIT)) { -+ pulse += l & PULSE_MASK; -+ if (pulse > 250) { -+ rbwrite(space); -+ rbwrite(pulse | PULSE_BIT); -+ ptr = 0; -+ pulse = 0; -+ } -+ return; -+ } -+ if (!(l & PULSE_BIT)) { -+ if (ptr == 0) { -+ if (l > 20000) { -+ space = l; -+ ptr++; -+ return; -+ } -+ } else { -+ if (l > 20000) { -+ space += pulse; -+ if (space > PULSE_MASK) -+ space = PULSE_MASK; -+ space += l; -+ if (space > PULSE_MASK) -+ space = PULSE_MASK; -+ pulse = 0; -+ return; -+ } -+ rbwrite(space); -+ rbwrite(pulse | PULSE_BIT); -+ ptr = 0; -+ pulse = 0; -+ } -+ } -+ rbwrite(l); ++ /* ++ * This should only be called on the master (VideoCore) side, but ++ * provide an implementation to avoid the need for ifdefery. ++ */ ++ BUG(); +} + -+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) ++void ++vchiq_dump_platform_state(void *dump_context) +{ -+ struct timeval tv; -+ long deltv; -+ int data; -+ int signal; -+ -+ /* use the GPIO signal level */ -+ signal = gpiochip->get(gpiochip, gpio_in_pin); -+ -+ if (sense != -1) { -+ /* get current time */ -+ do_gettimeofday(&tv); -+ -+ /* calc time since last interrupt in microseconds */ -+ deltv = tv.tv_sec-lasttv.tv_sec; -+ if (tv.tv_sec < lasttv.tv_sec || -+ (tv.tv_sec == lasttv.tv_sec && -+ tv.tv_usec < lasttv.tv_usec)) { -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": AIEEEE: your clock just jumped backwards\n"); -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": %d %d %lx %lx %lx %lx\n", signal, sense, -+ tv.tv_sec, lasttv.tv_sec, -+ tv.tv_usec, lasttv.tv_usec); -+ data = PULSE_MASK; -+ } else if (deltv > 15) { -+ data = PULSE_MASK; /* really long time */ -+ if (!(signal^sense)) { -+ /* sanity check */ -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": AIEEEE: %d %d %lx %lx %lx %lx\n", -+ signal, sense, tv.tv_sec, lasttv.tv_sec, -+ tv.tv_usec, lasttv.tv_usec); -+ /* -+ * detecting pulse while this -+ * MUST be a space! -+ */ -+ sense = sense ? 0 : 1; -+ } -+ } else { -+ data = (int) (deltv*1000000 + -+ (tv.tv_usec - lasttv.tv_usec)); -+ } -+ frbwrite(signal^sense ? data : (data|PULSE_BIT)); -+ lasttv = tv; -+ wake_up_interruptible(&rbuf.wait_poll); -+ } -+ -+ return IRQ_HANDLED; ++ char buf[80]; ++ int len; ++ len = snprintf(buf, sizeof(buf), ++ " Platform: 2835 (VC master)"); ++ vchiq_dump(dump_context, buf, len + 1); +} + -+static int is_right_chip(struct gpio_chip *chip, void *data) ++VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state) +{ -+ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); -+ -+ if (strcmp(data, chip->label) == 0) -+ return 1; -+ return 0; ++ return VCHIQ_ERROR; +} + -+static int init_port(void) ++VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state) +{ -+ int i, nlow, nhigh, ret; ++ return VCHIQ_SUCCESS; ++} + -+ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++void ++vchiq_platform_paused(VCHIQ_STATE_T *state) ++{ ++} + -+ if (!gpiochip) -+ return -ENODEV; ++void ++vchiq_platform_resumed(VCHIQ_STATE_T *state) ++{ ++} + -+ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { -+ printk(KERN_ALERT LIRC_DRIVER_NAME -+ ": cant claim gpio pin %d\n", gpio_out_pin); -+ ret = -ENODEV; -+ goto exit_init_port; ++int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state) ++{ ++ return 1; // autosuspend not supported - videocore always wanted ++} ++ ++int ++vchiq_platform_use_suspend_timer(void) ++{ ++ return 0; ++} ++void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state) ++{ ++ vchiq_log_info((vchiq_arm_log_level>=VCHIQ_LOG_INFO),"Suspend timer not in use"); ++} ++void ++vchiq_platform_handle_timeout(VCHIQ_STATE_T *state) ++{ ++ (void)state; ++} ++/* ++ * Local functions ++ */ ++ ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id) ++{ ++ VCHIQ_STATE_T *state = dev_id; ++ irqreturn_t ret = IRQ_NONE; ++ unsigned int status; ++ ++ /* Read (and clear) the doorbell */ ++ status = readl(__io_address(ARM_0_BELL0)); ++ ++ if (status & 0x4) { /* Was the doorbell rung? */ ++ remote_event_pollall(state); ++ ret = IRQ_HANDLED; + } + -+ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { -+ printk(KERN_ALERT LIRC_DRIVER_NAME -+ ": cant claim gpio pin %d\n", gpio_in_pin); -+ ret = -ENODEV; -+ goto exit_gpio_free_out_pin; -+ } -+ -+ bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); -+ gpiochip->direction_input(gpiochip, gpio_in_pin); -+ gpiochip->direction_output(gpiochip, gpio_out_pin, 1); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ -+ irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); -+ dprintk("to_irq %d\n", irq_num); -+ -+ /* if pin is high, then this must be an active low receiver. */ -+ if (sense == -1) { -+ /* wait 1/2 sec for the power supply */ -+ msleep(500); -+ -+ /* -+ * probe 9 times every 0.04s, collect "votes" for -+ * active high/low -+ */ -+ nlow = 0; -+ nhigh = 0; -+ for (i = 0; i < 9; i++) { -+ if (gpiochip->get(gpiochip, gpio_in_pin)) -+ nlow++; -+ else -+ nhigh++; -+ msleep(40); -+ } -+ sense = (nlow >= nhigh ? 1 : 0); -+ printk(KERN_INFO LIRC_DRIVER_NAME -+ ": auto-detected active %s receiver on GPIO pin %d\n", -+ sense ? "low" : "high", gpio_in_pin); -+ } else { -+ printk(KERN_INFO LIRC_DRIVER_NAME -+ ": manually using active %s receiver on GPIO pin %d\n", -+ sense ? "low" : "high", gpio_in_pin); -+ } -+ -+ return 0; -+ -+ exit_gpio_free_out_pin: -+ gpio_free(gpio_out_pin); -+ -+ exit_init_port: + return ret; +} + -+// called when the character device is opened -+static int set_use_inc(void *data) ++/* There is a potential problem with partial cache lines (pages?) ++** at the ends of the block when reading. If the CPU accessed anything in ++** the same line (page?) then it may have pulled old data into the cache, ++** obscuring the new data underneath. We can solve this by transferring the ++** partial cache lines separately, and allowing the ARM to copy into the ++** cached area. ++ ++** N.B. This implementation plays slightly fast and loose with the Linux ++** driver programming rules, e.g. its use of __virt_to_bus instead of ++** dma_map_single, but it isn't a multi-platform driver and it benefits ++** from increased speed as a result. ++*/ ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist) +{ -+ int result; ++ PAGELIST_T *pagelist; ++ struct page **pages; ++ struct page *page; ++ unsigned long *addrs; ++ unsigned int num_pages, offset, i; ++ char *addr, *base_addr, *next_addr; ++ int run, addridx, actual_pages; ++ unsigned long *need_release; + -+ /* initialize timestamp */ -+ do_gettimeofday(&lasttv); ++ offset = (unsigned int)buf & (PAGE_SIZE - 1); ++ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; + -+ result = request_irq(irq_num, -+ (irq_handler_t) irq_handler, -+ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, -+ LIRC_DRIVER_NAME, (void*) 0); ++ *ppagelist = NULL; + -+ switch (result) { -+ case -EBUSY: -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": IRQ %d is busy\n", -+ irq_num); -+ return -EBUSY; -+ case -EINVAL: -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": Bad irq number or handler\n"); -+ return -EINVAL; -+ default: -+ dprintk("Interrupt %d obtained\n", -+ irq_num); -+ break; -+ }; ++ /* Allocate enough storage to hold the page pointers and the page ++ ** list ++ */ ++ pagelist = kmalloc(sizeof(PAGELIST_T) + ++ (num_pages * sizeof(unsigned long)) + ++ sizeof(unsigned long) + ++ (num_pages * sizeof(pages[0])), ++ GFP_KERNEL); + -+ /* initialize pulse/space widths */ -+ init_timing_params(duty_cycle, freq); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "create_pagelist - %x", (unsigned int)pagelist); ++ if (!pagelist) ++ return -ENOMEM; ++ ++ addrs = pagelist->addrs; ++ need_release = (unsigned long *)(addrs + num_pages); ++ pages = (struct page **)(addrs + num_pages + 1); ++ ++ if (is_vmalloc_addr(buf)) { ++ for (actual_pages = 0; actual_pages < num_pages; actual_pages++) { ++ pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE)); ++ } ++ *need_release = 0; /* do not try and release vmalloc pages */ ++ } else { ++ down_read(&task->mm->mmap_sem); ++ actual_pages = get_user_pages(task, task->mm, ++ (unsigned long)buf & ~(PAGE_SIZE - 1), ++ num_pages, ++ (type == PAGELIST_READ) /*Write */ , ++ 0 /*Force */ , ++ pages, ++ NULL /*vmas */); ++ up_read(&task->mm->mmap_sem); ++ ++ if (actual_pages != num_pages) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "create_pagelist - only %d/%d pages locked", ++ actual_pages, ++ num_pages); ++ ++ /* This is probably due to the process being killed */ ++ while (actual_pages > 0) ++ { ++ actual_pages--; ++ page_cache_release(pages[actual_pages]); ++ } ++ kfree(pagelist); ++ if (actual_pages == 0) ++ actual_pages = -ENOMEM; ++ return actual_pages; ++ } ++ *need_release = 1; /* release user pages */ ++ } ++ ++ pagelist->length = count; ++ pagelist->type = type; ++ pagelist->offset = offset; ++ ++ /* Group the pages into runs of contiguous pages */ ++ ++ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0])); ++ next_addr = base_addr + PAGE_SIZE; ++ addridx = 0; ++ run = 0; ++ ++ for (i = 1; i < num_pages; i++) { ++ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i])); ++ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) { ++ next_addr += PAGE_SIZE; ++ run++; ++ } else { ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ base_addr = addr; ++ next_addr = addr + PAGE_SIZE; ++ run = 0; ++ } ++ } ++ ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ ++ /* Partial cache lines (fragments) require special measures */ ++ if ((type == PAGELIST_READ) && ++ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) || ++ ((pagelist->offset + pagelist->length) & ++ (CACHE_LINE_SIZE - 1)))) { ++ FRAGMENTS_T *fragments; ++ ++ if (down_interruptible(&g_free_fragments_sema) != 0) { ++ kfree(pagelist); ++ return -EINTR; ++ } ++ ++ WARN_ON(g_free_fragments == NULL); ++ ++ down(&g_free_fragments_mutex); ++ fragments = (FRAGMENTS_T *) g_free_fragments; ++ WARN_ON(fragments == NULL); ++ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments; ++ up(&g_free_fragments_mutex); ++ pagelist->type = ++ PAGELIST_READ_WITH_FRAGMENTS + (fragments - ++ g_fragments_base); ++ } ++ ++ for (page = virt_to_page(pagelist); ++ page <= virt_to_page(addrs + num_pages - 1); page++) { ++ flush_dcache_page(page); ++ } ++ ++ *ppagelist = pagelist; + + return 0; +} + -+static void set_use_dec(void *data) ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual) +{ -+ /* GPIO Pin Falling/Rising Edge Detect Disable */ -+ irq_set_irq_type(irq_num, 0); -+ disable_irq(irq_num); ++ unsigned long *need_release; ++ struct page **pages; ++ unsigned int num_pages, i; + -+ free_irq(irq_num, (void *) 0); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "free_pagelist - %x, %d", (unsigned int)pagelist, actual); + -+ dprintk(KERN_INFO LIRC_DRIVER_NAME -+ ": freed IRQ %d\n", irq_num); -+} ++ num_pages = ++ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / ++ PAGE_SIZE; + -+static ssize_t lirc_write(struct file *file, const char *buf, -+ size_t n, loff_t *ppos) -+{ -+ int i, count; -+ unsigned long flags; -+ long delta = 0; -+ int *wbuf; ++ need_release = (unsigned long *)(pagelist->addrs + num_pages); ++ pages = (struct page **)(pagelist->addrs + num_pages + 1); + -+ count = n / sizeof(int); -+ if (n % sizeof(int) || count % 2 == 0) -+ return -EINVAL; -+ wbuf = memdup_user(buf, n); -+ if (IS_ERR(wbuf)) -+ return PTR_ERR(wbuf); -+ spin_lock_irqsave(&lock, flags); ++ /* Deal with any partial cache lines (fragments) */ ++ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { ++ FRAGMENTS_T *fragments = g_fragments_base + ++ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS); ++ int head_bytes, tail_bytes; ++ head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & ++ (CACHE_LINE_SIZE - 1); ++ tail_bytes = (pagelist->offset + actual) & ++ (CACHE_LINE_SIZE - 1); + -+ for (i = 0; i < count; i++) { -+ if (i%2) -+ send_space(wbuf[i] - delta); -+ else -+ delta = send_pulse(wbuf[i]); ++ if ((actual >= 0) && (head_bytes != 0)) { ++ if (head_bytes > actual) ++ head_bytes = actual; ++ ++ memcpy((char *)page_address(pages[0]) + ++ pagelist->offset, ++ fragments->headbuf, ++ head_bytes); ++ } ++ if ((actual >= 0) && (head_bytes < actual) && ++ (tail_bytes != 0)) { ++ memcpy((char *)page_address(pages[num_pages - 1]) + ++ ((pagelist->offset + actual) & ++ (PAGE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)), ++ fragments->tailbuf, tail_bytes); ++ } ++ ++ down(&g_free_fragments_mutex); ++ *(FRAGMENTS_T **) fragments = g_free_fragments; ++ g_free_fragments = fragments; ++ up(&g_free_fragments_mutex); ++ up(&g_free_fragments_sema); + } -+ gpiochip->set(gpiochip, gpio_out_pin, invert); + -+ spin_unlock_irqrestore(&lock, flags); -+ kfree(wbuf); -+ return n; ++ if (*need_release) { ++ for (i = 0; i < num_pages; i++) { ++ if (pagelist->type != PAGELIST_WRITE) ++ set_page_dirty(pages[i]); ++ ++ page_cache_release(pages[i]); ++ } ++ } ++ ++ kfree(pagelist); ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +new file mode 100644 +index 0000000..0ad9656 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -0,0 +1,2884 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vchiq_core.h" ++#include "vchiq_ioctl.h" ++#include "vchiq_arm.h" ++#include "vchiq_debugfs.h" ++#include "vchiq_killable.h" ++ ++#define DEVICE_NAME "vchiq" ++ ++/* Override the default prefix, which would be vchiq_arm (from the filename) */ ++#undef MODULE_PARAM_PREFIX ++#define MODULE_PARAM_PREFIX DEVICE_NAME "." ++ ++#define VCHIQ_MINOR 0 ++ ++/* Some per-instance constants */ ++#define MAX_COMPLETIONS 16 ++#define MAX_SERVICES 64 ++#define MAX_ELEMENTS 8 ++#define MSG_QUEUE_SIZE 64 ++ ++#define KEEPALIVE_VER 1 ++#define KEEPALIVE_VER_MIN KEEPALIVE_VER ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_susp_log_level = VCHIQ_LOG_ERROR; ++ ++#define SUSPEND_TIMER_TIMEOUT_MS 100 ++#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 ++ ++#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ ++static const char *const suspend_state_names[] = { ++ "VC_SUSPEND_FORCE_CANCELED", ++ "VC_SUSPEND_REJECTED", ++ "VC_SUSPEND_FAILED", ++ "VC_SUSPEND_IDLE", ++ "VC_SUSPEND_REQUESTED", ++ "VC_SUSPEND_IN_PROGRESS", ++ "VC_SUSPEND_SUSPENDED" ++}; ++#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ ++static const char *const resume_state_names[] = { ++ "VC_RESUME_FAILED", ++ "VC_RESUME_IDLE", ++ "VC_RESUME_REQUESTED", ++ "VC_RESUME_IN_PROGRESS", ++ "VC_RESUME_RESUMED" ++}; ++/* The number of times we allow force suspend to timeout before actually ++** _forcing_ suspend. This is to cater for SW which fails to release vchiq ++** correctly - we don't want to prevent ARM suspend indefinitely in this case. ++*/ ++#define FORCE_SUSPEND_FAIL_MAX 8 ++ ++/* The time in ms allowed for videocore to go idle when force suspend has been ++ * requested */ ++#define FORCE_SUSPEND_TIMEOUT_MS 200 ++ ++ ++static void suspend_timer_callback(unsigned long context); ++ ++ ++typedef struct user_service_struct { ++ VCHIQ_SERVICE_T *service; ++ void *userdata; ++ VCHIQ_INSTANCE_T instance; ++ char is_vchi; ++ char dequeue_pending; ++ char close_pending; ++ int message_available_pos; ++ int msg_insert; ++ int msg_remove; ++ struct semaphore insert_event; ++ struct semaphore remove_event; ++ struct semaphore close_event; ++ VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; ++} USER_SERVICE_T; ++ ++struct bulk_waiter_node { ++ struct bulk_waiter bulk_waiter; ++ int pid; ++ struct list_head list; ++}; ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; ++ int completion_insert; ++ int completion_remove; ++ struct semaphore insert_event; ++ struct semaphore remove_event; ++ struct mutex completion_mutex; ++ ++ int connected; ++ int closing; ++ int pid; ++ int mark; ++ int use_close_delivered; ++ int trace; ++ ++ struct list_head bulk_waiter_list; ++ struct mutex bulk_waiter_list_mutex; ++ ++ VCHIQ_DEBUGFS_NODE_T debugfs_node; ++}; ++ ++typedef struct dump_context_struct { ++ char __user *buf; ++ size_t actual; ++ size_t space; ++ loff_t offset; ++} DUMP_CONTEXT_T; ++ ++static struct cdev vchiq_cdev; ++static dev_t vchiq_devid; ++static VCHIQ_STATE_T g_state; ++static struct class *vchiq_class; ++static struct device *vchiq_dev; ++static DEFINE_SPINLOCK(msg_queue_spinlock); ++ ++static const char *const ioctl_names[] = { ++ "CONNECT", ++ "SHUTDOWN", ++ "CREATE_SERVICE", ++ "REMOVE_SERVICE", ++ "QUEUE_MESSAGE", ++ "QUEUE_BULK_TRANSMIT", ++ "QUEUE_BULK_RECEIVE", ++ "AWAIT_COMPLETION", ++ "DEQUEUE_MESSAGE", ++ "GET_CLIENT_ID", ++ "GET_CONFIG", ++ "CLOSE_SERVICE", ++ "USE_SERVICE", ++ "RELEASE_SERVICE", ++ "SET_SERVICE_OPTION", ++ "DUMP_PHYS_MEM", ++ "LIB_VERSION", ++ "CLOSE_DELIVERED" ++}; ++ ++vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == ++ (VCHIQ_IOC_MAX + 1)); ++ ++static void ++dump_phys_mem(void *virt_addr, uint32_t num_bytes); ++ ++/**************************************************************************** ++* ++* add_completion ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service, ++ void *bulk_userdata) ++{ ++ VCHIQ_COMPLETION_DATA_T *completion; ++ DEBUG_INITIALISE(g_state.local) ++ ++ while (instance->completion_insert == ++ (instance->completion_remove + MAX_COMPLETIONS)) { ++ /* Out of space - wait for the client */ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "add_completion - completion queue full"); ++ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); ++ if (down_interruptible(&instance->remove_event) != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback interrupted"); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback closing"); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ } ++ ++ completion = ++ &instance->completions[instance->completion_insert & ++ (MAX_COMPLETIONS - 1)]; ++ ++ completion->header = header; ++ completion->reason = reason; ++ /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ ++ completion->service_userdata = user_service->service; ++ completion->bulk_userdata = bulk_userdata; ++ ++ if (reason == VCHIQ_SERVICE_CLOSED) { ++ /* Take an extra reference, to be held until ++ this CLOSED notification is delivered. */ ++ lock_service(user_service->service); ++ if (instance->use_close_delivered) ++ user_service->close_pending = 1; ++ } ++ ++ /* A write barrier is needed here to ensure that the entire completion ++ record is written out before the insert point. */ ++ wmb(); ++ ++ if (reason == VCHIQ_MESSAGE_AVAILABLE) ++ user_service->message_available_pos = ++ instance->completion_insert; ++ instance->completion_insert++; ++ ++ up(&instance->insert_event); ++ ++ return VCHIQ_SUCCESS; +} + -+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) ++/**************************************************************************** ++* ++* service_callback ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) +{ -+ int result; -+ __u32 value; ++ /* How do we ensure the callback goes to the right client? ++ ** The service_user data points to a USER_SERVICE_T record containing ++ ** the original callback and the user state structure, which contains a ++ ** circular buffer for completion records. ++ */ ++ USER_SERVICE_T *user_service; ++ VCHIQ_SERVICE_T *service; ++ VCHIQ_INSTANCE_T instance; ++ DEBUG_INITIALISE(g_state.local) ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ ++ service = handle_to_service(handle); ++ BUG_ON(!service); ++ user_service = (USER_SERVICE_T *)service->base.userdata; ++ instance = user_service->instance; ++ ++ if (!instance || instance->closing) ++ return VCHIQ_SUCCESS; ++ ++ vchiq_log_trace(vchiq_arm_log_level, ++ "service_callback - service %lx(%d,%p), reason %d, header %lx, " ++ "instance %lx, bulk_userdata %lx", ++ (unsigned long)user_service, ++ service->localport, user_service->userdata, ++ reason, (unsigned long)header, ++ (unsigned long)instance, (unsigned long)bulk_userdata); ++ ++ if (header && user_service->is_vchi) { ++ spin_lock(&msg_queue_spinlock); ++ while (user_service->msg_insert == ++ (user_service->msg_remove + MSG_QUEUE_SIZE)) { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "service_callback - msg queue full"); ++ /* If there is no MESSAGE_AVAILABLE in the completion ++ ** queue, add one ++ */ ++ if ((user_service->message_available_pos - ++ instance->completion_remove) < 0) { ++ VCHIQ_STATUS_T status; ++ vchiq_log_info(vchiq_arm_log_level, ++ "Inserting extra MESSAGE_AVAILABLE"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ status = add_completion(instance, reason, ++ NULL, user_service, bulk_userdata); ++ if (status != VCHIQ_SUCCESS) { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return status; ++ } ++ } ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ if (down_interruptible(&user_service->remove_event) ++ != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback interrupted"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback closing"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ spin_lock(&msg_queue_spinlock); ++ } ++ ++ user_service->msg_queue[user_service->msg_insert & ++ (MSG_QUEUE_SIZE - 1)] = header; ++ user_service->msg_insert++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ up(&user_service->insert_event); ++ ++ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if ++ ** there is a MESSAGE_AVAILABLE in the completion queue then ++ ** bypass the completion queue. ++ */ ++ if (((user_service->message_available_pos - ++ instance->completion_remove) >= 0) || ++ user_service->dequeue_pending) { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ user_service->dequeue_pending = 0; ++ return VCHIQ_SUCCESS; ++ } ++ ++ header = NULL; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ ++ return add_completion(instance, reason, header, user_service, ++ bulk_userdata); ++} ++ ++/**************************************************************************** ++* ++* user_service_free ++* ++***************************************************************************/ ++static void ++user_service_free(void *userdata) ++{ ++ kfree(userdata); ++} ++ ++/**************************************************************************** ++* ++* close_delivered ++* ++***************************************************************************/ ++static void close_delivered(USER_SERVICE_T *user_service) ++{ ++ vchiq_log_info(vchiq_arm_log_level, ++ "close_delivered(handle=%x)", ++ user_service->service->handle); ++ ++ if (user_service->close_pending) { ++ /* Allow the underlying service to be culled */ ++ unlock_service(user_service->service); ++ ++ /* Wake the user-thread blocked in close_ or remove_service */ ++ up(&user_service->close_event); ++ ++ user_service->close_pending = 0; ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_ioctl ++* ++***************************************************************************/ ++static long ++vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ VCHIQ_SERVICE_T *service = NULL; ++ long ret = 0; ++ int i, rc; ++ DEBUG_INITIALISE(g_state.local) ++ ++ vchiq_log_trace(vchiq_arm_log_level, ++ "vchiq_ioctl - instance %x, cmd %s, arg %lx", ++ (unsigned int)instance, ++ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? ++ ioctl_names[_IOC_NR(cmd)] : "", arg); + + switch (cmd) { -+ case LIRC_GET_SEND_MODE: -+ return -ENOIOCTLCMD; ++ case VCHIQ_IOC_SHUTDOWN: ++ if (!instance->connected) ++ break; ++ ++ /* Remove all services */ ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ status = vchiq_remove_service(service->handle); ++ unlock_service(service); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ service = NULL; ++ ++ if (status == VCHIQ_SUCCESS) { ++ /* Wake the completion thread and ask it to exit */ ++ instance->closing = 1; ++ up(&instance->insert_event); ++ } ++ + break; + -+ case LIRC_SET_SEND_MODE: -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ /* only LIRC_MODE_PULSE supported */ -+ if (value != LIRC_MODE_PULSE) -+ return -ENOSYS; ++ case VCHIQ_IOC_CONNECT: ++ if (instance->connected) { ++ ret = -EINVAL; ++ break; ++ } ++ rc = mutex_lock_interruptible(&instance->state->mutex); ++ if (rc != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq: connect: could not lock mutex for " ++ "state %d: %d", ++ instance->state->id, rc); ++ ret = -EINTR; ++ break; ++ } ++ status = vchiq_connect_internal(instance->state, instance); ++ mutex_unlock(&instance->state->mutex); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ else ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq: could not connect: %d", status); + break; + -+ case LIRC_GET_LENGTH: -+ return -ENOSYS; -+ break; -+ -+ case LIRC_SET_SEND_DUTY_CYCLE: -+ dprintk("SET_SEND_DUTY_CYCLE\n"); -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ if (value <= 0 || value > 100) -+ return -EINVAL; -+ return init_timing_params(value, freq); -+ break; -+ -+ case LIRC_SET_SEND_CARRIER: -+ dprintk("SET_SEND_CARRIER\n"); -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ if (value > 500000 || value < 20000) -+ return -EINVAL; -+ return init_timing_params(duty_cycle, value); -+ break; -+ -+ default: -+ return lirc_dev_fop_ioctl(filep, cmd, arg); -+ } -+ return 0; -+} -+ -+static const struct file_operations lirc_fops = { -+ .owner = THIS_MODULE, -+ .write = lirc_write, -+ .unlocked_ioctl = lirc_ioctl, -+ .read = lirc_dev_fop_read, -+ .poll = lirc_dev_fop_poll, -+ .open = lirc_dev_fop_open, -+ .release = lirc_dev_fop_close, -+ .llseek = no_llseek, -+}; -+ -+static struct lirc_driver driver = { -+ .name = LIRC_DRIVER_NAME, -+ .minor = -1, -+ .code_length = 1, -+ .sample_rate = 0, -+ .data = NULL, -+ .add_to_buf = NULL, -+ .rbuf = &rbuf, -+ .set_use_inc = set_use_inc, -+ .set_use_dec = set_use_dec, -+ .fops = &lirc_fops, -+ .dev = NULL, -+ .owner = THIS_MODULE, -+}; -+ -+static struct platform_driver lirc_rpi_driver = { -+ .driver = { -+ .name = LIRC_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init lirc_rpi_init(void) -+{ -+ int result; -+ -+ /* Init read buffer. */ -+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); -+ if (result < 0) -+ return -ENOMEM; -+ -+ result = platform_driver_register(&lirc_rpi_driver); -+ if (result) { -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": lirc register returned %d\n", result); -+ goto exit_buffer_free; -+ } -+ -+ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); -+ if (!lirc_rpi_dev) { -+ result = -ENOMEM; -+ goto exit_driver_unregister; -+ } -+ -+ result = platform_device_add(lirc_rpi_dev); -+ if (result) -+ goto exit_device_put; -+ -+ return 0; -+ -+ exit_device_put: -+ platform_device_put(lirc_rpi_dev); -+ -+ exit_driver_unregister: -+ platform_driver_unregister(&lirc_rpi_driver); -+ -+ exit_buffer_free: -+ lirc_buffer_free(&rbuf); -+ -+ return result; -+} -+ -+static void lirc_rpi_exit(void) -+{ -+ if (!lirc_rpi_dev->dev.of_node) -+ platform_device_unregister(lirc_rpi_dev); -+ platform_driver_unregister(&lirc_rpi_driver); -+ lirc_buffer_free(&rbuf); -+} -+ -+static int __init lirc_rpi_init_module(void) -+{ -+ int result; -+ -+ result = lirc_rpi_init(); -+ if (result) -+ return result; -+ -+ if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) { -+ result = -EINVAL; -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": invalid GPIO pin(s) specified!\n"); -+ goto exit_rpi; -+ } -+ -+ result = init_port(); -+ if (result < 0) -+ goto exit_rpi; -+ -+ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | -+ LIRC_CAN_SET_SEND_CARRIER | -+ LIRC_CAN_SEND_PULSE | -+ LIRC_CAN_REC_MODE2; -+ -+ driver.dev = &lirc_rpi_dev->dev; -+ driver.minor = lirc_register_driver(&driver); -+ -+ if (driver.minor < 0) { -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": device registration failed with %d\n", result); -+ result = -EIO; -+ goto exit_rpi; -+ } -+ -+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); -+ -+ return 0; -+ -+ exit_rpi: -+ lirc_rpi_exit(); -+ -+ return result; -+} -+ -+static void __exit lirc_rpi_exit_module(void) -+{ -+ lirc_unregister_driver(driver.minor); -+ -+ gpio_free(gpio_out_pin); -+ gpio_free(gpio_in_pin); -+ -+ lirc_rpi_exit(); -+ -+ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); -+} -+ -+module_init(lirc_rpi_init_module); -+module_exit(lirc_rpi_exit_module); -+ -+MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); -+MODULE_AUTHOR("Aron Robert Szabo "); -+MODULE_AUTHOR("Michael Bishop "); -+MODULE_LICENSE("GPL"); -+ -+module_param(gpio_out_pin, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" -+ " processor. (default 17"); -+ -+module_param(gpio_in_pin, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." -+ " (default 18"); -+ -+module_param(gpio_in_pull, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." -+ " (0 = off, 1 = up, 2 = down, default down)"); -+ -+module_param(sense, int, S_IRUGO); -+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" -+ " (0 = active high, 1 = active low )"); -+ -+module_param(softcarrier, bool, S_IRUGO); -+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); -+ -+module_param(invert, bool, S_IRUGO); -+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); -+ -+module_param(debug, bool, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(debug, "Enable debugging messages"); - -From c66c4562de98ab99b0bd25fcd238104ce8f4ebbe Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:49:20 +0100 -Subject: [PATCH 017/172] Add cpufreq driver - ---- - arch/arm/Kconfig | 1 + - drivers/cpufreq/Kconfig.arm | 8 ++ - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/bcm2835-cpufreq.c | 224 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 234 insertions(+) - create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 1dc698c..fdfb230 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -376,6 +376,7 @@ config ARCH_BCM2708 - select NEED_MACH_GPIO_H - select NEED_MACH_MEMORY_H - select CLKDEV_LOOKUP -+ select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select ARM_ERRATA_411920 - select MACH_BCM2708 -diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm -index 0f9a2c3..c71b6e1 100644 ---- a/drivers/cpufreq/Kconfig.arm -+++ b/drivers/cpufreq/Kconfig.arm -@@ -241,6 +241,14 @@ config ARM_SPEAR_CPUFREQ - help - This adds the CPUFreq driver support for SPEAr SOCs. - -+config ARM_BCM2835_CPUFREQ -+ bool "BCM2835 Driver" -+ default y -+ help -+ This adds the CPUFreq driver for BCM2835 -+ -+ If in doubt, say N. -+ - config ARM_TEGRA_CPUFREQ - bool "TEGRA CPUFreq support" - depends on ARCH_TEGRA -diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile -index b3ca7b0..2824b68 100644 ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -74,6 +74,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o - obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o - obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o - obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o - obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o - obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o - -diff --git a/drivers/cpufreq/bcm2835-cpufreq.c b/drivers/cpufreq/bcm2835-cpufreq.c -new file mode 100755 -index 0000000..447ca09 ---- /dev/null -+++ b/drivers/cpufreq/bcm2835-cpufreq.c -@@ -0,0 +1,224 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+/***************************************************************************** -+* FILENAME: bcm2835-cpufreq.h -+* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM -+* processor. Messages are sent to Videocore either setting or requesting the -+* frequency of the ARM in order to match an appropiate frequency to the current -+* usage of the processor. The policy which selects the frequency to use is -+* defined in the kernel .config file, but can be changed during runtime. -+*****************************************************************************/ -+ -+/* ---------- INCLUDES ---------- */ -+#include -+#include -+#include -+#include -+#include -+ -+/* ---------- DEFINES ---------- */ -+/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */ -+#define MODULE_NAME "bcm2835-cpufreq" -+ -+#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */ -+ -+/* debug printk macros */ -+#ifdef CPUFREQ_DEBUG_ENABLE -+#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__) -+ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the message id */ -+ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */ -+ uint32_t data_size; /* amount of data being sent or received */ -+ uint32_t dev_id; /* the ID of the clock/voltage to get or set */ -+ uint32_t val; /* the value (e.g. rate (in Hz)) to set */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+/* ---------- GLOBALS ---------- */ -+static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */ -+ -+static struct cpufreq_frequency_table bcm2835_freq_table[] = { -+ {0, 0, 0}, -+ {0, 0, 0}, -+ {0, 0, CPUFREQ_TABLE_END}, -+}; -+ -+/* -+ =============================================== -+ clk_rate either gets or sets the clock rates. -+ =============================================== -+*/ -+static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) -+{ -+ int s, actual_rate=0; -+ struct vc_msg msg; -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ msg.msg_size = sizeof msg; -+ -+ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE; -+ msg.tag.buffer_size = 8; -+ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */ -+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; -+ msg.tag.val = arm_rate * 1000; -+ -+ /* send the message */ -+ s = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in KHz */ -+ if (s == 0 && (msg.request_code & 0x80000000)) -+ actual_rate = msg.tag.val/1000; -+ -+ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate); -+ return actual_rate; -+} -+ -+static uint32_t bcm2835_cpufreq_get_clock(int tag) -+{ -+ int s; -+ int arm_rate = 0; -+ struct vc_msg msg; -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ msg.msg_size = sizeof msg; -+ msg.tag.tag_id = tag; -+ msg.tag.buffer_size = 8; -+ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */ -+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; -+ -+ /* send the message */ -+ s = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in KHz */ -+ if (s == 0 && (msg.request_code & 0x80000000)) -+ arm_rate = msg.tag.val/1000; -+ -+ print_debug("%s frequency = %d\n", -+ tag == VCMSG_GET_CLOCK_RATE ? "Current": -+ tag == VCMSG_GET_MIN_CLOCK ? "Min": -+ tag == VCMSG_GET_MAX_CLOCK ? "Max": -+ "Unexpected", arm_rate); -+ -+ return arm_rate; -+} -+ -+/* -+ ==================================================== -+ Module Initialisation registers the cpufreq driver -+ ==================================================== -+*/ -+static int __init bcm2835_cpufreq_module_init(void) -+{ -+ print_debug("IN\n"); -+ return cpufreq_register_driver(&bcm2835_cpufreq_driver); -+} -+ -+/* -+ ============= -+ Module exit -+ ============= -+*/ -+static void __exit bcm2835_cpufreq_module_exit(void) -+{ -+ print_debug("IN\n"); -+ cpufreq_unregister_driver(&bcm2835_cpufreq_driver); -+ return; -+} -+ -+/* -+ ============================================================== -+ Initialisation function sets up the CPU policy for first use -+ ============================================================== -+*/ -+static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy) -+{ -+ /* measured value of how long it takes to change frequency */ -+ const unsigned int transition_latency = 355000; /* ns */ -+ -+ /* now find out what the maximum and minimum frequencies are */ -+ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK); -+ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK); -+ -+ print_info("min=%d max=%d\n", bcm2835_freq_table[0].frequency, bcm2835_freq_table[1].frequency); -+ return cpufreq_generic_init(policy, bcm2835_freq_table, transition_latency); -+} -+ -+/* -+ ===================================================================== -+ Target index function chooses the requested frequency from the table -+ ===================================================================== -+*/ -+ -+static int bcm2835_cpufreq_driver_target_index(struct cpufreq_policy *policy, unsigned int state) -+{ -+ unsigned int target_freq = bcm2835_freq_table[state].frequency; -+ unsigned int cur = bcm2835_cpufreq_set_clock(policy->cur, target_freq); -+ -+ if (!cur) -+ { -+ print_err("Error occurred setting a new frequency (%d)\n", target_freq); -+ return -EINVAL; -+ } -+ print_debug("%s: %i: freq %d->%d\n", policy->governor->name, state, policy->cur, cur); -+ return 0; -+} -+ -+/* -+ ====================================================== -+ Get function returns the current frequency from table -+ ====================================================== -+*/ -+ -+static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu) -+{ -+ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE); -+ print_debug("%d: freq=%d\n", cpu, actual_rate); -+ return actual_rate <= bcm2835_freq_table[0].frequency ? bcm2835_freq_table[0].frequency : bcm2835_freq_table[1].frequency; -+} -+ -+/* the CPUFreq driver */ -+static struct cpufreq_driver bcm2835_cpufreq_driver = { -+ .name = "BCM2835 CPUFreq", -+ .init = bcm2835_cpufreq_driver_init, -+ .verify = cpufreq_generic_frequency_table_verify, -+ .target_index = bcm2835_cpufreq_driver_target_index, -+ .get = bcm2835_cpufreq_driver_get, -+ .attr = cpufreq_generic_attr, -+}; -+ -+MODULE_AUTHOR("Dorian Peake and Dom Cobley"); -+MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip"); -+MODULE_LICENSE("GPL"); -+ -+module_init(bcm2835_cpufreq_module_init); -+module_exit(bcm2835_cpufreq_module_exit); - -From b5de470ee4adcb247acbb7a8515a73db3a9494d7 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 19:24:24 +0000 -Subject: [PATCH 018/172] Added hwmon/thermal driver for reporting core - temperature. Thanks Dorian - ---- - arch/arm/mach-bcm2708/bcm2708.c | 11 ++ - drivers/hwmon/Kconfig | 10 ++ - drivers/hwmon/Makefile | 1 + - drivers/hwmon/bcm2835-hwmon.c | 219 ++++++++++++++++++++++++++++++++++++++ - drivers/thermal/Kconfig | 6 ++ - drivers/thermal/Makefile | 1 + - drivers/thermal/bcm2835-thermal.c | 184 ++++++++++++++++++++++++++++++++ - 7 files changed, 432 insertions(+) - create mode 100644 drivers/hwmon/bcm2835-hwmon.c - create mode 100644 drivers/thermal/bcm2835-thermal.c - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index f3dccae..af57d11 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -455,6 +455,14 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+static struct platform_device bcm2835_hwmon_device = { -+ .name = "bcm2835_hwmon", -+}; -+ -+static struct platform_device bcm2835_thermal_device = { -+ .name = "bcm2835_thermal", -+}; -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -563,6 +571,9 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index a7de26d..42c82b4 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1702,6 +1702,16 @@ config SENSORS_ULTRA45 - This driver provides support for the Ultra45 workstation environmental - sensors. - -+config SENSORS_BCM2835 -+ depends on THERMAL_BCM2835=n -+ tristate "Broadcom BCM2835 HWMON Driver" -+ help -+ If you say yes here you get support for the hardware -+ monitoring features of the BCM2835 Chip -+ -+ This driver can also be built as a module. If so, the module -+ will be called bcm2835-hwmon. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 6c94147..e7c60cb 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o - obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o - obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o - obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o -+obj-$(CONFIG_SENSORS_BCM2835) += bcm2835-hwmon.o - - obj-$(CONFIG_PMBUS) += pmbus/ - -diff --git a/drivers/hwmon/bcm2835-hwmon.c b/drivers/hwmon/bcm2835-hwmon.c -new file mode 100644 -index 0000000..5bbed45 ---- /dev/null -+++ b/drivers/hwmon/bcm2835-hwmon.c -@@ -0,0 +1,219 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MODULE_NAME "bcm2835_hwmon" -+ -+/*#define HWMON_DEBUG_ENABLE*/ -+ -+#ifdef HWMON_DEBUG_ENABLE -+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__) -+ -+#define VC_TAG_GET_TEMP 0x00030006 -+#define VC_TAG_GET_MAX_TEMP 0x0003000A -+ -+/* --- STRUCTS --- */ -+struct bcm2835_hwmon_data { -+ struct device *hwmon_dev; -+}; -+ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the tag ID for the temperature */ -+ uint32_t buffer_size; /* size of the buffer (should be 8) */ -+ uint32_t request_code; /* identifies message as a request (should be 0) */ -+ uint32_t id; /* extra ID field (should be 0) */ -+ uint32_t val; /* returned value of the temperature */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+typedef enum { -+ TEMP, -+ MAX_TEMP, -+} temp_type; -+ -+/* --- PROTOTYPES --- */ -+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf); -+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf); -+ -+/* --- GLOBALS --- */ -+ -+static struct bcm2835_hwmon_data *bcm2835_data; -+static struct platform_driver bcm2835_hwmon_driver; -+ -+static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0); -+static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP); -+static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP); -+ -+static struct attribute* bcm2835_attributes[] = { -+ &sensor_dev_attr_name.dev_attr.attr, -+ &sensor_dev_attr_temp1_input.dev_attr.attr, -+ &sensor_dev_attr_temp1_max.dev_attr.attr, -+ NULL, -+}; -+ -+static struct attribute_group bcm2835_attr_group = { -+ .attrs = bcm2835_attributes, -+}; -+ -+/* --- FUNCTIONS --- */ -+ -+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ return sprintf(buf,"bcm2835_hwmon\n"); -+} -+ -+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct vc_msg msg; -+ int result; -+ uint temp = 0; -+ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index; -+ -+ print_debug("IN"); -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ /* determine the message type */ -+ if(index == TEMP) -+ msg.tag.tag_id = VC_TAG_GET_TEMP; -+ else if (index == MAX_TEMP) -+ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP; -+ else -+ { -+ print_debug("Unknown temperature message!"); -+ return -EINVAL; -+ } -+ -+ msg.msg_size = sizeof msg; -+ msg.tag.buffer_size = 8; -+ -+ /* send the message */ -+ result = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in milli degrees C */ -+ if (result == 0 && (msg.request_code & 0x80000000)) -+ temp = (uint)msg.tag.val; -+ #ifdef HWMON_DEBUG_ENABLE -+ else -+ print_debug("Failed to get temperature!"); -+ #endif -+ print_debug("Got temperature as %u",temp); -+ print_debug("OUT"); -+ return sprintf(buf, "%u\n", temp); -+} -+ -+ -+static int bcm2835_hwmon_probe(struct platform_device *pdev) -+{ -+ int err; -+ -+ print_debug("IN"); -+ print_debug("HWMON Driver has been probed!"); -+ -+ /* check that the device isn't null!*/ -+ if(pdev == NULL) -+ { -+ print_debug("Platform device is empty!"); -+ return -ENODEV; -+ } -+ -+ /* allocate memory for neccessary data */ -+ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL); -+ if(!bcm2835_data) -+ { -+ print_debug("Unable to allocate memory for hwmon data!"); -+ err = -ENOMEM; -+ goto kzalloc_error; -+ } -+ -+ /* create the sysfs files */ -+ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group)) -+ { -+ print_debug("Unable to create sysfs files!"); -+ err = -EFAULT; -+ goto sysfs_error; -+ } -+ -+ /* register the hwmon device */ -+ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev); -+ if (IS_ERR(bcm2835_data->hwmon_dev)) -+ { -+ err = PTR_ERR(bcm2835_data->hwmon_dev); -+ goto hwmon_error; -+ } -+ print_debug("OUT"); -+ return 0; -+ -+ /* error goto's */ -+ hwmon_error: -+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); -+ -+ sysfs_error: -+ kfree(bcm2835_data); -+ -+ kzalloc_error: -+ -+ return err; -+ -+} -+ -+static int bcm2835_hwmon_remove(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ hwmon_device_unregister(bcm2835_data->hwmon_dev); -+ -+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); -+ print_debug("OUT"); -+ return 0; -+} -+ -+/* Hwmon Driver */ -+static struct platform_driver bcm2835_hwmon_driver = { -+ .probe = bcm2835_hwmon_probe, -+ .remove = bcm2835_hwmon_remove, -+ .driver = { -+ .name = "bcm2835_hwmon", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Dorian Peake"); -+MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip"); -+ -+module_platform_driver(bcm2835_hwmon_driver); -diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig -index af40db0..4584b1e 100644 ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -238,6 +238,12 @@ config INTEL_POWERCLAMP - enforce idle time which results in more package C-state residency. The - user interface is exposed via generic thermal framework. - -+config THERMAL_BCM2835 -+ tristate "BCM2835 Thermal Driver" -+ help -+ This will enable temperature monitoring for the Broadcom BCM2835 -+ chip. If built as a module, it will be called 'bcm2835-thermal'. -+ - config X86_PKG_TEMP_THERMAL - tristate "X86 package temperature thermal driver" - depends on X86_THERMAL_VECTOR -diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile -index fa0dc48..453d4d9 100644 ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -33,6 +33,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o - obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o - obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o - obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o -+obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o - obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o - obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o - obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ -diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c -new file mode 100644 -index 0000000..85fceb5 ---- /dev/null -+++ b/drivers/thermal/bcm2835-thermal.c -@@ -0,0 +1,184 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* --- DEFINITIONS --- */ -+#define MODULE_NAME "bcm2835_thermal" -+ -+/*#define THERMAL_DEBUG_ENABLE*/ -+ -+#ifdef THERMAL_DEBUG_ENABLE -+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+ -+#define VC_TAG_GET_TEMP 0x00030006 -+#define VC_TAG_GET_MAX_TEMP 0x0003000A -+ -+typedef enum { -+ TEMP, -+ MAX_TEMP, -+} temp_type; -+ -+/* --- STRUCTS --- */ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the tag ID for the temperature */ -+ uint32_t buffer_size; /* size of the buffer (should be 8) */ -+ uint32_t request_code; /* identifies message as a request (should be 0) */ -+ uint32_t id; /* extra ID field (should be 0) */ -+ uint32_t val; /* returned value of the temperature */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+struct bcm2835_thermal_data { -+ struct thermal_zone_device *thermal_dev; -+ struct vc_msg msg; -+}; -+ -+/* --- GLOBALS --- */ -+static struct bcm2835_thermal_data bcm2835_data; -+ -+/* Thermal Device Operations */ -+static struct thermal_zone_device_ops ops; -+ -+/* --- FUNCTIONS --- */ -+ -+static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id) -+{ -+ int result = -1, retry = 3; -+ print_debug("IN"); -+ -+ *temp = 0; -+ while (result != 0 && retry-- > 0) { -+ /* wipe all previous message data */ -+ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg); -+ -+ /* prepare message */ -+ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg; -+ bcm2835_data.msg.tag.buffer_size = 8; -+ bcm2835_data.msg.tag.tag_id = tag_id; -+ -+ /* send the message */ -+ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg); -+ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code); -+ if (!(bcm2835_data.msg.request_code & 0x80000000)) -+ result = -1; -+ } -+ -+ /* check if it was all ok and return the rate in milli degrees C */ -+ if (result == 0) -+ *temp = (uint)bcm2835_data.msg.tag.val; -+ else -+ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result); -+ print_debug("OUT"); -+ return result; -+} -+ -+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp) -+{ -+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP); -+} -+ -+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp) -+{ -+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP); -+} -+ -+static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type) -+{ -+ *trip_type = THERMAL_TRIP_HOT; -+ return 0; -+} -+ -+ -+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode) -+{ -+ *dev_mode = THERMAL_DEVICE_ENABLED; -+ return 0; -+} -+ -+ -+static int bcm2835_thermal_probe(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ print_debug("THERMAL Driver has been probed!"); -+ -+ /* check that the device isn't null!*/ -+ if(pdev == NULL) -+ { -+ print_debug("Platform device is empty!"); -+ return -ENODEV; -+ } -+ -+ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0))) -+ { -+ print_debug("Unable to register the thermal device!"); -+ return -EFAULT; -+ } -+ return 0; -+} -+ -+ -+static int bcm2835_thermal_remove(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ -+ thermal_zone_device_unregister(bcm2835_data.thermal_dev); -+ -+ print_debug("OUT"); -+ -+ return 0; -+} -+ -+static struct thermal_zone_device_ops ops = { -+ .get_temp = bcm2835_get_temp, -+ .get_trip_temp = bcm2835_get_max_temp, -+ .get_trip_type = bcm2835_get_trip_type, -+ .get_mode = bcm2835_get_mode, -+}; -+ -+/* Thermal Driver */ -+static struct platform_driver bcm2835_thermal_driver = { -+ .probe = bcm2835_thermal_probe, -+ .remove = bcm2835_thermal_remove, -+ .driver = { -+ .name = "bcm2835_thermal", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Dorian Peake"); -+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); -+ -+module_platform_driver(bcm2835_thermal_driver); - -From 5af87652d480b6de0edca41bde86a469bf15ffc6 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 17:26:38 +0000 -Subject: [PATCH 019/172] Allow mac address to be set in smsc95xx - -Signed-off-by: popcornmix ---- - drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 56 insertions(+) - -diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c -index 26423ad..e29a323 100644 ---- a/drivers/net/usb/smsc95xx.c -+++ b/drivers/net/usb/smsc95xx.c -@@ -59,6 +59,7 @@ - #define SUSPEND_SUSPEND3 (0x08) - #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ - SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) -+#define MAC_ADDR_LEN (6) - - struct smsc95xx_priv { - u32 mac_cr; -@@ -74,6 +75,10 @@ static bool turbo_mode = true; - module_param(turbo_mode, bool, 0644); - MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); - -+static char *macaddr = ":"; -+module_param(macaddr, charp, 0); -+MODULE_PARM_DESC(macaddr, "MAC address"); -+ - static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, - u32 *data, int in_pm) - { -@@ -763,8 +768,59 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) - return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); - } - -+/* Check the macaddr module parameter for a MAC address */ -+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac) -+{ -+ int i, j, got_num, num; -+ u8 mtbl[MAC_ADDR_LEN]; -+ -+ if (macaddr[0] == ':') -+ return 0; -+ -+ i = 0; -+ j = 0; -+ num = 0; -+ got_num = 0; -+ while (j < MAC_ADDR_LEN) { -+ if (macaddr[i] && macaddr[i] != ':') { -+ got_num++; -+ if ('0' <= macaddr[i] && macaddr[i] <= '9') -+ num = num * 16 + macaddr[i] - '0'; -+ else if ('A' <= macaddr[i] && macaddr[i] <= 'F') -+ num = num * 16 + 10 + macaddr[i] - 'A'; -+ else if ('a' <= macaddr[i] && macaddr[i] <= 'f') -+ num = num * 16 + 10 + macaddr[i] - 'a'; -+ else -+ break; -+ i++; -+ } else if (got_num == 2) { -+ mtbl[j++] = (u8) num; -+ num = 0; -+ got_num = 0; -+ i++; -+ } else { -+ break; -+ } -+ } -+ -+ if (j == MAC_ADDR_LEN) { -+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: " -+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2], -+ mtbl[3], mtbl[4], mtbl[5]); -+ for (i = 0; i < MAC_ADDR_LEN; i++) -+ dev_mac[i] = mtbl[i]; -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ - static void smsc95xx_init_mac_address(struct usbnet *dev) - { -+ /* Check module parameters */ -+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) -+ return; -+ - /* try reading mac address from EEPROM */ - if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, - dev->net->dev_addr) == 0) { - -From b8f0a117f160b4a8a9c41d3e61754dbe220a69dd Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 4 Nov 2013 18:56:10 +0000 -Subject: [PATCH 020/172] Add Chris Boot's i2c and spi drivers. - -i2c-bcm2708: fixed baudrate - -Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock). -In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits. -This resulted in incorrect setting of CDIV and higher baudrate than intended. -Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz -After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz -The correct baudrate is shown in the log after the cdiv > 0xffff correction. ---- - arch/arm/configs/bcmrpi_defconfig | 7 + - arch/arm/mach-bcm2708/Kconfig | 7 + - arch/arm/mach-bcm2708/bcm2708.c | 104 ++++++- - drivers/i2c/busses/Kconfig | 19 ++ - drivers/i2c/busses/Makefile | 1 + - drivers/i2c/busses/i2c-bcm2708.c | 420 +++++++++++++++++++++++++ - drivers/spi/Kconfig | 8 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-bcm2708.c | 626 ++++++++++++++++++++++++++++++++++++++ - 9 files changed, 1191 insertions(+), 2 deletions(-) - create mode 100644 drivers/i2c/busses/i2c-bcm2708.c - create mode 100644 drivers/spi/spi-bcm2708.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 63cb6a4..6d2eae1 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -195,6 +195,13 @@ CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y - # CONFIG_HW_RANDOM is not set - CONFIG_RAW_DRIVER=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2708=m -+CONFIG_SPI_SPIDEV=m -+CONFIG_GPIO_SYSFS=y - # CONFIG_HWMON is not set - CONFIG_WATCHDOG=y - CONFIG_FB=y -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index 9355841..e151ed4 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -31,4 +31,11 @@ config BCM2708_NOL2CACHE - help - Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. - -+config BCM2708_SPIDEV -+ bool "Bind spidev to SPI0 master" -+ depends on MACH_BCM2708 -+ depends on SPI -+ default y -+ help -+ Binds spidev driver to the SPI0 master - endmenu -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index af57d11..82f56fb 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -205,7 +206,6 @@ static struct clk osc_clk = { - - /* warning - the USB needs a clock > 34MHz */ - --#ifdef CONFIG_MMC_BCM2708 - static struct clk sdhost_clk = { - #ifdef CONFIG_ARCH_BCM2708_CHIPIT - .rate = 4000000, /* 4MHz */ -@@ -213,7 +213,6 @@ static struct clk sdhost_clk = { - .rate = 250000000, /* 250MHz */ - #endif - }; --#endif - - static struct clk_lookup lookups[] = { - { /* UART0 */ -@@ -223,6 +222,15 @@ static struct clk_lookup lookups[] = { - { /* USB */ - .dev_id = "bcm2708_usb", - .clk = &osc_clk, -+ }, { /* SPI */ -+ .dev_id = "bcm2708_spi.0", -+ .clk = &sdhost_clk, -+ }, { /* BSC0 */ -+ .dev_id = "bcm2708_i2c.0", -+ .clk = &sdhost_clk, -+ }, { /* BSC1 */ -+ .dev_id = "bcm2708_i2c.1", -+ .clk = &sdhost_clk, - } - }; - -@@ -455,6 +463,89 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+static struct resource bcm2708_spi_resources[] = { -+ { -+ .start = SPI0_BASE, -+ .end = SPI0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = IRQ_SPI, -+ .end = IRQ_SPI, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+ -+static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+static struct platform_device bcm2708_spi_device = { -+ .name = "bcm2708_spi", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), -+ .resource = bcm2708_spi_resources, -+ .dev = { -+ .dma_mask = &bcm2708_spi_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, -+}; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+static struct spi_board_info bcm2708_spi_devices[] = { -+#ifdef CONFIG_SPI_SPIDEV -+ { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 0, -+ .mode = SPI_MODE_0, -+ }, { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 1, -+ .mode = SPI_MODE_0, -+ } -+#endif -+}; -+#endif -+ -+static struct resource bcm2708_bsc0_resources[] = { -+ { -+ .start = BSC0_BASE, -+ .end = BSC0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc0_device = { -+ .name = "bcm2708_i2c", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), -+ .resource = bcm2708_bsc0_resources, -+}; -+ -+ -+static struct resource bcm2708_bsc1_resources[] = { -+ { -+ .start = BSC1_BASE, -+ .end = BSC1_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc1_device = { -+ .name = "bcm2708_i2c", -+ .id = 1, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), -+ .resource = bcm2708_bsc1_resources, -+}; -+ - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", - }; -@@ -571,6 +662,10 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2708_spi_device); -+ bcm_register_device(&bcm2708_bsc0_device); -+ bcm_register_device(&bcm2708_bsc1_device); -+ - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); - -@@ -580,6 +675,11 @@ void __init bcm2708_init(void) - } - system_rev = boardrev; - system_serial_low = serial; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); -+#endif - } - - static void timer_set_mode(enum clock_event_mode mode, -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index ab838d9..68362ab 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -372,6 +372,25 @@ config I2C_BCM2835 - This support is also available as a module. If so, the module - will be called i2c-bcm2835. - -+config I2C_BCM2708 -+ tristate "BCM2708 BSC" -+ depends on MACH_BCM2708 -+ help -+ Enabling this option will add BSC (Broadcom Serial Controller) -+ support for the BCM2708. BSC is a Broadcom proprietary bus compatible -+ with I2C/TWI/SMBus. -+ -+config I2C_BCM2708_BAUDRATE -+ prompt "BCM2708 I2C baudrate" -+ depends on I2C_BCM2708 -+ int -+ default 100000 -+ help -+ Set the I2C baudrate. This will alter the default value. A -+ different baudrate can be set by using a module parameter as well. If -+ no parameter is provided when loading, this is the value that will be -+ used. -+ - config I2C_BCM_KONA - tristate "BCM Kona I2C adapter" - depends on ARCH_BCM_MOBILE -diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile -index 56388f6..5403a9b 100644 ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o - obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o - obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o - obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o -+obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o - obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o - obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o - obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -new file mode 100644 -index 0000000..09203c0 ---- /dev/null -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -0,0 +1,420 @@ -+/* -+ * Driver for Broadcom BCM2708 BSC Controllers -+ * -+ * Copyright (C) 2012 Chris Boot & Frank Buss -+ * -+ * This driver is inspired by: -+ * i2c-ocores.c, by Peter Korsgaard -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* BSC register offsets */ -+#define BSC_C 0x00 -+#define BSC_S 0x04 -+#define BSC_DLEN 0x08 -+#define BSC_A 0x0c -+#define BSC_FIFO 0x10 -+#define BSC_DIV 0x14 -+#define BSC_DEL 0x18 -+#define BSC_CLKT 0x1c -+ -+/* Bitfields in BSC_C */ -+#define BSC_C_I2CEN 0x00008000 -+#define BSC_C_INTR 0x00000400 -+#define BSC_C_INTT 0x00000200 -+#define BSC_C_INTD 0x00000100 -+#define BSC_C_ST 0x00000080 -+#define BSC_C_CLEAR_1 0x00000020 -+#define BSC_C_CLEAR_2 0x00000010 -+#define BSC_C_READ 0x00000001 -+ -+/* Bitfields in BSC_S */ -+#define BSC_S_CLKT 0x00000200 -+#define BSC_S_ERR 0x00000100 -+#define BSC_S_RXF 0x00000080 -+#define BSC_S_TXE 0x00000040 -+#define BSC_S_RXD 0x00000020 -+#define BSC_S_TXD 0x00000010 -+#define BSC_S_RXR 0x00000008 -+#define BSC_S_TXW 0x00000004 -+#define BSC_S_DONE 0x00000002 -+#define BSC_S_TA 0x00000001 -+ -+#define I2C_TIMEOUT_MS 150 -+ -+#define DRV_NAME "bcm2708_i2c" -+ -+static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; -+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); -+MODULE_PARM_DESC(baudrate, "The I2C baudrate"); -+ -+ -+struct bcm2708_i2c { -+ struct i2c_adapter adapter; -+ -+ spinlock_t lock; -+ void __iomem *base; -+ int irq; -+ struct clk *clk; -+ -+ struct completion done; -+ -+ struct i2c_msg *msg; -+ int pos; -+ int nmsgs; -+ bool error; -+}; -+ -+/* -+ * This function sets the ALT mode on the I2C pins so that we can use them with -+ * the BSC hardware. -+ * -+ * FIXME: This is a hack. Use pinmux / pinctrl. -+ */ -+static void bcm2708_i2c_init_pinmode(int id) -+{ -+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) -+ -+ int pin; -+ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); -+ -+ BUG_ON(id != 0 && id != 1); -+ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */ -+ for (pin = id*2+0; pin <= id*2+1; pin++) { -+printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin); -+ INP_GPIO(pin); /* set mode to GPIO input first */ -+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ -+ } -+ -+ iounmap(gpio); -+ -+#undef INP_GPIO -+#undef SET_GPIO_ALT -+} -+ -+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg) -+{ -+ return readl(bi->base + reg); -+} -+ -+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val) -+{ -+ writel(val, bi->base + reg); -+} -+ -+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi) -+{ -+ bcm2708_wr(bi, BSC_C, 0); -+ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); -+} -+ -+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi) -+{ -+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len)) -+ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); -+} -+ -+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) -+{ -+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len)) -+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); -+} -+ -+static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) -+{ -+ unsigned long bus_hz; -+ u32 cdiv; -+ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; -+ -+ bus_hz = clk_get_rate(bi->clk); -+ cdiv = bus_hz / baudrate; -+ if (cdiv > 0xffff) -+ cdiv = 0xffff; -+ -+ if (bi->msg->flags & I2C_M_RD) -+ c |= BSC_C_INTR | BSC_C_READ; -+ else -+ c |= BSC_C_INTT; -+ -+ bcm2708_wr(bi, BSC_DIV, cdiv); -+ bcm2708_wr(bi, BSC_A, bi->msg->addr); -+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ bcm2708_wr(bi, BSC_C, c); -+} -+ -+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) -+{ -+ struct bcm2708_i2c *bi = dev_id; -+ bool handled = true; -+ u32 s; -+ -+ spin_lock(&bi->lock); -+ -+ /* we may see camera interrupts on the "other" I2C channel -+ Just return if we've not sent anything */ -+ if (!bi->nmsgs || !bi->msg ) -+ goto early_exit; -+ -+ s = bcm2708_rd(bi, BSC_S); -+ -+ if (s & (BSC_S_CLKT | BSC_S_ERR)) { -+ bcm2708_bsc_reset(bi); -+ bi->error = true; -+ -+ /* wake up our bh */ -+ complete(&bi->done); -+ } else if (s & BSC_S_DONE) { -+ bi->nmsgs--; -+ -+ if (bi->msg->flags & I2C_M_RD) -+ bcm2708_bsc_fifo_drain(bi); -+ -+ bcm2708_bsc_reset(bi); -+ -+ if (bi->nmsgs) { -+ /* advance to next message */ -+ bi->msg++; -+ bi->pos = 0; -+ bcm2708_bsc_setup(bi); ++ case VCHIQ_IOC_CREATE_SERVICE: { ++ VCHIQ_CREATE_SERVICE_T args; ++ USER_SERVICE_T *user_service = NULL; ++ void *userdata; ++ int srvstate; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ user_service = kmalloc(sizeof(USER_SERVICE_T), GFP_KERNEL); ++ if (!user_service) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ if (args.is_open) { ++ if (!instance->connected) { ++ ret = -ENOTCONN; ++ kfree(user_service); ++ break; ++ } ++ srvstate = VCHIQ_SRVSTATE_OPENING; + } else { -+ /* wake up our bh */ -+ complete(&bi->done); ++ srvstate = ++ instance->connected ? ++ VCHIQ_SRVSTATE_LISTENING : ++ VCHIQ_SRVSTATE_HIDDEN; + } -+ } else if (s & BSC_S_TXW) { -+ bcm2708_bsc_fifo_fill(bi); -+ } else if (s & BSC_S_RXR) { -+ bcm2708_bsc_fifo_drain(bi); -+ } else { -+ handled = false; -+ } + -+early_exit: -+ spin_unlock(&bi->lock); -+ -+ return handled ? IRQ_HANDLED : IRQ_NONE; -+} -+ -+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap, -+ struct i2c_msg *msgs, int num) -+{ -+ struct bcm2708_i2c *bi = adap->algo_data; -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&bi->lock, flags); -+ -+ reinit_completion(&bi->done); -+ bi->msg = msgs; -+ bi->pos = 0; -+ bi->nmsgs = num; -+ bi->error = false; -+ -+ bcm2708_bsc_setup(bi); -+ -+ /* unlockig _after_ the setup to avoid races with the interrupt routine */ -+ spin_unlock_irqrestore(&bi->lock, flags); -+ -+ ret = wait_for_completion_timeout(&bi->done, -+ msecs_to_jiffies(I2C_TIMEOUT_MS)); -+ if (ret == 0) { -+ dev_err(&adap->dev, "transfer timed out\n"); -+ spin_lock_irqsave(&bi->lock, flags); -+ bcm2708_bsc_reset(bi); -+ spin_unlock_irqrestore(&bi->lock, flags); -+ return -ETIMEDOUT; -+ } -+ -+ return bi->error ? -EIO : num; -+} -+ -+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap) -+{ -+ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL; -+} -+ -+static struct i2c_algorithm bcm2708_i2c_algorithm = { -+ .master_xfer = bcm2708_i2c_master_xfer, -+ .functionality = bcm2708_i2c_functionality, -+}; -+ -+static int bcm2708_i2c_probe(struct platform_device *pdev) -+{ -+ struct resource *regs; -+ int irq, err = -ENOMEM; -+ struct clk *clk; -+ struct bcm2708_i2c *bi; -+ struct i2c_adapter *adap; -+ unsigned long bus_hz; -+ u32 cdiv; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) { -+ dev_err(&pdev->dev, "could not get IO memory\n"); -+ return -ENXIO; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(&pdev->dev, "could not get IRQ\n"); -+ return irq; -+ } -+ -+ clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); -+ return PTR_ERR(clk); -+ } -+ -+ bcm2708_i2c_init_pinmode(pdev->id); -+ -+ bi = kzalloc(sizeof(*bi), GFP_KERNEL); -+ if (!bi) -+ goto out_clk_put; -+ -+ platform_set_drvdata(pdev, bi); -+ -+ adap = &bi->adapter; -+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC; -+ adap->algo = &bcm2708_i2c_algorithm; -+ adap->algo_data = bi; -+ adap->dev.parent = &pdev->dev; -+ adap->nr = pdev->id; -+ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); -+ -+ switch (pdev->id) { -+ case 0: -+ adap->class = I2C_CLASS_HWMON; -+ break; -+ case 1: -+ adap->class = I2C_CLASS_DDC; -+ break; -+ default: -+ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n"); -+ err = -ENXIO; -+ goto out_free_bi; -+ } -+ -+ spin_lock_init(&bi->lock); -+ init_completion(&bi->done); -+ -+ bi->base = ioremap(regs->start, resource_size(regs)); -+ if (!bi->base) { -+ dev_err(&pdev->dev, "could not remap memory\n"); -+ goto out_free_bi; -+ } -+ -+ bi->irq = irq; -+ bi->clk = clk; -+ -+ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED, -+ dev_name(&pdev->dev), bi); -+ if (err) { -+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); -+ goto out_iounmap; -+ } -+ -+ bcm2708_bsc_reset(bi); -+ -+ err = i2c_add_numbered_adapter(adap); -+ if (err < 0) { -+ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err); -+ goto out_free_irq; -+ } -+ -+ bus_hz = clk_get_rate(bi->clk); -+ cdiv = bus_hz / baudrate; -+ if (cdiv > 0xffff) { -+ cdiv = 0xffff; -+ baudrate = bus_hz / cdiv; -+ } -+ -+ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", -+ pdev->id, (unsigned long)regs->start, irq, baudrate); -+ -+ return 0; -+ -+out_free_irq: -+ free_irq(bi->irq, bi); -+out_iounmap: -+ iounmap(bi->base); -+out_free_bi: -+ kfree(bi); -+out_clk_put: -+ clk_put(clk); -+ return err; -+} -+ -+static int bcm2708_i2c_remove(struct platform_device *pdev) -+{ -+ struct bcm2708_i2c *bi = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ i2c_del_adapter(&bi->adapter); -+ free_irq(bi->irq, bi); -+ iounmap(bi->base); -+ clk_disable(bi->clk); -+ clk_put(bi->clk); -+ kfree(bi); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm2708_i2c_driver = { -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm2708_i2c_probe, -+ .remove = bcm2708_i2c_remove, -+}; -+ -+// module_platform_driver(bcm2708_i2c_driver); -+ -+ -+static int __init bcm2708_i2c_init(void) -+{ -+ return platform_driver_register(&bcm2708_i2c_driver); -+} -+ -+static void __exit bcm2708_i2c_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_i2c_driver); -+} -+ -+module_init(bcm2708_i2c_init); -+module_exit(bcm2708_i2c_exit); -+ -+ -+ -+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708"); -+MODULE_AUTHOR("Chris Boot "); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" DRV_NAME); -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 9982998..ae4ea48 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -86,6 +86,14 @@ config SPI_BCM2835 - is for the regular SPI controller. Slave mode operation is not also - not supported. - -+config SPI_BCM2708 -+ tristate "BCM2708 SPI controller driver (SPI0)" -+ depends on MACH_BCM2708 -+ help -+ This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This -+ driver is not compatible with the "Universal SPI Master" or the SPI slave -+ device. -+ - config SPI_BFIN5XX - tristate "SPI controller driver for ADI Blackfin5xx" - depends on BLACKFIN && !BF60x -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index 6b9d2ac..b322f27 100644 ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o - obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o - obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o - obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o -+obj-$(CONFIG_SPI_BCM2708) += spi-bcm2708.o - obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o - obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o - obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -new file mode 100644 -index 0000000..b04a57d ---- /dev/null -+++ b/drivers/spi/spi-bcm2708.c -@@ -0,0 +1,626 @@ -+/* -+ * Driver for Broadcom BCM2708 SPI Controllers -+ * -+ * Copyright (C) 2012 Chris Boot -+ * -+ * This driver is inspired by: -+ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos -+ * spi-atmel.c, Copyright (C) 2006 Atmel Corporation -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* SPI register offsets */ -+#define SPI_CS 0x00 -+#define SPI_FIFO 0x04 -+#define SPI_CLK 0x08 -+#define SPI_DLEN 0x0c -+#define SPI_LTOH 0x10 -+#define SPI_DC 0x14 -+ -+/* Bitfields in CS */ -+#define SPI_CS_LEN_LONG 0x02000000 -+#define SPI_CS_DMA_LEN 0x01000000 -+#define SPI_CS_CSPOL2 0x00800000 -+#define SPI_CS_CSPOL1 0x00400000 -+#define SPI_CS_CSPOL0 0x00200000 -+#define SPI_CS_RXF 0x00100000 -+#define SPI_CS_RXR 0x00080000 -+#define SPI_CS_TXD 0x00040000 -+#define SPI_CS_RXD 0x00020000 -+#define SPI_CS_DONE 0x00010000 -+#define SPI_CS_LEN 0x00002000 -+#define SPI_CS_REN 0x00001000 -+#define SPI_CS_ADCS 0x00000800 -+#define SPI_CS_INTR 0x00000400 -+#define SPI_CS_INTD 0x00000200 -+#define SPI_CS_DMAEN 0x00000100 -+#define SPI_CS_TA 0x00000080 -+#define SPI_CS_CSPOL 0x00000040 -+#define SPI_CS_CLEAR_RX 0x00000020 -+#define SPI_CS_CLEAR_TX 0x00000010 -+#define SPI_CS_CPOL 0x00000008 -+#define SPI_CS_CPHA 0x00000004 -+#define SPI_CS_CS_10 0x00000002 -+#define SPI_CS_CS_01 0x00000001 -+ -+#define SPI_TIMEOUT_MS 150 -+ -+#define DRV_NAME "bcm2708_spi" -+ -+struct bcm2708_spi { -+ spinlock_t lock; -+ void __iomem *base; -+ int irq; -+ struct clk *clk; -+ bool stopping; -+ -+ struct list_head queue; -+ struct workqueue_struct *workq; -+ struct work_struct work; -+ struct completion done; -+ -+ const u8 *tx_buf; -+ u8 *rx_buf; -+ int len; -+}; -+ -+struct bcm2708_spi_state { -+ u32 cs; -+ u16 cdiv; -+}; -+ -+/* -+ * This function sets the ALT mode on the SPI pins so that we can use them with -+ * the SPI hardware. -+ * -+ * FIXME: This is a hack. Use pinmux / pinctrl. -+ */ -+static void bcm2708_init_pinmode(void) -+{ -+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) -+ -+ int pin; -+ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); -+ -+ /* SPI is on GPIO 7..11 */ -+ for (pin = 7; pin <= 11; pin++) { -+ INP_GPIO(pin); /* set mode to GPIO input first */ -+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ -+ } -+ -+ iounmap(gpio); -+ -+#undef INP_GPIO -+#undef SET_GPIO_ALT -+} -+ -+static inline u32 bcm2708_rd(struct bcm2708_spi *bs, unsigned reg) -+{ -+ return readl(bs->base + reg); -+} -+ -+static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val) -+{ -+ writel(val, bs->base + reg); -+} -+ -+static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len) -+{ -+ u8 byte; -+ -+ while (len--) { -+ byte = bcm2708_rd(bs, SPI_FIFO); -+ if (bs->rx_buf) -+ *bs->rx_buf++ = byte; -+ } -+} -+ -+static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len) -+{ -+ u8 byte; -+ u16 val; -+ -+ if (len > bs->len) -+ len = bs->len; -+ -+ if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) { -+ /* LoSSI mode */ -+ if (unlikely(len % 2)) { -+ printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n"); -+ bs->len = 0; -+ return; -+ } -+ while (len) { -+ if (bs->tx_buf) { -+ val = *(const u16 *)bs->tx_buf; -+ bs->tx_buf += 2; -+ } else -+ val = 0; -+ bcm2708_wr(bs, SPI_FIFO, val); -+ bs->len -= 2; -+ len -= 2; -+ } -+ return; -+ } -+ -+ while (len--) { -+ byte = bs->tx_buf ? *bs->tx_buf++ : 0; -+ bcm2708_wr(bs, SPI_FIFO, byte); -+ bs->len--; -+ } -+} -+ -+static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id) -+{ -+ struct spi_master *master = dev_id; -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ u32 cs; -+ -+ spin_lock(&bs->lock); -+ -+ cs = bcm2708_rd(bs, SPI_CS); -+ -+ if (cs & SPI_CS_DONE) { -+ if (bs->len) { /* first interrupt in a transfer */ -+ /* fill the TX fifo with up to 16 bytes */ -+ bcm2708_wr_fifo(bs, 16); -+ } else { /* transfer complete */ -+ /* disable interrupts */ -+ cs &= ~(SPI_CS_INTR | SPI_CS_INTD); -+ bcm2708_wr(bs, SPI_CS, cs); -+ -+ /* drain RX FIFO */ -+ while (cs & SPI_CS_RXD) { -+ bcm2708_rd_fifo(bs, 1); -+ cs = bcm2708_rd(bs, SPI_CS); ++ userdata = args.params.userdata; ++ args.params.callback = service_callback; ++ args.params.userdata = user_service; ++ service = vchiq_add_service_internal( ++ instance->state, ++ &args.params, srvstate, ++ instance, user_service_free); ++ ++ if (service != NULL) { ++ user_service->service = service; ++ user_service->userdata = userdata; ++ user_service->instance = instance; ++ user_service->is_vchi = (args.is_vchi != 0); ++ user_service->dequeue_pending = 0; ++ user_service->close_pending = 0; ++ user_service->message_available_pos = ++ instance->completion_remove - 1; ++ user_service->msg_insert = 0; ++ user_service->msg_remove = 0; ++ sema_init(&user_service->insert_event, 0); ++ sema_init(&user_service->remove_event, 0); ++ sema_init(&user_service->close_event, 0); ++ ++ if (args.is_open) { ++ status = vchiq_open_service_internal ++ (service, instance->pid); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_remove_service(service->handle); ++ service = NULL; ++ ret = (status == VCHIQ_RETRY) ? ++ -EINTR : -EIO; ++ break; ++ } + } + -+ /* wake up our bh */ -+ complete(&bs->done); ++ if (copy_to_user((void __user *) ++ &(((VCHIQ_CREATE_SERVICE_T __user *) ++ arg)->handle), ++ (const void *)&service->handle, ++ sizeof(service->handle)) != 0) { ++ ret = -EFAULT; ++ vchiq_remove_service(service->handle); ++ } ++ ++ service = NULL; ++ } else { ++ ret = -EEXIST; ++ kfree(user_service); + } -+ } else if (cs & SPI_CS_RXR) { -+ /* read 12 bytes of data */ -+ bcm2708_rd_fifo(bs, 12); ++ } break; + -+ /* write up to 12 bytes */ -+ bcm2708_wr_fifo(bs, 12); -+ } ++ case VCHIQ_IOC_CLOSE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + -+ spin_unlock(&bs->lock); ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_close_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } + -+ return IRQ_HANDLED; -+} -+ -+static int bcm2708_setup_state(struct spi_master *master, -+ struct device *dev, struct bcm2708_spi_state *state, -+ u32 hz, u8 csel, u8 mode, u8 bpw) -+{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ int cdiv; -+ unsigned long bus_hz; -+ u32 cs = 0; -+ -+ bus_hz = clk_get_rate(bs->clk); -+ -+ if (hz >= bus_hz) { -+ cdiv = 2; /* bus_hz / 2 is as fast as we can go */ -+ } else if (hz) { -+ cdiv = DIV_ROUND_UP(bus_hz, hz); -+ -+ /* CDIV must be a power of 2, so round up */ -+ cdiv = roundup_pow_of_two(cdiv); -+ -+ if (cdiv > 65536) { -+ dev_dbg(dev, -+ "setup: %d Hz too slow, cdiv %u; min %ld Hz\n", -+ hz, cdiv, bus_hz / 65536); -+ return -EINVAL; -+ } else if (cdiv == 65536) { -+ cdiv = 0; -+ } else if (cdiv == 1) { -+ cdiv = 2; /* 1 gets rounded down to 0; == 65536 */ ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; + } -+ } else { -+ cdiv = 0; -+ } ++ else ++ ret = -EINVAL; ++ } break; + -+ switch (bpw) { -+ case 8: -+ break; -+ case 9: -+ /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */ -+ cs |= SPI_CS_LEN; -+ break; -+ default: -+ dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n", -+ bpw); -+ return -EINVAL; -+ } ++ case VCHIQ_IOC_REMOVE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + -+ if (mode & SPI_CPOL) -+ cs |= SPI_CS_CPOL; -+ if (mode & SPI_CPHA) -+ cs |= SPI_CS_CPHA; ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_remove_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } + -+ if (!(mode & SPI_NO_CS)) { -+ if (mode & SPI_CS_HIGH) { -+ cs |= SPI_CS_CSPOL; -+ cs |= SPI_CS_CSPOL0 << csel; ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; ++ } ++ else ++ ret = -EINVAL; ++ } break; ++ ++ case VCHIQ_IOC_USE_SERVICE: ++ case VCHIQ_IOC_RELEASE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? ++ vchiq_use_service_internal(service) : ++ vchiq_release_service_internal(service); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s: cmd %s returned error %d for " ++ "service %c%c%c%c:%03d", ++ __func__, ++ (cmd == VCHIQ_IOC_USE_SERVICE) ? ++ "VCHIQ_IOC_USE_SERVICE" : ++ "VCHIQ_IOC_RELEASE_SERVICE", ++ status, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->client_id); ++ ret = -EINVAL; ++ } ++ } else ++ ret = -EINVAL; ++ } break; ++ ++ case VCHIQ_IOC_QUEUE_MESSAGE: { ++ VCHIQ_QUEUE_MESSAGE_T args; ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; + } + -+ cs |= csel; -+ } else { -+ cs |= SPI_CS_CS_10 | SPI_CS_CS_01; -+ } ++ service = find_service_for_instance(instance, args.handle); + -+ if (state) { -+ state->cs = cs; -+ state->cdiv = cdiv; -+ dev_dbg(dev, "setup: want %d Hz; " -+ "bus_hz=%lu / cdiv=%u == %lu Hz; " -+ "mode %u: cs 0x%08X\n", -+ hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs); -+ } ++ if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { ++ /* Copy elements into kernel space */ ++ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; ++ if (copy_from_user(elements, args.elements, ++ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) ++ status = vchiq_queue_message ++ (args.handle, ++ elements, args.count); ++ else ++ ret = -EFAULT; ++ } else { ++ ret = -EINVAL; ++ } ++ } break; + -+ return 0; -+} ++ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: ++ case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ struct bulk_waiter_node *waiter = NULL; ++ VCHIQ_BULK_DIR_T dir = ++ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? ++ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; + -+static int bcm2708_process_transfer(struct bcm2708_spi *bs, -+ struct spi_message *msg, struct spi_transfer *xfer) -+{ -+ struct spi_device *spi = msg->spi; -+ struct bcm2708_spi_state state, *stp; -+ int ret; -+ u32 cs; ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } + -+ if (bs->stopping) -+ return -ESHUTDOWN; ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } + -+ if (xfer->bits_per_word || xfer->speed_hz) { -+ ret = bcm2708_setup_state(spi->master, &spi->dev, &state, -+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, -+ spi->chip_select, spi->mode, -+ xfer->bits_per_word ? xfer->bits_per_word : -+ spi->bits_per_word); -+ if (ret) -+ return ret; ++ if (args.mode == VCHIQ_BULK_MODE_BLOCKING) { ++ waiter = kzalloc(sizeof(struct bulk_waiter_node), ++ GFP_KERNEL); ++ if (!waiter) { ++ ret = -ENOMEM; ++ break; ++ } ++ args.userdata = &waiter->bulk_waiter; ++ } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { ++ struct list_head *pos; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_for_each(pos, &instance->bulk_waiter_list) { ++ if (list_entry(pos, struct bulk_waiter_node, ++ list)->pid == current->pid) { ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ break; ++ } + -+ stp = &state; -+ } else { -+ stp = spi->controller_state; -+ } ++ } ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ if (!waiter) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "no bulk_waiter found for pid %d", ++ current->pid); ++ ret = -ESRCH; ++ break; ++ } ++ vchiq_log_info(vchiq_arm_log_level, ++ "found bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); ++ args.userdata = &waiter->bulk_waiter; ++ } ++ status = vchiq_bulk_transfer ++ (args.handle, ++ VCHI_MEM_HANDLE_INVALID, ++ args.data, args.size, ++ args.userdata, args.mode, ++ dir); ++ if (!waiter) ++ break; ++ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || ++ !waiter->bulk_waiter.bulk) { ++ if (waiter->bulk_waiter.bulk) { ++ /* Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ waiter->bulk_waiter.bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); ++ } ++ kfree(waiter); ++ } else { ++ const VCHIQ_BULK_MODE_T mode_waiting = ++ VCHIQ_BULK_MODE_WAITING; ++ waiter->pid = current->pid; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_add(&waiter->list, &instance->bulk_waiter_list); ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ vchiq_log_info(vchiq_arm_log_level, ++ "saved bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); + -+ reinit_completion(&bs->done); -+ bs->tx_buf = xfer->tx_buf; -+ bs->rx_buf = xfer->rx_buf; -+ bs->len = xfer->len; ++ if (copy_to_user((void __user *) ++ &(((VCHIQ_QUEUE_BULK_TRANSFER_T __user *) ++ arg)->mode), ++ (const void *)&mode_waiting, ++ sizeof(mode_waiting)) != 0) ++ ret = -EFAULT; ++ } ++ } break; + -+ cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA; ++ case VCHIQ_IOC_AWAIT_COMPLETION: { ++ VCHIQ_AWAIT_COMPLETION_T args; + -+ bcm2708_wr(bs, SPI_CLK, stp->cdiv); -+ bcm2708_wr(bs, SPI_CS, cs); ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ if (!instance->connected) { ++ ret = -ENOTCONN; ++ break; ++ } + -+ ret = wait_for_completion_timeout(&bs->done, -+ msecs_to_jiffies(SPI_TIMEOUT_MS)); -+ if (ret == 0) { -+ dev_err(&spi->dev, "transfer timed out\n"); -+ return -ETIMEDOUT; -+ } ++ if (copy_from_user(&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } + -+ if (xfer->delay_usecs) -+ udelay(xfer->delay_usecs); ++ mutex_lock(&instance->completion_mutex); + -+ if (list_is_last(&xfer->transfer_list, &msg->transfers) || -+ xfer->cs_change) { -+ /* clear TA and interrupt flags */ -+ bcm2708_wr(bs, SPI_CS, stp->cs); -+ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ while ((instance->completion_remove == ++ instance->completion_insert) ++ && !instance->closing) { ++ int rc; ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ mutex_unlock(&instance->completion_mutex); ++ rc = down_interruptible(&instance->insert_event); ++ mutex_lock(&instance->completion_mutex); ++ if (rc != 0) { ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ vchiq_log_info(vchiq_arm_log_level, ++ "AWAIT_COMPLETION interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); + -+ msg->actual_length += (xfer->len - bs->len); ++ /* A read memory barrier is needed to stop prefetch of a stale ++ ** completion record ++ */ ++ rmb(); + -+ return 0; -+} ++ if (ret == 0) { ++ int msgbufcount = args.msgbufcount; ++ for (ret = 0; ret < args.count; ret++) { ++ VCHIQ_COMPLETION_DATA_T *completion; ++ VCHIQ_SERVICE_T *service; ++ USER_SERVICE_T *user_service; ++ VCHIQ_HEADER_T *header; ++ if (instance->completion_remove == ++ instance->completion_insert) ++ break; ++ completion = &instance->completions[ ++ instance->completion_remove & ++ (MAX_COMPLETIONS - 1)]; + -+static void bcm2708_work(struct work_struct *work) -+{ -+ struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work); -+ unsigned long flags; -+ struct spi_message *msg; -+ struct spi_transfer *xfer; -+ int status = 0; ++ service = completion->service_userdata; ++ user_service = service->base.userdata; ++ completion->service_userdata = ++ user_service->userdata; + -+ spin_lock_irqsave(&bs->lock, flags); -+ while (!list_empty(&bs->queue)) { -+ msg = list_first_entry(&bs->queue, struct spi_message, queue); -+ list_del_init(&msg->queue); -+ spin_unlock_irqrestore(&bs->lock, flags); ++ header = completion->header; ++ if (header) { ++ void __user *msgbuf; ++ int msglen; + -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ status = bcm2708_process_transfer(bs, msg, xfer); -+ if (status) ++ msglen = header->size + ++ sizeof(VCHIQ_HEADER_T); ++ /* This must be a VCHIQ-style service */ ++ if (args.msgbufsize < msglen) { ++ vchiq_log_error( ++ vchiq_arm_log_level, ++ "header %x: msgbufsize" ++ " %x < msglen %x", ++ (unsigned int)header, ++ args.msgbufsize, ++ msglen); ++ WARN(1, "invalid message " ++ "size\n"); ++ if (ret == 0) ++ ret = -EMSGSIZE; ++ break; ++ } ++ if (msgbufcount <= 0) ++ /* Stall here for lack of a ++ ** buffer for the message. */ ++ break; ++ /* Get the pointer from user space */ ++ msgbufcount--; ++ if (copy_from_user(&msgbuf, ++ (const void __user *) ++ &args.msgbufs[msgbufcount], ++ sizeof(msgbuf)) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Copy the message to user space */ ++ if (copy_to_user(msgbuf, header, ++ msglen) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Now it has been copied, the message ++ ** can be released. */ ++ vchiq_release_message(service->handle, ++ header); ++ ++ /* The completion must point to the ++ ** msgbuf. */ ++ completion->header = msgbuf; ++ } ++ ++ if ((completion->reason == ++ VCHIQ_SERVICE_CLOSED) && ++ !instance->use_close_delivered) ++ unlock_service(service); ++ ++ if (copy_to_user((void __user *)( ++ (size_t)args.buf + ++ ret * sizeof(VCHIQ_COMPLETION_DATA_T)), ++ completion, ++ sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ instance->completion_remove++; ++ } ++ ++ if (msgbufcount != args.msgbufcount) { ++ if (copy_to_user((void __user *) ++ &((VCHIQ_AWAIT_COMPLETION_T *)arg)-> ++ msgbufcount, ++ &msgbufcount, ++ sizeof(msgbufcount)) != 0) { ++ ret = -EFAULT; ++ } ++ } ++ } ++ ++ if (ret != 0) ++ up(&instance->remove_event); ++ mutex_unlock(&instance->completion_mutex); ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ } break; ++ ++ case VCHIQ_IOC_DEQUEUE_MESSAGE: { ++ VCHIQ_DEQUEUE_MESSAGE_T args; ++ USER_SERVICE_T *user_service; ++ VCHIQ_HEADER_T *header; ++ ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } ++ user_service = (USER_SERVICE_T *)service->base.userdata; ++ if (user_service->is_vchi == 0) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ spin_lock(&msg_queue_spinlock); ++ if (user_service->msg_remove == user_service->msg_insert) { ++ if (!args.blocking) { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ ret = -EWOULDBLOCK; ++ break; ++ } ++ user_service->dequeue_pending = 1; ++ do { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (down_interruptible( ++ &user_service->insert_event) != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "DEQUEUE_MESSAGE interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ spin_lock(&msg_queue_spinlock); ++ } while (user_service->msg_remove == ++ user_service->msg_insert); ++ ++ if (ret) + break; + } + -+ msg->status = status; -+ msg->complete(msg->context); ++ BUG_ON((int)(user_service->msg_insert - ++ user_service->msg_remove) < 0); + -+ spin_lock_irqsave(&bs->lock, flags); ++ header = user_service->msg_queue[user_service->msg_remove & ++ (MSG_QUEUE_SIZE - 1)]; ++ user_service->msg_remove++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ up(&user_service->remove_event); ++ if (header == NULL) ++ ret = -ENOTCONN; ++ else if (header->size <= args.bufsize) { ++ /* Copy to user space if msgbuf is not NULL */ ++ if ((args.buf == NULL) || ++ (copy_to_user((void __user *)args.buf, ++ header->data, ++ header->size) == 0)) { ++ ret = header->size; ++ vchiq_release_message( ++ service->handle, ++ header); ++ } else ++ ret = -EFAULT; ++ } else { ++ vchiq_log_error(vchiq_arm_log_level, ++ "header %x: bufsize %x < size %x", ++ (unsigned int)header, args.bufsize, ++ header->size); ++ WARN(1, "invalid size\n"); ++ ret = -EMSGSIZE; ++ } ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ } break; ++ ++ case VCHIQ_IOC_GET_CLIENT_ID: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ ret = vchiq_get_client_id(handle); ++ } break; ++ ++ case VCHIQ_IOC_GET_CONFIG: { ++ VCHIQ_GET_CONFIG_T args; ++ VCHIQ_CONFIG_T config; ++ ++ if (copy_from_user(&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ if (args.config_size > sizeof(config)) { ++ ret = -EINVAL; ++ break; ++ } ++ status = vchiq_get_config(instance, args.config_size, &config); ++ if (status == VCHIQ_SUCCESS) { ++ if (copy_to_user((void __user *)args.pconfig, ++ &config, args.config_size) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ } ++ } break; ++ ++ case VCHIQ_IOC_SET_SERVICE_OPTION: { ++ VCHIQ_SET_SERVICE_OPTION_T args; ++ ++ if (copy_from_user( ++ &args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ status = vchiq_set_service_option( ++ args.handle, args.option, args.value); ++ } break; ++ ++ case VCHIQ_IOC_DUMP_PHYS_MEM: { ++ VCHIQ_DUMP_MEM_T args; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ dump_phys_mem(args.virt_addr, args.num_bytes); ++ } break; ++ ++ case VCHIQ_IOC_LIB_VERSION: { ++ unsigned int lib_version = (unsigned int)arg; ++ ++ if (lib_version < VCHIQ_VERSION_MIN) ++ ret = -EINVAL; ++ else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) ++ instance->use_close_delivered = 1; ++ } break; ++ ++ case VCHIQ_IOC_CLOSE_DELIVERED: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ service = find_closed_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ close_delivered(user_service); ++ } ++ else ++ ret = -EINVAL; ++ } break; ++ ++ default: ++ ret = -ENOTTY; ++ break; + } -+ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ if (service) ++ unlock_service(service); ++ ++ if (ret == 0) { ++ if (status == VCHIQ_ERROR) ++ ret = -EIO; ++ else if (status == VCHIQ_RETRY) ++ ret = -EINTR; ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) && ++ (ret != -EWOULDBLOCK)) ++ vchiq_log_info(vchiq_arm_log_level, ++ " ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ++ ioctl_names[_IOC_NR(cmd)] : ++ "", ++ status, ret); ++ else ++ vchiq_log_trace(vchiq_arm_log_level, ++ " ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ++ ioctl_names[_IOC_NR(cmd)] : ++ "", ++ status, ret); ++ ++ return ret; +} + -+static int bcm2708_spi_setup(struct spi_device *spi) ++/**************************************************************************** ++* ++* vchiq_open ++* ++***************************************************************************/ ++ ++static int ++vchiq_open(struct inode *inode, struct file *file) +{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); -+ struct bcm2708_spi_state *state; -+ int ret; ++ int dev = iminor(inode) & 0x0f; ++ vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); ++ switch (dev) { ++ case VCHIQ_MINOR: { ++ int ret; ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ VCHIQ_INSTANCE_T instance; + -+ if (bs->stopping) -+ return -ESHUTDOWN; ++ if (!state) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq has no connection to VideoCore"); ++ return -ENOTCONN; ++ } + -+ if (!(spi->mode & SPI_NO_CS) && -+ (spi->chip_select > spi->master->num_chipselect)) { -+ dev_dbg(&spi->dev, -+ "setup: invalid chipselect %u (%u defined)\n", -+ spi->chip_select, spi->master->num_chipselect); -+ return -EINVAL; -+ } -+ -+ state = spi->controller_state; -+ if (!state) { -+ state = kzalloc(sizeof(*state), GFP_KERNEL); -+ if (!state) ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) + return -ENOMEM; + -+ spi->controller_state = state; -+ } ++ instance->state = state; ++ instance->pid = current->tgid; + -+ ret = bcm2708_setup_state(spi->master, &spi->dev, state, -+ spi->max_speed_hz, spi->chip_select, spi->mode, -+ spi->bits_per_word); -+ if (ret < 0) { -+ kfree(state); -+ spi->controller_state = NULL; -+ return ret; -+ } -+ -+ dev_dbg(&spi->dev, -+ "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n", -+ spi->chip_select, spi->max_speed_hz, spi->bits_per_word, -+ spi->mode, state->cs, state->cdiv); -+ -+ return 0; -+} -+ -+static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg) -+{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); -+ struct spi_transfer *xfer; -+ int ret; -+ unsigned long flags; -+ -+ if (unlikely(list_empty(&msg->transfers))) -+ return -EINVAL; -+ -+ if (bs->stopping) -+ return -ESHUTDOWN; -+ -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { -+ dev_dbg(&spi->dev, "missing rx or tx buf\n"); -+ return -EINVAL; ++ ret = vchiq_debugfs_add_instance(instance); ++ if (ret != 0) { ++ kfree(instance); ++ return ret; + } + -+ if (!xfer->bits_per_word || xfer->speed_hz) -+ continue; ++ sema_init(&instance->insert_event, 0); ++ sema_init(&instance->remove_event, 0); ++ mutex_init(&instance->completion_mutex); ++ mutex_init(&instance->bulk_waiter_list_mutex); ++ INIT_LIST_HEAD(&instance->bulk_waiter_list); + -+ ret = bcm2708_setup_state(spi->master, &spi->dev, NULL, -+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, -+ spi->chip_select, spi->mode, -+ xfer->bits_per_word ? xfer->bits_per_word : -+ spi->bits_per_word); -+ if (ret) -+ return ret; -+ } ++ file->private_data = instance; ++ } break; + -+ msg->status = -EINPROGRESS; -+ msg->actual_length = 0; -+ -+ spin_lock_irqsave(&bs->lock, flags); -+ list_add_tail(&msg->queue, &bs->queue); -+ queue_work(bs->workq, &bs->work); -+ spin_unlock_irqrestore(&bs->lock, flags); -+ -+ return 0; -+} -+ -+static void bcm2708_spi_cleanup(struct spi_device *spi) -+{ -+ if (spi->controller_state) { -+ kfree(spi->controller_state); -+ spi->controller_state = NULL; -+ } -+} -+ -+static int bcm2708_spi_probe(struct platform_device *pdev) -+{ -+ struct resource *regs; -+ int irq, err = -ENOMEM; -+ struct clk *clk; -+ struct spi_master *master; -+ struct bcm2708_spi *bs; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) { -+ dev_err(&pdev->dev, "could not get IO memory\n"); ++ default: ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unknown minor device: %d", dev); + return -ENXIO; + } + -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(&pdev->dev, "could not get IRQ\n"); -+ return irq; ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vchiq_release ++* ++***************************************************************************/ ++ ++static int ++vchiq_release(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode) & 0x0f; ++ int ret = 0; ++ switch (dev) { ++ case VCHIQ_MINOR: { ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq_release: instance=%lx", ++ (unsigned long)instance); ++ ++ if (!state) { ++ ret = -EPERM; ++ goto out; ++ } ++ ++ /* Ensure videocore is awake to allow termination. */ ++ vchiq_use_internal(instance->state, NULL, ++ USE_TYPE_VCHIQ); ++ ++ mutex_lock(&instance->completion_mutex); ++ ++ /* Wake the completion thread and ask it to exit */ ++ instance->closing = 1; ++ up(&instance->insert_event); ++ ++ mutex_unlock(&instance->completion_mutex); ++ ++ /* Wake the slot handler if the completion queue is full. */ ++ up(&instance->remove_event); ++ ++ /* Mark all services for termination... */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ USER_SERVICE_T *user_service = service->base.userdata; ++ ++ /* Wake the slot handler if the msg queue is full. */ ++ up(&user_service->remove_event); ++ ++ vchiq_terminate_service_internal(service); ++ unlock_service(service); ++ } ++ ++ /* ...and wait for them to die */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, &i)) ++ != NULL) { ++ USER_SERVICE_T *user_service = service->base.userdata; ++ ++ down(&service->remove_event); ++ ++ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); ++ ++ spin_lock(&msg_queue_spinlock); ++ ++ while (user_service->msg_remove != ++ user_service->msg_insert) { ++ VCHIQ_HEADER_T *header = user_service-> ++ msg_queue[user_service->msg_remove & ++ (MSG_QUEUE_SIZE - 1)]; ++ user_service->msg_remove++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ if (header) ++ vchiq_release_message( ++ service->handle, ++ header); ++ spin_lock(&msg_queue_spinlock); ++ } ++ ++ spin_unlock(&msg_queue_spinlock); ++ ++ unlock_service(service); ++ } ++ ++ /* Release any closed services */ ++ while (instance->completion_remove != ++ instance->completion_insert) { ++ VCHIQ_COMPLETION_DATA_T *completion; ++ VCHIQ_SERVICE_T *service; ++ completion = &instance->completions[ ++ instance->completion_remove & ++ (MAX_COMPLETIONS - 1)]; ++ service = completion->service_userdata; ++ if (completion->reason == VCHIQ_SERVICE_CLOSED) ++ { ++ USER_SERVICE_T *user_service = ++ service->base.userdata; ++ ++ /* Wake any blocked user-thread */ ++ if (instance->use_close_delivered) ++ up(&user_service->close_event); ++ unlock_service(service); ++ } ++ instance->completion_remove++; ++ } ++ ++ /* Release the PEER service count. */ ++ vchiq_release_internal(instance->state, NULL); ++ ++ { ++ struct list_head *pos, *next; ++ list_for_each_safe(pos, next, ++ &instance->bulk_waiter_list) { ++ struct bulk_waiter_node *waiter; ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ vchiq_log_info(vchiq_arm_log_level, ++ "bulk_waiter - cleaned up %x " ++ "for pid %d", ++ (unsigned int)waiter, waiter->pid); ++ kfree(waiter); ++ } ++ } ++ ++ vchiq_debugfs_remove_instance(instance); ++ ++ kfree(instance); ++ file->private_data = NULL; ++ } break; ++ ++ default: ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unknown minor device: %d", dev); ++ ret = -ENXIO; + } + -+ clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); -+ return PTR_ERR(clk); ++out: ++ return ret; ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump ++* ++***************************************************************************/ ++ ++void ++vchiq_dump(void *dump_context, const char *str, int len) ++{ ++ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; ++ ++ if (context->actual < context->space) { ++ int copy_bytes; ++ if (context->offset > 0) { ++ int skip_bytes = min(len, (int)context->offset); ++ str += skip_bytes; ++ len -= skip_bytes; ++ context->offset -= skip_bytes; ++ if (context->offset > 0) ++ return; ++ } ++ copy_bytes = min(len, (int)(context->space - context->actual)); ++ if (copy_bytes == 0) ++ return; ++ if (copy_to_user(context->buf + context->actual, str, ++ copy_bytes)) ++ context->actual = -EFAULT; ++ context->actual += copy_bytes; ++ len -= copy_bytes; ++ ++ /* If tne terminating NUL is included in the length, then it ++ ** marks the end of a line and should be replaced with a ++ ** carriage return. */ ++ if ((len == 0) && (str[copy_bytes - 1] == '\0')) { ++ char cr = '\n'; ++ if (copy_to_user(context->buf + context->actual - 1, ++ &cr, 1)) ++ context->actual = -EFAULT; ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_instance_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_instances(void *dump_context) ++{ ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ char buf[80]; ++ int len; ++ int i; ++ ++ /* There is no list of instances, so instead scan all services, ++ marking those that have been dumped. */ ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; ++ ++ if (service && (service->base.callback == service_callback)) { ++ instance = service->instance; ++ if (instance) ++ instance->mark = 0; ++ } + } + -+ bcm2708_init_pinmode(); ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; + -+ master = spi_alloc_master(&pdev->dev, sizeof(*bs)); -+ if (!master) { -+ dev_err(&pdev->dev, "spi_alloc_master() failed\n"); -+ goto out_clk_put; ++ if (service && (service->base.callback == service_callback)) { ++ instance = service->instance; ++ if (instance && !instance->mark) { ++ len = snprintf(buf, sizeof(buf), ++ "Instance %x: pid %d,%s completions " ++ "%d/%d", ++ (unsigned int)instance, instance->pid, ++ instance->connected ? " connected, " : ++ "", ++ instance->completion_insert - ++ instance->completion_remove, ++ MAX_COMPLETIONS); ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ instance->mark = 1; ++ } ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_service_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; ++ char buf[80]; ++ int len; ++ ++ len = snprintf(buf, sizeof(buf), " instance %x", ++ (unsigned int)service->instance); ++ ++ if ((service->base.callback == service_callback) && ++ user_service->is_vchi) { ++ len += snprintf(buf + len, sizeof(buf) - len, ++ ", %d/%d messages", ++ user_service->msg_insert - user_service->msg_remove, ++ MSG_QUEUE_SIZE); ++ ++ if (user_service->dequeue_pending) ++ len += snprintf(buf + len, sizeof(buf) - len, ++ " (dequeue pending)"); + } + -+ /* the spi->mode bits understood by this driver: */ -+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS; ++ vchiq_dump(dump_context, buf, len + 1); ++} + -+ master->bus_num = pdev->id; -+ master->num_chipselect = 3; -+ master->setup = bcm2708_spi_setup; -+ master->transfer = bcm2708_spi_transfer; -+ master->cleanup = bcm2708_spi_cleanup; -+ platform_set_drvdata(pdev, master); ++/**************************************************************************** ++* ++* dump_user_mem ++* ++***************************************************************************/ + -+ bs = spi_master_get_devdata(master); ++static void ++dump_phys_mem(void *virt_addr, uint32_t num_bytes) ++{ ++ int rc; ++ uint8_t *end_virt_addr = virt_addr + num_bytes; ++ int num_pages; ++ int offset; ++ int end_offset; ++ int page_idx; ++ int prev_idx; ++ struct page *page; ++ struct page **pages; ++ uint8_t *kmapped_virt_ptr; + -+ spin_lock_init(&bs->lock); -+ INIT_LIST_HEAD(&bs->queue); -+ init_completion(&bs->done); -+ INIT_WORK(&bs->work, bcm2708_work); ++ /* Align virtAddr and endVirtAddr to 16 byte boundaries. */ + -+ bs->base = ioremap(regs->start, resource_size(regs)); -+ if (!bs->base) { -+ dev_err(&pdev->dev, "could not remap memory\n"); -+ goto out_master_put; ++ virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL); ++ end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) & ++ ~0x0fuL); ++ ++ offset = (int)(long)virt_addr & (PAGE_SIZE - 1); ++ end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); ++ ++ num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); ++ if (pages == NULL) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocation memory for %d pages\n", ++ num_pages); ++ return; + } + -+ bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev)); -+ if (!bs->workq) { -+ dev_err(&pdev->dev, "could not create workqueue\n"); -+ goto out_iounmap; ++ down_read(¤t->mm->mmap_sem); ++ rc = get_user_pages(current, /* task */ ++ current->mm, /* mm */ ++ (unsigned long)virt_addr, /* start */ ++ num_pages, /* len */ ++ 0, /* write */ ++ 0, /* force */ ++ pages, /* pages (array of page pointers) */ ++ NULL); /* vmas */ ++ up_read(¤t->mm->mmap_sem); ++ ++ prev_idx = -1; ++ page = NULL; ++ ++ while (offset < end_offset) { ++ ++ int page_offset = offset % PAGE_SIZE; ++ page_idx = offset / PAGE_SIZE; ++ ++ if (page_idx != prev_idx) { ++ ++ if (page != NULL) ++ kunmap(page); ++ page = pages[page_idx]; ++ kmapped_virt_ptr = kmap(page); ++ ++ prev_idx = page_idx; ++ } ++ ++ if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE) ++ vchiq_log_dump_mem("ph", ++ (uint32_t)(unsigned long)&kmapped_virt_ptr[ ++ page_offset], ++ &kmapped_virt_ptr[page_offset], 16); ++ ++ offset += 16; ++ } ++ if (page != NULL) ++ kunmap(page); ++ ++ for (page_idx = 0; page_idx < num_pages; page_idx++) ++ page_cache_release(pages[page_idx]); ++ ++ kfree(pages); ++} ++ ++/**************************************************************************** ++* ++* vchiq_read ++* ++***************************************************************************/ ++ ++static ssize_t ++vchiq_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ DUMP_CONTEXT_T context; ++ context.buf = buf; ++ context.actual = 0; ++ context.space = count; ++ context.offset = *ppos; ++ ++ vchiq_dump_state(&context, &g_state); ++ ++ *ppos += context.actual; ++ ++ return context.actual; ++} ++ ++VCHIQ_STATE_T * ++vchiq_get_state(void) ++{ ++ ++ if (g_state.remote == NULL) ++ printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); ++ else if (g_state.remote->initialised != 1) ++ printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", ++ __func__, g_state.remote->initialised); ++ ++ return ((g_state.remote != NULL) && ++ (g_state.remote->initialised == 1)) ? &g_state : NULL; ++} ++ ++static const struct file_operations ++vchiq_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = vchiq_ioctl, ++ .open = vchiq_open, ++ .release = vchiq_release, ++ .read = vchiq_read ++}; ++ ++/* ++ * Autosuspend related functionality ++ */ ++ ++int ++vchiq_videocore_wanted(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ if (!arm_state) ++ /* autosuspend not supported - always return wanted */ ++ return 1; ++ else if (arm_state->blocked_count) ++ return 1; ++ else if (!arm_state->videocore_use_count) ++ /* usage count zero - check for override unless we're forcing */ ++ if (arm_state->resume_blocked) ++ return 0; ++ else ++ return vchiq_platform_videocore_wanted(state); ++ else ++ /* non-zero usage count - videocore still required */ ++ return 1; ++} ++ ++static VCHIQ_STATUS_T ++vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T service_user, ++ void *bulk_user) ++{ ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s callback reason %d", __func__, reason); ++ return 0; ++} ++ ++static int ++vchiq_keepalive_thread_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ VCHIQ_STATUS_T status; ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_SERVICE_HANDLE_T ka_handle; ++ ++ VCHIQ_SERVICE_PARAMS_T params = { ++ .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'), ++ .callback = vchiq_keepalive_vchiq_callback, ++ .version = KEEPALIVE_VER, ++ .version_min = KEEPALIVE_VER_MIN ++ }; ++ ++ status = vchiq_initialise(&instance); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_initialise failed %d", __func__, status); ++ goto exit; + } + -+ bs->irq = irq; -+ bs->clk = clk; -+ bs->stopping = false; -+ -+ err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev), -+ master); -+ if (err) { -+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); -+ goto out_workqueue; ++ status = vchiq_connect(instance); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_connect failed %d", __func__, status); ++ goto shutdown; + } + -+ /* initialise the hardware */ -+ clk_enable(clk); -+ bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); -+ -+ err = spi_register_master(master); -+ if (err) { -+ dev_err(&pdev->dev, "could not register SPI master: %d\n", err); -+ goto out_free_irq; ++ status = vchiq_add_service(instance, ¶ms, &ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_open_service failed %d", __func__, status); ++ goto shutdown; + } + -+ dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n", -+ (unsigned long)regs->start, irq); ++ while (1) { ++ long rc = 0, uc = 0; ++ if (wait_for_completion_interruptible(&arm_state->ka_evt) ++ != 0) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s interrupted", __func__); ++ flush_signals(current); ++ continue; ++ } ++ ++ /* read and clear counters. Do release_count then use_count to ++ * prevent getting more releases than uses */ ++ rc = atomic_xchg(&arm_state->ka_release_count, 0); ++ uc = atomic_xchg(&arm_state->ka_use_count, 0); ++ ++ /* Call use/release service the requisite number of times. ++ * Process use before release so use counts don't go negative */ ++ while (uc--) { ++ atomic_inc(&arm_state->ka_use_ack_count); ++ status = vchiq_use_service(ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_use_service error %d", ++ __func__, status); ++ } ++ } ++ while (rc--) { ++ status = vchiq_release_service(ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_release_service error %d", ++ __func__, status); ++ } ++ } ++ } ++ ++shutdown: ++ vchiq_shutdown(instance); ++exit: ++ return 0; ++} ++ ++ ++ ++VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (arm_state) { ++ rwlock_init(&arm_state->susp_res_lock); ++ ++ init_completion(&arm_state->ka_evt); ++ atomic_set(&arm_state->ka_use_count, 0); ++ atomic_set(&arm_state->ka_use_ack_count, 0); ++ atomic_set(&arm_state->ka_release_count, 0); ++ ++ init_completion(&arm_state->vc_suspend_complete); ++ ++ init_completion(&arm_state->vc_resume_complete); ++ /* Initialise to 'done' state. We only want to block on resume ++ * completion while videocore is suspended. */ ++ set_resume_state(arm_state, VC_RESUME_RESUMED); ++ ++ init_completion(&arm_state->resume_blocker); ++ /* Initialise to 'done' state. We only want to block on this ++ * completion while resume is blocked */ ++ complete_all(&arm_state->resume_blocker); ++ ++ init_completion(&arm_state->blocked_blocker); ++ /* Initialise to 'done' state. We only want to block on this ++ * completion while things are waiting on the resume blocker */ ++ complete_all(&arm_state->blocked_blocker); ++ ++ arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; ++ arm_state->suspend_timer_running = 0; ++ init_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer.data = (unsigned long)(state); ++ arm_state->suspend_timer.function = suspend_timer_callback; ++ ++ arm_state->first_connect = 0; ++ ++ } ++ return status; ++} ++ ++/* ++** Functions to modify the state variables; ++** set_suspend_state ++** set_resume_state ++** ++** There are more state variables than we might like, so ensure they remain in ++** step. Suspend and resume state are maintained separately, since most of ++** these state machines can operate independently. However, there are a few ++** states where state transitions in one state machine cause a reset to the ++** other state machine. In addition, there are some completion events which ++** need to occur on state machine reset and end-state(s), so these are also ++** dealt with in these functions. ++** ++** In all states we set the state variable according to the input, but in some ++** cases we perform additional steps outlined below; ++** ++** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. ++** The suspend completion is completed after any suspend ++** attempt. When we reset the state machine we also reset ++** the completion. This reset occurs when videocore is ++** resumed, and also if we initiate suspend after a suspend ++** failure. ++** ++** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for ++** suspend - ie from this point on we must try to suspend ++** before resuming can occur. We therefore also reset the ++** resume state machine to VC_RESUME_IDLE in this state. ++** ++** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call ++** complete_all on the suspend completion to notify ++** anything waiting for suspend to happen. ++** ++** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also ++** initiate resume, so no need to alter resume state. ++** We call complete_all on the suspend completion to notify ++** of suspend rejection. ++** ++** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the ++** suspend completion and reset the resume state machine. ++** ++** VC_RESUME_IDLE - Initialise the resume completion at the same time. The ++** resume completion is in it's 'done' state whenever ++** videcore is running. Therfore, the VC_RESUME_IDLE state ++** implies that videocore is suspended. ++** Hence, any thread which needs to wait until videocore is ++** running can wait on this completion - it will only block ++** if videocore is suspended. ++** ++** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. ++** Call complete_all on the resume completion to unblock ++** any threads waiting for resume. Also reset the suspend ++** state machine to it's idle state. ++** ++** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. ++*/ ++ ++void ++set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_suspend_status new_state) ++{ ++ /* set the state in all cases */ ++ arm_state->vc_suspend_state = new_state; ++ ++ /* state specific additional actions */ ++ switch (new_state) { ++ case VC_SUSPEND_FORCE_CANCELED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_REJECTED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_FAILED: ++ complete_all(&arm_state->vc_suspend_complete); ++ arm_state->vc_resume_state = VC_RESUME_RESUMED; ++ complete_all(&arm_state->vc_resume_complete); ++ break; ++ case VC_SUSPEND_IDLE: ++ reinit_completion(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_REQUESTED: ++ break; ++ case VC_SUSPEND_IN_PROGRESS: ++ set_resume_state(arm_state, VC_RESUME_IDLE); ++ break; ++ case VC_SUSPEND_SUSPENDED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ default: ++ BUG(); ++ break; ++ } ++} ++ ++void ++set_resume_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_resume_status new_state) ++{ ++ /* set the state in all cases */ ++ arm_state->vc_resume_state = new_state; ++ ++ /* state specific additional actions */ ++ switch (new_state) { ++ case VC_RESUME_FAILED: ++ break; ++ case VC_RESUME_IDLE: ++ reinit_completion(&arm_state->vc_resume_complete); ++ break; ++ case VC_RESUME_REQUESTED: ++ break; ++ case VC_RESUME_IN_PROGRESS: ++ break; ++ case VC_RESUME_RESUMED: ++ complete_all(&arm_state->vc_resume_complete); ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ break; ++ default: ++ BUG(); ++ break; ++ } ++} ++ ++ ++/* should be called with the write lock held */ ++inline void ++start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ del_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer.expires = jiffies + ++ msecs_to_jiffies(arm_state-> ++ suspend_timer_timeout); ++ add_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer_running = 1; ++} ++ ++/* should be called with the write lock held */ ++static inline void ++stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ if (arm_state->suspend_timer_running) { ++ del_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer_running = 0; ++ } ++} ++ ++static inline int ++need_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && ++ (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && ++ vchiq_videocore_wanted(state); ++} ++ ++static int ++block_resume(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ int status = VCHIQ_SUCCESS; ++ const unsigned long timeout_val = ++ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS); ++ int resume_count = 0; ++ ++ /* Allow any threads which were blocked by the last force suspend to ++ * complete if they haven't already. Only give this one shot; if ++ * blocked_count is incremented after blocked_blocker is completed ++ * (which only happens when blocked_count hits 0) then those threads ++ * will have to wait until next time around */ ++ if (arm_state->blocked_count) { ++ reinit_completion(&arm_state->blocked_blocker); ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " ++ "blocked clients", __func__); ++ if (wait_for_completion_interruptible_timeout( ++ &arm_state->blocked_blocker, timeout_val) ++ <= 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s wait for " ++ "previously blocked clients failed" , __func__); ++ status = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s previously blocked " ++ "clients resumed", __func__); ++ write_lock_bh(&arm_state->susp_res_lock); ++ } ++ ++ /* We need to wait for resume to complete if it's in process */ ++ while (arm_state->vc_resume_state != VC_RESUME_RESUMED && ++ arm_state->vc_resume_state > VC_RESUME_IDLE) { ++ if (resume_count > 1) { ++ status = VCHIQ_ERROR; ++ vchiq_log_error(vchiq_susp_log_level, "%s waited too " ++ "many times for resume" , __func__); ++ goto out; ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s wait for resume", ++ __func__); ++ if (wait_for_completion_interruptible_timeout( ++ &arm_state->vc_resume_complete, timeout_val) ++ <= 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s wait for " ++ "resume failed (%s)", __func__, ++ resume_state_names[arm_state->vc_resume_state + ++ VC_RESUME_NUM_OFFSET]); ++ status = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__); ++ write_lock_bh(&arm_state->susp_res_lock); ++ resume_count++; ++ } ++ reinit_completion(&arm_state->resume_blocker); ++ arm_state->resume_blocked = 1; ++ ++out: ++ return status; ++} ++ ++static inline void ++unblock_resume(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ complete_all(&arm_state->resume_blocker); ++ arm_state->resume_blocked = 0; ++} ++ ++/* Initiate suspend via slot handler. Should be called with the write lock ++ * held */ ++VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ status = VCHIQ_SUCCESS; ++ ++ ++ switch (arm_state->vc_suspend_state) { ++ case VC_SUSPEND_REQUESTED: ++ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " ++ "requested", __func__); ++ break; ++ case VC_SUSPEND_IN_PROGRESS: ++ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " ++ "progress", __func__); ++ break; ++ ++ default: ++ /* We don't expect to be in other states, so log but continue ++ * anyway */ ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s unexpected suspend state %s", __func__, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ /* fall through */ ++ case VC_SUSPEND_REJECTED: ++ case VC_SUSPEND_FAILED: ++ /* Ensure any idle state actions have been run */ ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ /* fall through */ ++ case VC_SUSPEND_IDLE: ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: suspending", __func__); ++ set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); ++ /* kick the slot handler thread to initiate suspend */ ++ request_poll(state, NULL, 0); ++ break; ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); ++ return status; ++} ++ ++void ++vchiq_platform_check_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int susp = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && ++ arm_state->vc_resume_state == VC_RESUME_RESUMED) { ++ set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); ++ susp = 1; ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (susp) ++ vchiq_platform_suspend(state); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++} ++ ++ ++static void ++output_timeout_error(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ char service_err[50] = ""; ++ int vc_use_count = arm_state->videocore_use_count; ++ int active_services = state->unused_service; ++ int i; ++ ++ if (!arm_state->videocore_use_count) { ++ snprintf(service_err, 50, " Videocore usecount is 0"); ++ goto output_msg; ++ } ++ for (i = 0; i < active_services; i++) { ++ VCHIQ_SERVICE_T *service_ptr = state->services[i]; ++ if (service_ptr && service_ptr->service_use_count && ++ (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { ++ snprintf(service_err, 50, " %c%c%c%c(%d) service has " ++ "use count %d%s", VCHIQ_FOURCC_AS_4CHARS( ++ service_ptr->base.fourcc), ++ service_ptr->client_id, ++ service_ptr->service_use_count, ++ service_ptr->service_use_count == ++ vc_use_count ? "" : " (+ more)"); ++ break; ++ } ++ } ++ ++output_msg: ++ vchiq_log_error(vchiq_susp_log_level, ++ "timed out waiting for vc suspend (%d).%s", ++ arm_state->autosuspend_override, service_err); ++ ++} ++ ++/* Try to get videocore into suspended state, regardless of autosuspend state. ++** We don't actually force suspend, since videocore may get into a bad state ++** if we force suspend at a bad time. Instead, we wait for autosuspend to ++** determine a good point to suspend. If this doesn't happen within 100ms we ++** report failure. ++** ++** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if ++** videocore failed to suspend in time or VCHIQ_ERROR if interrupted. ++*/ ++VCHIQ_STATUS_T ++vchiq_arm_force_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ long rc = 0; ++ int repeat = -1; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ ++ status = block_resume(arm_state); ++ if (status != VCHIQ_SUCCESS) ++ goto unlock; ++ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { ++ /* Already suspended - just block resume and exit */ ++ vchiq_log_info(vchiq_susp_log_level, "%s already suspended", ++ __func__); ++ status = VCHIQ_SUCCESS; ++ goto unlock; ++ } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) { ++ /* initiate suspend immediately in the case that we're waiting ++ * for the timeout */ ++ stop_suspend_timer(arm_state); ++ if (!vchiq_videocore_wanted(state)) { ++ vchiq_log_info(vchiq_susp_log_level, "%s videocore " ++ "idle, initiating suspend", __func__); ++ status = vchiq_arm_vcsuspend(state); ++ } else if (arm_state->autosuspend_override < ++ FORCE_SUSPEND_FAIL_MAX) { ++ vchiq_log_info(vchiq_susp_log_level, "%s letting " ++ "videocore go idle", __func__); ++ status = VCHIQ_SUCCESS; ++ } else { ++ vchiq_log_warning(vchiq_susp_log_level, "%s failed too " ++ "many times - attempting suspend", __func__); ++ status = vchiq_arm_vcsuspend(state); ++ } ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend " ++ "in progress - wait for completion", __func__); ++ status = VCHIQ_SUCCESS; ++ } ++ ++ /* Wait for suspend to happen due to system idle (not forced..) */ ++ if (status != VCHIQ_SUCCESS) ++ goto unblock_resume; ++ ++ do { ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ rc = wait_for_completion_interruptible_timeout( ++ &arm_state->vc_suspend_complete, ++ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS)); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (rc < 0) { ++ vchiq_log_warning(vchiq_susp_log_level, "%s " ++ "interrupted waiting for suspend", __func__); ++ status = VCHIQ_ERROR; ++ goto unblock_resume; ++ } else if (rc == 0) { ++ if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) { ++ /* Repeat timeout once if in progress */ ++ if (repeat < 0) { ++ repeat = 1; ++ continue; ++ } ++ } ++ arm_state->autosuspend_override++; ++ output_timeout_error(state); ++ ++ status = VCHIQ_RETRY; ++ goto unblock_resume; ++ } ++ } while (0 < (repeat--)); ++ ++ /* Check and report state in case we need to abort ARM suspend */ ++ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) { ++ status = VCHIQ_RETRY; ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s videocore suspend failed (state %s)", __func__, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ /* Reset the state only if it's still in an error state. ++ * Something could have already initiated another suspend. */ ++ if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE) ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ ++ goto unblock_resume; ++ } ++ ++ /* successfully suspended - unlock and exit */ ++ goto unlock; ++ ++unblock_resume: ++ /* all error states need to unblock resume before exit */ ++ unblock_resume(arm_state); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); ++ return status; ++} ++ ++void ++vchiq_check_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && ++ arm_state->first_connect && ++ !vchiq_videocore_wanted(state)) { ++ vchiq_arm_vcsuspend(state); ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++} ++ ++ ++int ++vchiq_arm_allow_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int resume = 0; ++ int ret = -1; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ unblock_resume(arm_state); ++ resume = vchiq_check_resume(state); ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (resume) { ++ if (wait_for_completion_interruptible( ++ &arm_state->vc_resume_complete) < 0) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s interrupted", __func__); ++ /* failed, cannot accurately derive suspend ++ * state, so exit early. */ ++ goto out; ++ } ++ } ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: Videocore remains suspended", __func__); ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: Videocore resumed", __func__); ++ ret = 0; ++ } ++ read_unlock_bh(&arm_state->susp_res_lock); ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++/* This function should be called with the write lock held */ ++int ++vchiq_check_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int resume = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (need_resume(state)) { ++ set_resume_state(arm_state, VC_RESUME_REQUESTED); ++ request_poll(state, NULL, 0); ++ resume = 1; ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return resume; ++} ++ ++void ++vchiq_platform_check_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int res = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->wake_address == 0) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: already awake", __func__); ++ goto unlock; ++ } ++ if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: already resuming", __func__); ++ goto unlock; ++ } ++ ++ if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) { ++ set_resume_state(arm_state, VC_RESUME_IN_PROGRESS); ++ res = 1; ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s: not resuming (resume state %s)", __func__, ++ resume_state_names[arm_state->vc_resume_state + ++ VC_RESUME_NUM_OFFSET]); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (res) ++ vchiq_platform_resume(state); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++ ++} ++ ++ ++ ++VCHIQ_STATUS_T ++vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ enum USE_TYPE_E use_type) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[16]; ++ int *entity_uc; ++ int local_uc, local_entity_uc; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (use_type == USE_TYPE_VCHIQ) { ++ sprintf(entity, "VCHIQ: "); ++ entity_uc = &arm_state->peer_use_count; ++ } else if (service) { ++ sprintf(entity, "%c%c%c%c:%03d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id); ++ entity_uc = &service->service_use_count; ++ } else { ++ vchiq_log_error(vchiq_susp_log_level, "%s null service " ++ "ptr", __func__); ++ ret = VCHIQ_ERROR; ++ goto out; ++ } ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ while (arm_state->resume_blocked) { ++ /* If we call 'use' while force suspend is waiting for suspend, ++ * then we're about to block the thread which the force is ++ * waiting to complete, so we're bound to just time out. In this ++ * case, set the suspend state such that the wait will be ++ * canceled, so we can complete as quickly as possible. */ ++ if (arm_state->resume_blocked && arm_state->vc_suspend_state == ++ VC_SUSPEND_IDLE) { ++ set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); ++ break; ++ } ++ /* If suspend is already in progress then we need to block */ ++ if (!try_wait_for_completion(&arm_state->resume_blocker)) { ++ /* Indicate that there are threads waiting on the resume ++ * blocker. These need to be allowed to complete before ++ * a _second_ call to force suspend can complete, ++ * otherwise low priority threads might never actually ++ * continue */ ++ arm_state->blocked_count++; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " ++ "blocked - waiting...", __func__, entity); ++ if (wait_for_completion_killable( ++ &arm_state->resume_blocker) != 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s %s " ++ "wait for resume blocker interrupted", ++ __func__, entity); ++ ret = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ arm_state->blocked_count--; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " ++ "unblocked", __func__, entity); ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (--arm_state->blocked_count == 0) ++ complete_all(&arm_state->blocked_blocker); ++ } ++ } ++ ++ stop_suspend_timer(arm_state); ++ ++ local_uc = ++arm_state->videocore_use_count; ++ local_entity_uc = ++(*entity_uc); ++ ++ /* If there's a pending request which hasn't yet been serviced then ++ * just clear it. If we're past VC_SUSPEND_REQUESTED state then ++ * vc_resume_complete will block until we either resume or fail to ++ * suspend */ ++ if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ ++ if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { ++ set_resume_state(arm_state, VC_RESUME_REQUESTED); ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, local_entity_uc, local_uc); ++ request_poll(state, NULL, 0); ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, *entity_uc, local_uc); ++ ++ ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ /* Completion is in a done state when we're not suspended, so this won't ++ * block for the non-suspended case. */ ++ if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { ++ vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", ++ __func__, entity); ++ if (wait_for_completion_killable( ++ &arm_state->vc_resume_complete) != 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " ++ "resume interrupted", __func__, entity); ++ ret = VCHIQ_ERROR; ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, ++ entity); ++ } ++ ++ if (ret == VCHIQ_SUCCESS) { ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); ++ while (ack_cnt && (status == VCHIQ_SUCCESS)) { ++ /* Send the use notify to videocore */ ++ status = vchiq_send_remote_use_active(state); ++ if (status == VCHIQ_SUCCESS) ++ ack_cnt--; ++ else ++ atomic_add(ack_cnt, ++ &arm_state->ka_use_ack_count); ++ } ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[16]; ++ int *entity_uc; ++ int local_uc, local_entity_uc; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (service) { ++ sprintf(entity, "%c%c%c%c:%03d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id); ++ entity_uc = &service->service_use_count; ++ } else { ++ sprintf(entity, "PEER: "); ++ entity_uc = &arm_state->peer_use_count; ++ } ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (!arm_state->videocore_use_count || !(*entity_uc)) { ++ /* Don't use BUG_ON - don't allow user thread to crash kernel */ ++ WARN_ON(!arm_state->videocore_use_count); ++ WARN_ON(!(*entity_uc)); ++ ret = VCHIQ_ERROR; ++ goto unlock; ++ } ++ local_uc = --arm_state->videocore_use_count; ++ local_entity_uc = --(*entity_uc); ++ ++ if (!vchiq_videocore_wanted(state)) { ++ if (vchiq_platform_use_suspend_timer() && ++ !arm_state->resume_blocked) { ++ /* Only use the timer if we're not trying to force ++ * suspend (=> resume_blocked) */ ++ start_suspend_timer(arm_state); ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s %s count %d, state count %d - suspending", ++ __func__, entity, *entity_uc, ++ arm_state->videocore_use_count); ++ vchiq_arm_vcsuspend(state); ++ } ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, *entity_uc, ++ arm_state->videocore_use_count); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++void ++vchiq_on_remote_use(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ atomic_inc(&arm_state->ka_use_count); ++ complete(&arm_state->ka_evt); ++} ++ ++void ++vchiq_on_remote_release(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ atomic_inc(&arm_state->ka_release_count); ++ complete(&arm_state->ka_evt); ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ return vchiq_release_internal(service->state, service); ++} ++ ++VCHIQ_DEBUGFS_NODE_T * ++vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance) ++{ ++ return &instance->debugfs_node; ++} ++ ++int ++vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int use_count = 0, i; ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ use_count += service->service_use_count; ++ unlock_service(service); ++ } ++ return use_count; ++} ++ ++int ++vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->pid; ++} ++ ++int ++vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->trace; ++} ++ ++void ++vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ service->trace = trace; ++ unlock_service(service); ++ } ++ instance->trace = (trace != 0); ++} ++ ++static void suspend_timer_callback(unsigned long context) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ if (!arm_state) ++ goto out; ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s - suspend timer expired - check suspend", __func__); ++ vchiq_check_suspend(state); ++out: ++ return; ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_use_internal(service->state, service, ++ USE_TYPE_SERVICE_NO_RESUME); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_use_internal(service->state, service, ++ USE_TYPE_SERVICE); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_release_internal(service->state, service); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int i, j = 0; ++ /* Only dump 64 services */ ++ static const int local_max_services = 64; ++ /* If there's more than 64 services, only dump ones with ++ * non-zero counts */ ++ int only_nonzero = 0; ++ static const char *nz = "<-- preventing suspend"; ++ ++ enum vc_suspend_status vc_suspend_state; ++ enum vc_resume_status vc_resume_state; ++ int peer_count; ++ int vc_use_count; ++ int active_services; ++ struct service_data_struct { ++ int fourcc; ++ int clientid; ++ int use_count; ++ } service_data[local_max_services]; ++ ++ if (!arm_state) ++ return; ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ vc_suspend_state = arm_state->vc_suspend_state; ++ vc_resume_state = arm_state->vc_resume_state; ++ peer_count = arm_state->peer_use_count; ++ vc_use_count = arm_state->videocore_use_count; ++ active_services = state->unused_service; ++ if (active_services > local_max_services) ++ only_nonzero = 1; ++ ++ for (i = 0; (i < active_services) && (j < local_max_services); i++) { ++ VCHIQ_SERVICE_T *service_ptr = state->services[i]; ++ if (!service_ptr) ++ continue; ++ ++ if (only_nonzero && !service_ptr->service_use_count) ++ continue; ++ ++ if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) { ++ service_data[j].fourcc = service_ptr->base.fourcc; ++ service_data[j].clientid = service_ptr->client_id; ++ service_data[j++].use_count = service_ptr-> ++ service_use_count; ++ } ++ } ++ ++ read_unlock_bh(&arm_state->susp_res_lock); ++ ++ vchiq_log_warning(vchiq_susp_log_level, ++ "-- Videcore suspend state: %s --", ++ suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); ++ vchiq_log_warning(vchiq_susp_log_level, ++ "-- Videcore resume state: %s --", ++ resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); ++ ++ if (only_nonzero) ++ vchiq_log_warning(vchiq_susp_log_level, "Too many active " ++ "services (%d). Only dumping up to first %d services " ++ "with non-zero use-count", active_services, ++ local_max_services); ++ ++ for (i = 0; i < j; i++) { ++ vchiq_log_warning(vchiq_susp_log_level, ++ "----- %c%c%c%c:%d service count %d %s", ++ VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc), ++ service_data[i].clientid, ++ service_data[i].use_count, ++ service_data[i].use_count ? nz : ""); ++ } ++ vchiq_log_warning(vchiq_susp_log_level, ++ "----- VCHIQ use count count %d", peer_count); ++ vchiq_log_warning(vchiq_susp_log_level, ++ "--- Overall vchiq instance use count %d", vc_use_count); ++ ++ vchiq_dump_platform_use_state(state); ++} ++ ++VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state; ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ ++ if (!service || !service->state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ arm_state = vchiq_platform_get_arm_state(service->state); ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ if (service->service_use_count) ++ ret = VCHIQ_SUCCESS; ++ read_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (ret == VCHIQ_ERROR) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s ERROR - %c%c%c%c:%d service count %d, " ++ "state count %d, videocore suspend state %s", __func__, ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id, service->service_use_count, ++ arm_state->videocore_use_count, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ vchiq_dump_service_use_state(service->state); ++ } ++out: ++ return ret; ++} ++ ++/* stub functions */ ++void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) ++{ ++ (void)state; ++} ++ ++void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, ++ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, ++ get_conn_state_name(oldstate), get_conn_state_name(newstate)); ++ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (!arm_state->first_connect) { ++ char threadname[10]; ++ arm_state->first_connect = 1; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ snprintf(threadname, sizeof(threadname), "VCHIQka-%d", ++ state->id); ++ arm_state->ka_thread = kthread_create( ++ &vchiq_keepalive_thread_func, ++ (void *)state, ++ threadname); ++ if (arm_state->ka_thread == NULL) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "vchiq: FATAL: couldn't create thread %s", ++ threadname); ++ } else { ++ wake_up_process(arm_state->ka_thread); ++ } ++ } else ++ write_unlock_bh(&arm_state->susp_res_lock); ++ } ++} ++ ++ ++/**************************************************************************** ++* ++* vchiq_init - called when the module is loaded. ++* ++***************************************************************************/ ++ ++static int __init ++vchiq_init(void) ++{ ++ int err; ++ void *ptr_err; ++ ++ /* create debugfs entries */ ++ err = vchiq_debugfs_init(); ++ if (err != 0) ++ goto failed_debugfs_init; ++ ++ err = alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1, DEVICE_NAME); ++ if (err != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocate device number"); ++ goto failed_alloc_chrdev; ++ } ++ cdev_init(&vchiq_cdev, &vchiq_fops); ++ vchiq_cdev.owner = THIS_MODULE; ++ err = cdev_add(&vchiq_cdev, vchiq_devid, 1); ++ if (err != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to register device"); ++ goto failed_cdev_add; ++ } ++ ++ /* create sysfs entries */ ++ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); ++ ptr_err = vchiq_class; ++ if (IS_ERR(ptr_err)) ++ goto failed_class_create; ++ ++ vchiq_dev = device_create(vchiq_class, NULL, ++ vchiq_devid, NULL, "vchiq"); ++ ptr_err = vchiq_dev; ++ if (IS_ERR(ptr_err)) ++ goto failed_device_create; ++ ++ err = vchiq_platform_init(&g_state); ++ if (err != 0) ++ goto failed_platform_init; ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq: initialised - version %d (min %d), device %d.%d", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN, ++ MAJOR(vchiq_devid), MINOR(vchiq_devid)); + + return 0; + -+out_free_irq: -+ free_irq(bs->irq, master); -+out_workqueue: -+ destroy_workqueue(bs->workq); -+out_iounmap: -+ iounmap(bs->base); -+out_master_put: -+ spi_master_put(master); -+out_clk_put: -+ clk_put(clk); ++failed_platform_init: ++ device_destroy(vchiq_class, vchiq_devid); ++failed_device_create: ++ class_destroy(vchiq_class); ++failed_class_create: ++ cdev_del(&vchiq_cdev); ++ err = PTR_ERR(ptr_err); ++failed_cdev_add: ++ unregister_chrdev_region(vchiq_devid, 1); ++failed_alloc_chrdev: ++ vchiq_debugfs_deinit(); ++failed_debugfs_init: ++ vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq"); + return err; +} + -+static int bcm2708_spi_remove(struct platform_device *pdev) ++/**************************************************************************** ++* ++* vchiq_exit - called when the module is unloaded. ++* ++***************************************************************************/ ++ ++static void __exit ++vchiq_exit(void) +{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ -+ /* reset the hardware and block queue progress */ -+ spin_lock_irq(&bs->lock); -+ bs->stopping = true; -+ bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); -+ spin_unlock_irq(&bs->lock); -+ -+ flush_work(&bs->work); -+ -+ clk_disable(bs->clk); -+ clk_put(bs->clk); -+ free_irq(bs->irq, master); -+ iounmap(bs->base); -+ -+ spi_unregister_master(master); -+ -+ return 0; ++ vchiq_platform_exit(&g_state); ++ device_destroy(vchiq_class, vchiq_devid); ++ class_destroy(vchiq_class); ++ cdev_del(&vchiq_cdev); ++ unregister_chrdev_region(vchiq_devid, 1); +} + -+static struct platform_driver bcm2708_spi_driver = { -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm2708_spi_probe, -+ .remove = bcm2708_spi_remove, -+}; -+ -+ -+static int __init bcm2708_spi_init(void) -+{ -+ return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe); -+} -+module_init(bcm2708_spi_init); -+ -+static void __exit bcm2708_spi_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_spi_driver); -+} -+module_exit(bcm2708_spi_exit); -+ -+ -+//module_platform_driver(bcm2708_spi_driver); -+ -+MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708"); -+MODULE_AUTHOR("Chris Boot "); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" DRV_NAME); - -From 682719078e0d0e6ac1abe5845faa65e5f0b579bb Mon Sep 17 00:00:00 2001 -From: cbeytas -Date: Mon, 24 Jun 2013 00:05:40 -0400 -Subject: [PATCH 021/172] Perform I2C combined transactions when possible - -Perform I2C combined transactions whenever possible, within the -restrictions of the Broadcomm Serial Controller. - -Disable DONE interrupt during TA poll - -Prevent interrupt from being triggered if poll is missed and transfer -starts and finishes. - -i2c: Make combined transactions optional and disabled by default ---- - drivers/i2c/busses/i2c-bcm2708.c | 31 ++++++++++++++++++++++++++++++- - 1 file changed, 30 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -index 09203c0..7d385a3 100644 ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -74,6 +74,9 @@ static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; - module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - MODULE_PARM_DESC(baudrate, "The I2C baudrate"); - -+static bool combined = false; -+module_param(combined, bool, 0644); -+MODULE_PARM_DESC(combined, "Use combined transactions"); - - struct bcm2708_i2c { - struct i2c_adapter adapter; -@@ -150,7 +153,7 @@ static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) - static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) - { - unsigned long bus_hz; -- u32 cdiv; -+ u32 cdiv, s; - u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; - - bus_hz = clk_get_rate(bi->clk); -@@ -166,6 +169,32 @@ static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) - bcm2708_wr(bi, BSC_DIV, cdiv); - bcm2708_wr(bi, BSC_A, bi->msg->addr); - bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ if (combined) -+ { -+ /* Do the next two messages meet combined transaction criteria? -+ - Current message is a write, next message is a read -+ - Both messages to same slave address -+ - Write message can fit inside FIFO (16 bytes or less) */ -+ if ( (bi->nmsgs > 1) && -+ !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && -+ (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { -+ /* Fill FIFO with entire write message (16 byte FIFO) */ -+ while (bi->pos < bi->msg->len) -+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); -+ /* Start write transfer (no interrupts, don't clear FIFO) */ -+ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); -+ /* poll for transfer start bit (should only take 1-20 polls) */ -+ do { -+ s = bcm2708_rd(bi, BSC_S); -+ } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE))); -+ /* Send next read message before the write transfer finishes. */ -+ bi->nmsgs--; -+ bi->msg++; -+ bi->pos = 0; -+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; -+ } -+ } - bcm2708_wr(bi, BSC_C, c); - } - - -From db6f4cb59c89e54db302266e6c827137f3da5d96 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 8 May 2013 11:46:50 +0100 -Subject: [PATCH 022/172] enabling the realtime clock 1-wire chip DS1307 and - 1-wire on GPIO4 (as a module) - -1-wire: Add support for configuring pin for w1-gpio kernel module -See: https://github.com/raspberrypi/linux/pull/457 - -Add bitbanging pullups, use them for w1-gpio - -Allows parasite power to work, uses module option pullup=1 - -bcm2708: Ensure 1-wire pullup is disabled by default, and expose as module parameter - -Signed-off-by: Alex J Lennon - -w1-gpio: Add gpiopin module parameter and correctly free up gpio pull-up pin, if set - -Signed-off-by: Alex J Lennon ---- - arch/arm/mach-bcm2708/bcm2708.c | 29 ++++++++++++++++++++++ - drivers/w1/masters/w1-gpio.c | 55 +++++++++++++++++++++++++++++++++++++---- - drivers/w1/w1.h | 6 +++++ - drivers/w1/w1_int.c | 14 +++++++++++ - drivers/w1/w1_io.c | 18 +++++++++++--- - 5 files changed, 114 insertions(+), 8 deletions(-) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 82f56fb..7dd89a7f 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -76,12 +77,19 @@ - */ - #define DMA_MASK_BITS_COMMON 32 - -+// use GPIO 4 for the one-wire GPIO pin, if enabled -+#define W1_GPIO 4 -+// ensure one-wire GPIO pullup is disabled by default -+#define W1_PULLUP -1 -+ - /* command line parameters */ - static unsigned boardrev, serial; - static unsigned uart_clock; - static unsigned disk_led_gpio = 16; - static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; -+static unsigned w1_gpio_pin = W1_GPIO; -+static unsigned w1_gpio_pullup = W1_PULLUP; - - static void __init bcm2708_init_led(void); - -@@ -258,6 +266,20 @@ static struct platform_device bcm2708_dmaman_device = { - .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), - }; - -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+static struct w1_gpio_platform_data w1_gpio_pdata = { -+ .pin = W1_GPIO, -+ .ext_pullup_enable_pin = W1_PULLUP, -+ .is_open_drain = 0, -+}; -+ -+static struct platform_device w1_device = { -+ .name = "w1-gpio", -+ .id = -1, -+ .dev.platform_data = &w1_gpio_pdata, -+}; -+#endif -+ - static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_fb_device = { -@@ -652,6 +674,11 @@ void __init bcm2708_init(void) - #ifdef CONFIG_BCM2708_GPIO - bcm_register_device(&bcm2708_gpio_device); - #endif -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+ w1_gpio_pdata.pin = w1_gpio_pin; -+ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -+ platform_device_register(&w1_device); -+#endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); - bcm_register_device(&bcm2708_usb_device); -@@ -853,3 +880,5 @@ module_param(uart_clock, uint, 0644); - module_param(disk_led_gpio, uint, 0644); - module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); -+module_param(w1_gpio_pin, uint, 0644); -+module_param(w1_gpio_pullup, uint, 0644); -diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c -index b99a932..3ad15cc 100644 ---- a/drivers/w1/masters/w1-gpio.c -+++ b/drivers/w1/masters/w1-gpio.c -@@ -23,6 +23,15 @@ - #include "../w1.h" - #include "../w1_int.h" - -+static int w1_gpio_pullup = -1; -+static int w1_gpio_pullup_orig = -1; -+module_param_named(pullup, w1_gpio_pullup, int, 0); -+MODULE_PARM_DESC(pullup, "GPIO pin pullup number"); -+static int w1_gpio_pin = -1; -+static int w1_gpio_pin_orig = -1; -+module_param_named(gpiopin, w1_gpio_pin, int, 0); -+MODULE_PARM_DESC(gpiopin, "GPIO pin number"); -+ - static u8 w1_gpio_set_pullup(void *data, int delay) - { - struct w1_gpio_platform_data *pdata = data; -@@ -67,6 +76,16 @@ static u8 w1_gpio_read_bit(void *data) - return gpio_get_value(pdata->pin) ? 1 : 0; - } - -+static void w1_gpio_bitbang_pullup(void *data, u8 on) -+{ -+ struct w1_gpio_platform_data *pdata = data; -+ -+ if (on) -+ gpio_direction_output(pdata->pin, 1); -+ else -+ gpio_direction_input(pdata->pin); -+} -+ - #if defined(CONFIG_OF) - static struct of_device_id w1_gpio_dt_ids[] = { - { .compatible = "w1-gpio" }, -@@ -113,13 +132,15 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) - static int w1_gpio_probe(struct platform_device *pdev) - { - struct w1_bus_master *master; -- struct w1_gpio_platform_data *pdata; -+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; - int err; - -- if (of_have_populated_dt()) { -- err = w1_gpio_probe_dt(pdev); -- if (err < 0) -- return err; -+ if(pdata == NULL) { -+ if (of_have_populated_dt()) { -+ err = w1_gpio_probe_dt(pdev); -+ if (err < 0) -+ return err; -+ } - } - - pdata = dev_get_platdata(&pdev->dev); -@@ -136,6 +157,19 @@ static int w1_gpio_probe(struct platform_device *pdev) - return -ENOMEM; - } - -+ w1_gpio_pin_orig = pdata->pin; -+ w1_gpio_pullup_orig = pdata->ext_pullup_enable_pin; -+ -+ if(gpio_is_valid(w1_gpio_pin)) { -+ pdata->pin = w1_gpio_pin; -+ pdata->ext_pullup_enable_pin = -1; -+ } -+ if(gpio_is_valid(w1_gpio_pullup)) { -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup; -+ } -+ -+ dev_info(&pdev->dev, "gpio pin %d, gpio pullup pin %d\n", pdata->pin, pdata->ext_pullup_enable_pin); -+ - err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); - if (err) { - dev_err(&pdev->dev, "gpio_request (pin) failed\n"); -@@ -165,6 +199,14 @@ static int w1_gpio_probe(struct platform_device *pdev) - master->set_pullup = w1_gpio_set_pullup; - } - -+ if (gpio_is_valid(w1_gpio_pullup)) { -+ if (pdata->is_open_drain) -+ printk(KERN_ERR "w1-gpio 'pullup' option " -+ "doesn't work with open drain GPIO\n"); -+ else -+ master->bitbang_pullup = w1_gpio_bitbang_pullup; -+ } -+ - err = w1_add_master_device(master); - if (err) { - dev_err(&pdev->dev, "w1_add_master device failed\n"); -@@ -195,6 +237,9 @@ static int w1_gpio_remove(struct platform_device *pdev) - - w1_remove_master_device(master); - -+ pdata->pin = w1_gpio_pin_orig; -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup_orig; -+ - return 0; - } - -diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h -index 56a49ba..881d728 100644 ---- a/drivers/w1/w1.h -+++ b/drivers/w1/w1.h -@@ -171,6 +171,12 @@ struct w1_bus_master - - u8 (*set_pullup)(void *, int); - -+ /** -+ * Turns the pullup on/off in bitbanging mode, takes an on/off argument. -+ * @return -1=Error, 0=completed -+ */ -+ void (*bitbang_pullup) (void *, u8); -+ - void (*search)(void *, struct w1_master *, - u8, w1_slave_found_callback); - }; -diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c -index 47249a3..a4b4a8d 100644 ---- a/drivers/w1/w1_int.c -+++ b/drivers/w1/w1_int.c -@@ -123,6 +123,20 @@ int w1_add_master_device(struct w1_bus_master *master) - return(-EINVAL); - } - -+ /* bitbanging hardware uses bitbang_pullup, other hardware uses set_pullup -+ * and takes care of timing itself */ -+ if (!master->write_byte && !master->touch_bit && master->set_pullup) { -+ printk(KERN_ERR "w1_add_master_device: set_pullup requires " -+ "write_byte or touch_bit, disabling\n"); -+ master->set_pullup = NULL; -+ } -+ -+ if (master->set_pullup && master->bitbang_pullup) { -+ printk(KERN_ERR "w1_add_master_device: set_pullup should not " -+ "be set when bitbang_pullup is used, disabling\n"); -+ master->set_pullup = NULL; -+ } -+ - /* Lock until the device is added (or not) to w1_masters. */ - mutex_lock(&w1_mlock); - /* Search for the first available id (starting at 1). */ -diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c -index 2820924..fd0550f 100644 ---- a/drivers/w1/w1_io.c -+++ b/drivers/w1/w1_io.c -@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_master *dev) - static void w1_post_write(struct w1_master *dev) - { - if (dev->pullup_duration) { -- if (dev->enable_pullup && dev->bus_master->set_pullup) -- dev->bus_master->set_pullup(dev->bus_master->data, 0); -- else -+ if (dev->enable_pullup) { -+ if (dev->bus_master->set_pullup) { -+ dev->bus_master->set_pullup(dev-> -+ bus_master->data, -+ 0); -+ } else if (dev->bus_master->bitbang_pullup) { -+ dev->bus_master-> -+ bitbang_pullup(dev->bus_master->data, 1); -+ msleep(dev->pullup_duration); -+ dev->bus_master-> -+ bitbang_pullup(dev->bus_master->data, 0); -+ } -+ } else { - msleep(dev->pullup_duration); -+ } -+ - dev->pullup_duration = 0; - } - } - -From 8160010b3d9ff17f29c3e332e9f3c37c527e26c0 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Fri, 12 Apr 2013 23:58:47 +0100 -Subject: [PATCH 024/172] config: add missing options from 3.6.y kernel - ---- - arch/arm/configs/bcmrpi_defconfig | 692 ++++++++++++++++++++++++++++++++++++-- - 1 file changed, 667 insertions(+), 25 deletions(-) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 6d2eae1..6b39872 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -1,3 +1,5 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0 - # CONFIG_LOCALVERSION_AUTO is not set - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y -@@ -7,6 +9,10 @@ CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y - CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y - CONFIG_IKCONFIG=y - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y -@@ -16,28 +22,41 @@ CONFIG_RESOURCE_COUNTERS=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y -+CONFIG_BLK_DEV_INITRD=y - CONFIG_EMBEDDED=y - # CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y - CONFIG_PROFILING=y - CONFIG_OPROFILE=m - CONFIG_KPROBES=y -+CONFIG_JUMP_LABEL=y - CONFIG_MODULES=y - CONFIG_MODULE_UNLOAD=y - CONFIG_MODVERSIONS=y - CONFIG_MODULE_SRCVERSION_ALL=y --# CONFIG_BLK_DEV_BSG is not set - CONFIG_BLK_DEV_THROTTLING=y - CONFIG_PARTITION_ADVANCED=y - CONFIG_MAC_PARTITION=y - CONFIG_CFQ_GROUP_IOSCHED=y - CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y - CONFIG_AEABI=y -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y - CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" - CONFIG_KEXEC=y -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_STAT=m -+CONFIG_CPU_FREQ_STAT_DETAILS=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y - CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_BINFMT_MISC=m -@@ -48,19 +67,278 @@ CONFIG_XFRM_USER=y - CONFIG_NET_KEY=m - CONFIG_INET=y - CONFIG_IP_MULTICAST=y -+CONFIG_IP_ADVANCED_ROUTER=y -+CONFIG_IP_MULTIPLE_TABLES=y -+CONFIG_IP_ROUTE_MULTIPATH=y -+CONFIG_IP_ROUTE_VERBOSE=y - CONFIG_IP_PNP=y - CONFIG_IP_PNP_DHCP=y - CONFIG_IP_PNP_RARP=y -+CONFIG_NET_IPIP=m -+CONFIG_NET_IPGRE_DEMUX=m -+CONFIG_NET_IPGRE=m -+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_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set -+CONFIG_INET_AH=m -+CONFIG_INET_ESP=m -+CONFIG_INET_IPCOMP=m -+CONFIG_INET_XFRM_MODE_TRANSPORT=m -+CONFIG_INET_XFRM_MODE_TUNNEL=m -+CONFIG_INET_XFRM_MODE_BEET=m -+CONFIG_INET_LRO=m -+CONFIG_INET_DIAG=m -+CONFIG_INET6_AH=m -+CONFIG_INET6_ESP=m -+CONFIG_INET6_IPCOMP=m -+CONFIG_IPV6_TUNNEL=m -+CONFIG_IPV6_MULTIPLE_TABLES=y -+CONFIG_IPV6_MROUTE=y -+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y -+CONFIG_IPV6_PIMSM_V2=y -+CONFIG_NETFILTER=y -+CONFIG_NF_CONNTRACK=m -+CONFIG_NF_CONNTRACK_ZONES=y -+CONFIG_NF_CONNTRACK_EVENTS=y -+CONFIG_NF_CONNTRACK_TIMESTAMP=y -+CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m -+CONFIG_NETFILTER_XT_TARGET_AUDIT=m -+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m -+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -+CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m -+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -+CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 -+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_ESP=m -+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m -+CONFIG_NETFILTER_XT_MATCH_HELPER=m -+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m -+CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y -+CONFIG_IP_VS_PROTO_UDP=y -+CONFIG_IP_VS_PROTO_ESP=y -+CONFIG_IP_VS_PROTO_AH=y -+CONFIG_IP_VS_PROTO_SCTP=y -+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 -+CONFIG_IP_VS_FTP=m -+CONFIG_IP_VS_PE_SIP=m -+CONFIG_NF_CONNTRACK_IPV4=m -+CONFIG_IP_NF_IPTABLES=m -+CONFIG_IP_NF_MATCH_AH=m -+CONFIG_IP_NF_MATCH_ECN=m -+CONFIG_IP_NF_MATCH_TTL=m -+CONFIG_IP_NF_FILTER=m -+CONFIG_IP_NF_TARGET_REJECT=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 -+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_RT=m -+CONFIG_IP6_NF_TARGET_HL=m -+CONFIG_IP6_NF_FILTER=m -+CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y -+CONFIG_ATM=m -+CONFIG_L2TP=m -+CONFIG_L2TP_V3=y -+CONFIG_L2TP_IP=m -+CONFIG_L2TP_ETH=m -+CONFIG_BRIDGE=m -+CONFIG_VLAN_8021Q=m -+CONFIG_VLAN_8021Q_GVRP=y -+CONFIG_ATALK=m -+CONFIG_NET_SCHED=y -+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 -+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_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_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_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_BATMAN_ADV=m -+CONFIG_OPENVSWITCH=m - CONFIG_NET_PKTGEN=m -+CONFIG_HAMRADIO=y -+CONFIG_AX25=m -+CONFIG_NETROM=m -+CONFIG_ROSE=m -+CONFIG_MKISS=m -+CONFIG_6PACK=m -+CONFIG_BPQETHER=m -+CONFIG_BAYCOM_SER_FDX=m -+CONFIG_BAYCOM_SER_HDX=m -+CONFIG_YAM=m - CONFIG_IRDA=m - CONFIG_IRLAN=m -+CONFIG_IRNET=m - CONFIG_IRCOMM=m - CONFIG_IRDA_ULTRA=y - CONFIG_IRDA_CACHE_LAST_LSAP=y -@@ -87,45 +365,88 @@ CONFIG_BT_HCIVHCI=m - CONFIG_BT_MRVL=m - CONFIG_BT_MRVL_SDIO=m - CONFIG_BT_ATH3K=m --CONFIG_CFG80211=m -+CONFIG_BT_WILINK=m -+CONFIG_CFG80211_WEXT=y - CONFIG_MAC80211=m - CONFIG_MAC80211_MESH=y - CONFIG_WIMAX=m -+CONFIG_RFKILL=m -+CONFIG_RFKILL_INPUT=y - CONFIG_NET_9P=m - CONFIG_NFC=m - CONFIG_NFC_PN533=m --CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=5 - CONFIG_BLK_DEV_LOOP=y - CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set --CONFIG_BLK_DEV_SD=m -+CONFIG_BLK_DEV_SD=y -+CONFIG_CHR_DEV_ST=m -+CONFIG_CHR_DEV_OSST=m - CONFIG_BLK_DEV_SR=m --# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_CHR_DEV_SG=m -+CONFIG_SCSI_ISCSI_ATTRS=y -+CONFIG_ISCSI_TCP=m -+CONFIG_ISCSI_BOOT_SYSFS=m - CONFIG_MD=y -+CONFIG_MD_LINEAR=m -+CONFIG_MD_RAID0=m -+CONFIG_BLK_DEV_DM=m -+CONFIG_DM_CRYPT=m -+CONFIG_DM_SNAPSHOT=m -+CONFIG_DM_MIRROR=m -+CONFIG_DM_LOG_USERSPACE=m -+CONFIG_DM_RAID=m -+CONFIG_DM_ZERO=m -+CONFIG_DM_DELAY=m - CONFIG_NETDEVICES=y -+CONFIG_BONDING=m -+CONFIG_DUMMY=m -+CONFIG_IFB=m -+CONFIG_MACVLAN=m - CONFIG_NETCONSOLE=m - CONFIG_TUN=m -+CONFIG_VETH=m -+CONFIG_ENC28J60=m - CONFIG_MDIO_BITBANG=m - CONFIG_PPP=m - CONFIG_PPP_BSDCOMP=m - CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_FILTER=y -+CONFIG_PPP_MPPE=m -+CONFIG_PPP_MULTILINK=y -+CONFIG_PPPOATM=m -+CONFIG_PPPOE=m -+CONFIG_PPPOL2TP=m - CONFIG_PPP_ASYNC=m - CONFIG_PPP_SYNC_TTY=m - CONFIG_SLIP=m - CONFIG_SLIP_COMPRESSED=y -+CONFIG_SLIP_SMART=y - CONFIG_USB_CATC=m - CONFIG_USB_KAWETH=m - CONFIG_USB_PEGASUS=m - CONFIG_USB_RTL8150=m -+CONFIG_USB_RTL8152=m - CONFIG_USB_USBNET=y - 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=y - CONFIG_USB_NET_GL620A=m -@@ -135,10 +456,13 @@ CONFIG_USB_NET_MCS7830=m - CONFIG_USB_NET_CDC_SUBSET=m - CONFIG_USB_ALI_M5632=y - CONFIG_USB_AN2720=y -+CONFIG_USB_EPSON2888=y - CONFIG_USB_KC2190=y --# CONFIG_USB_NET_ZAURUS is not set -+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 -@@ -150,8 +474,18 @@ CONFIG_USB_ZD1201=m - CONFIG_USB_NET_RNDIS_WLAN=m - CONFIG_RTL8187=m - CONFIG_MAC80211_HWSIM=m -+CONFIG_ATH_CARDS=m -+CONFIG_ATH9K=m -+CONFIG_ATH9K_HTC=m -+CONFIG_CARL9170=m -+CONFIG_ATH6KL=m -+CONFIG_ATH6KL_USB=m -+CONFIG_AR5523=m - CONFIG_B43=m -+# CONFIG_B43_PHY_N is not set - CONFIG_B43LEGACY=m -+CONFIG_BRCMFMAC=m -+CONFIG_BRCMFMAC_USB=y - CONFIG_HOSTAP=m - CONFIG_LIBERTAS=m - CONFIG_LIBERTAS_USB=m -@@ -162,7 +496,10 @@ CONFIG_RT2X00=m - CONFIG_RT2500USB=m - CONFIG_RT73USB=m - CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT3573=y - CONFIG_RT2800USB_RT53XX=y -+CONFIG_RT2800USB_RT55XX=y -+CONFIG_RT2800USB_UNKNOWN=y - CONFIG_RTL8192CU=m - CONFIG_ZD1211RW=m - CONFIG_MWIFIEX=m -@@ -174,6 +511,13 @@ CONFIG_INPUT_JOYDEV=m - CONFIG_INPUT_EVDEV=m - # CONFIG_INPUT_KEYBOARD is not set - # CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_JOYSTICK=y -+CONFIG_JOYSTICK_IFORCE=m -+CONFIG_JOYSTICK_IFORCE_USB=y -+CONFIG_JOYSTICK_XPAD=m -+CONFIG_JOYSTICK_XPAD_FF=y -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -182,6 +526,7 @@ CONFIG_INPUT_POWERMATE=m - CONFIG_INPUT_YEALINK=m - CONFIG_INPUT_CM109=m - CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m - CONFIG_INPUT_ADXL34X=m - CONFIG_INPUT_CMA3000=m - CONFIG_SERIO=m -@@ -189,22 +534,223 @@ CONFIG_SERIO_RAW=m - CONFIG_GAMEPORT=m - CONFIG_GAMEPORT_NS558=m - CONFIG_GAMEPORT_L4=m -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y - # CONFIG_LEGACY_PTYS is not set - # CONFIG_DEVKMEM is not set - CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --# CONFIG_HW_RANDOM is not set -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=m - CONFIG_RAW_DRIVER=y -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VC_SM=y - CONFIG_I2C=y - CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2708=m --CONFIG_SPI_SPIDEV=m -+CONFIG_SPI_SPIDEV=y -+CONFIG_PPS=m -+CONFIG_PPS_CLIENT_LDISC=m -+CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_ARIZONA=m -+CONFIG_W1=m -+CONFIG_W1_MASTER_DS2490=m -+CONFIG_W1_MASTER_DS2482=m -+CONFIG_W1_MASTER_DS1WM=m -+CONFIG_W1_MASTER_GPIO=m -+CONFIG_W1_SLAVE_THERM=m -+CONFIG_W1_SLAVE_SMEM=m -+CONFIG_W1_SLAVE_DS2408=m -+CONFIG_W1_SLAVE_DS2413=m -+CONFIG_W1_SLAVE_DS2406=m -+CONFIG_W1_SLAVE_DS2423=m -+CONFIG_W1_SLAVE_DS2431=m -+CONFIG_W1_SLAVE_DS2433=m -+CONFIG_W1_SLAVE_DS2760=m -+CONFIG_W1_SLAVE_DS2780=m -+CONFIG_W1_SLAVE_DS2781=m -+CONFIG_W1_SLAVE_DS28E04=m -+CONFIG_W1_SLAVE_BQ27000=m -+CONFIG_BATTERY_DS2760=m - # CONFIG_HWMON is not set -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=m -+CONFIG_UCB1400_CORE=m -+CONFIG_MFD_ARIZONA_I2C=m -+CONFIG_MFD_ARIZONA_SPI=m -+CONFIG_MFD_WM5102=y -+CONFIG_MEDIA_SUPPORT=m -+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_LIRC=m -+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_MEDIA_USB_SUPPORT=y -+CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m -+CONFIG_USB_ZR364XX=m -+CONFIG_USB_STKWEBCAM=m -+CONFIG_USB_S2255=m -+CONFIG_VIDEO_USBTV=m -+CONFIG_VIDEO_PVRUSB2=m -+CONFIG_VIDEO_HDPVR=m -+CONFIG_VIDEO_TLG2300=m -+CONFIG_VIDEO_USBVISION=m -+CONFIG_VIDEO_STK1160_COMMON=m -+CONFIG_VIDEO_STK1160_AC97=y -+CONFIG_VIDEO_GO7007=m -+CONFIG_VIDEO_GO7007_USB=m -+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m -+CONFIG_VIDEO_AU0828=m -+CONFIG_VIDEO_AU0828_RC=y -+CONFIG_VIDEO_CX231XX=m -+CONFIG_VIDEO_CX231XX_ALSA=m -+CONFIG_VIDEO_CX231XX_DVB=m -+CONFIG_VIDEO_TM6000=m -+CONFIG_VIDEO_TM6000_ALSA=m -+CONFIG_VIDEO_TM6000_DVB=m -+CONFIG_DVB_USB=m -+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_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_AS102=m -+CONFIG_VIDEO_EM28XX=m -+CONFIG_VIDEO_EM28XX_V4L2=m -+CONFIG_VIDEO_EM28XX_ALSA=m -+CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_RADIO_SI470X=y -+CONFIG_USB_SI470X=m -+CONFIG_I2C_SI470X=m -+CONFIG_RADIO_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_MA901=m -+CONFIG_RADIO_TEA5764=m -+CONFIG_RADIO_SAA7706H=m -+CONFIG_RADIO_TEF6862=m -+CONFIG_RADIO_WL1273=m -+CONFIG_RADIO_WL128X=m -+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -+CONFIG_VIDEO_UDA1342=m -+CONFIG_VIDEO_SONY_BTF_MPX=m -+CONFIG_VIDEO_TVP5150=m -+CONFIG_VIDEO_TW2804=m -+CONFIG_VIDEO_TW9903=m -+CONFIG_VIDEO_TW9906=m -+CONFIG_VIDEO_OV7640=m -+CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+# CONFIG_BACKLIGHT_GENERIC is not set - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set -@@ -227,8 +773,10 @@ CONFIG_SND_BCM2835=m - CONFIG_SND_USB_AUDIO=m - CONFIG_SND_USB_UA101=m - CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_CAIAQ_INPUT=y - CONFIG_SND_USB_6FIRE=m - CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y - CONFIG_HID_A4TECH=m - CONFIG_HID_ACRUX=m - CONFIG_HID_APPLE=m -@@ -239,6 +787,7 @@ CONFIG_HID_CYPRESS=m - CONFIG_HID_DRAGONRISE=m - CONFIG_HID_EMS_FF=m - CONFIG_HID_ELECOM=m -+CONFIG_HID_ELO=m - CONFIG_HID_EZKEY=m - CONFIG_HID_HOLTEK=m - CONFIG_HID_KEYTOUCH=m -@@ -267,9 +816,11 @@ CONFIG_HID_SUNPLUS=m - CONFIG_HID_GREENASIA=m - CONFIG_HID_SMARTJOYPLUS=m - CONFIG_HID_TOPSEED=m -+CONFIG_HID_THINGM=m - CONFIG_HID_THRUSTMASTER=m - CONFIG_HID_WACOM=m - CONFIG_HID_WIIMOTE=m -+CONFIG_HID_XINMO=m - CONFIG_HID_ZEROPLUS=m - CONFIG_HID_ZYDACRON=m - CONFIG_HID_PID=y -@@ -277,6 +828,8 @@ CONFIG_USB_HIDDEV=y - CONFIG_USB=y - CONFIG_USB_ANNOUNCE_NEW_DEVICES=y - CONFIG_USB_MON=m -+CONFIG_USB_DWCOTG=y -+CONFIG_USB_PRINTER=m - CONFIG_USB_STORAGE=y - CONFIG_USB_STORAGE_REALTEK=m - CONFIG_USB_STORAGE_DATAFAB=m -@@ -294,6 +847,9 @@ CONFIG_USB_STORAGE_ENE_UB6250=m - CONFIG_USB_UAS=m - CONFIG_USB_MDC800=m - CONFIG_USB_MICROTEK=m -+CONFIG_USBIP_CORE=m -+CONFIG_USBIP_VHCI_HCD=m -+CONFIG_USBIP_HOST=m - CONFIG_USB_SERIAL=m - CONFIG_USB_SERIAL_GENERIC=y - CONFIG_USB_SERIAL_AIRCABLE=m -@@ -311,6 +867,7 @@ 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 -@@ -319,6 +876,7 @@ CONFIG_USB_SERIAL_KEYSPAN=m - 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_NAVMAN=m -@@ -336,7 +894,10 @@ CONFIG_USB_SERIAL_XIRCOM=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 - CONFIG_USB_EMI62=m - CONFIG_USB_EMI26=m -@@ -357,12 +918,81 @@ CONFIG_USB_IOWARRIOR=m - CONFIG_USB_TEST=m - CONFIG_USB_ISIGHTFW=m - CONFIG_USB_YUREX=m -+CONFIG_USB_ATM=m -+CONFIG_USB_SPEEDTOUCH=m -+CONFIG_USB_CXACRU=m -+CONFIG_USB_UEAGLEATM=m -+CONFIG_USB_XUSBATM=m - CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SPI=m -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_GPIO=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=y -+CONFIG_LEDS_TRIGGER_GPIO=y -+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y -+CONFIG_LEDS_TRIGGER_TRANSIENT=m -+CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_RTC_CLASS=y -+# CONFIG_RTC_HCTOSYS is not set -+CONFIG_RTC_DRV_DS1307=m -+CONFIG_RTC_DRV_DS1374=m -+CONFIG_RTC_DRV_DS1672=m -+CONFIG_RTC_DRV_DS3232=m -+CONFIG_RTC_DRV_MAX6900=m -+CONFIG_RTC_DRV_RS5C372=m -+CONFIG_RTC_DRV_ISL1208=m -+CONFIG_RTC_DRV_ISL12022=m -+CONFIG_RTC_DRV_ISL12057=m -+CONFIG_RTC_DRV_X1205=m -+CONFIG_RTC_DRV_PCF2127=m -+CONFIG_RTC_DRV_PCF8523=m -+CONFIG_RTC_DRV_PCF8563=m -+CONFIG_RTC_DRV_PCF8583=m -+CONFIG_RTC_DRV_M41T80=m -+CONFIG_RTC_DRV_BQ32K=m -+CONFIG_RTC_DRV_S35390A=m -+CONFIG_RTC_DRV_FM3130=m -+CONFIG_RTC_DRV_RX8581=m -+CONFIG_RTC_DRV_RX8025=m -+CONFIG_RTC_DRV_EM3027=m -+CONFIG_RTC_DRV_RV3029C2=m -+CONFIG_RTC_DRV_M41T93=m -+CONFIG_RTC_DRV_M41T94=m -+CONFIG_RTC_DRV_DS1305=m -+CONFIG_RTC_DRV_DS1390=m -+CONFIG_RTC_DRV_MAX6902=m -+CONFIG_RTC_DRV_R9701=m -+CONFIG_RTC_DRV_RS5C348=m -+CONFIG_RTC_DRV_DS3234=m -+CONFIG_RTC_DRV_PCF2123=m -+CONFIG_RTC_DRV_RX4581=m - CONFIG_UIO=m - CONFIG_UIO_PDRV_GENIRQ=m -+CONFIG_STAGING=y -+CONFIG_PRISM2_USB=m -+CONFIG_R8712U=m -+CONFIG_R8188EU=m -+CONFIG_R8723AU=m -+CONFIG_VT6656=m -+CONFIG_SPEAKUP=m -+CONFIG_SPEAKUP_SYNTH_SOFT=m -+CONFIG_STAGING_MEDIA=y -+CONFIG_LIRC_STAGING=y -+CONFIG_LIRC_IGORPLUGUSB=m -+CONFIG_LIRC_IMON=m -+CONFIG_LIRC_RPI=m -+CONFIG_LIRC_SASEM=m -+CONFIG_LIRC_SERIAL=m - # CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m - CONFIG_EXT4_FS=y - CONFIG_EXT4_FS_POSIX_ACL=y - CONFIG_EXT4_FS_SECURITY=y -@@ -384,6 +1014,8 @@ CONFIG_BTRFS_FS=m - CONFIG_BTRFS_FS_POSIX_ACL=y - CONFIG_NILFS2_FS=m - CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m - CONFIG_AUTOFS4_FS=y - CONFIG_FUSE_FS=m - CONFIG_CUSE=m -@@ -399,18 +1031,27 @@ CONFIG_MSDOS_FS=y - CONFIG_VFAT_FS=y - CONFIG_FAT_DEFAULT_IOCHARSET="ascii" - CONFIG_NTFS_FS=m -+CONFIG_NTFS_RW=y - CONFIG_TMPFS=y - CONFIG_TMPFS_POSIX_ACL=y - CONFIG_CONFIGFS_FS=y -+CONFIG_ECRYPT_FS=m -+CONFIG_HFS_FS=m -+CONFIG_HFSPLUS_FS=m - CONFIG_SQUASHFS=m - CONFIG_SQUASHFS_XATTR=y - CONFIG_SQUASHFS_LZO=y - CONFIG_SQUASHFS_XZ=y -+CONFIG_F2FS_FS=y - CONFIG_NFS_FS=y - CONFIG_NFS_V3_ACL=y - CONFIG_NFS_V4=y -+CONFIG_NFS_SWAP=y - CONFIG_ROOT_NFS=y - CONFIG_NFS_FSCACHE=y -+CONFIG_NFSD=m -+CONFIG_NFSD_V3_ACL=y -+CONFIG_NFSD_V4=y - CONFIG_CIFS=m - CONFIG_CIFS_WEAK_PW_HASH=y - CONFIG_CIFS_XATTR=y -@@ -455,36 +1096,37 @@ CONFIG_NLS_ISO8859_14=m - CONFIG_NLS_ISO8859_15=m - CONFIG_NLS_KOI8_R=m - CONFIG_NLS_KOI8_U=m --CONFIG_NLS_UTF8=m -+CONFIG_DLM=m - CONFIG_PRINTK_TIME=y - CONFIG_BOOT_PRINTK_DELAY=y --CONFIG_DEBUG_INFO=y --CONFIG_DEBUG_STACK_USAGE=y - CONFIG_DEBUG_MEMORY_INIT=y - CONFIG_DETECT_HUNG_TASK=y - CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set - CONFIG_LATENCYTOP=y - CONFIG_IRQSOFF_TRACER=y - CONFIG_SCHED_TRACER=y - CONFIG_STACK_TRACER=y - CONFIG_BLK_DEV_IO_TRACE=y -+# CONFIG_KPROBE_EVENT is not set - CONFIG_FUNCTION_PROFILER=y - CONFIG_KGDB=y - CONFIG_KGDB_KDB=y - CONFIG_KDB_KEYBOARD=y --CONFIG_STRICT_DEVMEM=y --CONFIG_CRYPTO_AUTHENC=m -+CONFIG_CRYPTO_USER=m -+CONFIG_CRYPTO_NULL=m -+CONFIG_CRYPTO_CRYPTD=m - CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_CTS=m -+CONFIG_CRYPTO_XTS=m - CONFIG_CRYPTO_XCBC=m --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_SHA1_ARM=m - CONFIG_CRYPTO_SHA512=m - CONFIG_CRYPTO_TGR192=m - CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_AES_ARM=m - CONFIG_CRYPTO_CAST5=m - CONFIG_CRYPTO_DES=y --CONFIG_CRYPTO_DEFLATE=m - # CONFIG_CRYPTO_ANSI_CPRNG is not set - # CONFIG_CRYPTO_HW is not set - CONFIG_CRC_ITU_T=y - -From 9546603deded7c808fd5a04a691377a0cdf2fa25 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Dec 2013 22:16:19 +0000 -Subject: [PATCH 025/172] config: Enable CONFIG_MEMCG, but leave it disabled - (due to memory cost). Enable with cgroup_enable=memory. - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - kernel/cgroup.c | 23 +++++++++++++++++++++++ - mm/memcontrol.c | 1 + - 3 files changed, 25 insertions(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 6b39872..ef20985 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -19,6 +19,7 @@ CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y - CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y -diff --git a/kernel/cgroup.c b/kernel/cgroup.c -index 04cfe8a..399cea3 100644 ---- a/kernel/cgroup.c -+++ b/kernel/cgroup.c -@@ -5389,6 +5389,29 @@ static int __init cgroup_disable(char *str) - } - __setup("cgroup_disable=", cgroup_disable); - -+static int __init cgroup_enable(char *str) -+{ -+ struct cgroup_subsys *ss; -+ char *token; -+ int i; -+ -+ while ((token = strsep(&str, ",")) != NULL) { -+ if (!*token) -+ continue; -+ -+ for_each_subsys(ss, i) { -+ if (!strcmp(token, ss->name)) { -+ ss->disabled = 0; -+ printk(KERN_INFO "Enabling %s control group" -+ " subsystem\n", ss->name); -+ break; -+ } -+ } -+ } -+ return 1; -+} -+__setup("cgroup_enable=", cgroup_enable); -+ - static int __init cgroup_set_legacy_files_on_dfl(char *str) - { - printk("cgroup: using legacy files on the default hierarchy\n"); -diff --git a/mm/memcontrol.c b/mm/memcontrol.c -index 2f6893c..8778eba 100644 ---- a/mm/memcontrol.c -+++ b/mm/memcontrol.c -@@ -5398,6 +5398,7 @@ struct cgroup_subsys memory_cgrp_subsys = { - .bind = mem_cgroup_bind, - .legacy_cftypes = mem_cgroup_files, - .early_init = 0, -+ .disabled = 1, - }; - - #ifdef CONFIG_MEMCG_SWAP - -From c73af020d71c020a59390d693ce357e0052cf457 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:46:42 +0100 -Subject: [PATCH 026/172] Add FIQ patch to dwc_otg driver. Enable with - dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks - to Gordon and Costas - -Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005. - -Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh - -Make sure we wait for the reset to finish - -dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel - memory corruption, escalating to OOPS under high USB load. - -dwc_otg: Fix unsafe access of QTD during URB enqueue - -In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the -transaction could complete almost immediately after the qtd was assigned -to a host channel during URB enqueue, which meant the qtd pointer was no -longer valid having been completed and removed. Usually, this resulted in -an OOPS during URB submission. By predetermining whether transactions -need to be queued or not, this unsafe pointer access is avoided. - -This bug was only evident on the Pi model A where a device was attached -that had no periodic endpoints (e.g. USB pendrive or some wlan devices). - -dwc_otg: Fix incorrect URB allocation error handling - -If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS -because for some reason a member of the *unallocated* struct was set to -zero. Error handling changed to fail correctly. - -dwc_otg: fix potential use-after-free case in interrupt handler - -If a transaction had previously aborted, certain interrupts are -enabled to track error counts and reset where necessary. On IN -endpoints the host generates an ACK interrupt near-simultaneously -with completion of transfer. In the case where this transfer had -previously had an error, this results in a use-after-free on -the QTD memory space with a 1-byte length being overwritten to -0x00. - -dwc_otg: add handling of SPLIT transaction data toggle errors - -Previously a data toggle error on packets from a USB1.1 device behind -a TT would result in the Pi locking up as the driver never handled -the associated interrupt. Patch adds basic retry mechanism and -interrupt acknowledgement to cater for either a chance toggle error or -for devices that have a broken initial toggle state (FT8U232/FT232BM). - -dwc_otg: implement tasklet for returning URBs to usbcore hcd layer - -The dwc_otg driver interrupt handler for transfer completion will spend -a very long time with interrupts disabled when a URB is completed - -this is because usb_hcd_giveback_urb is called from within the handler -which for a USB device driver with complicated processing (e.g. webcam) -will take an exorbitant amount of time to complete. This results in -missed completion interrupts for other USB packets which lead to them -being dropped due to microframe overruns. - -This patch splits returning the URB to the usb hcd layer into a -high-priority tasklet. This will have most benefit for isochronous IN -transfers but will also have incidental benefit where multiple periodic -devices are active at once. - -dwc_otg: fix NAK holdoff and allow on split transactions only - -This corrects a bug where if a single active non-periodic endpoint -had at least one transaction in its qh, on frnum == MAX_FRNUM the qh -would get skipped and never get queued again. This would result in -a silent device until error detection (automatic or otherwise) would -either reset the device or flush and requeue the URBs. - -Additionally the NAK holdoff was enabled for all transactions - this -would potentially stall a HS endpoint for 1ms if a previous error state -enabled this interrupt and the next response was a NAK. Fix so that -only split transactions get held off. - -dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler - -usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it -asynchronously in the tasklet was not safe (regression in -c4564d4a1a0a9b10d4419e48239f5d99e88d2667). - -This change unlinks it from the endpoint prior to queueing it for handling in -the tasklet, and also adds a check to ensure the urb is OK to be unlinked -before doing so. - -NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb -when a USB device was unplugged/replugged during data transfer. This effect -was reproduced using automated USB port power control, hundreds of replug -events were performed during active transfers to confirm that the problem was -eliminated. - -USB fix using a FIQ to implement split transactions - -This commit adds a FIQ implementaion that schedules -the split transactions using a FIQ so we don't get -held off by the interrupt latency of Linux - -dwc_otg: fix device attributes and avoid kernel warnings on boot - -dcw_otg: avoid logging function that can cause panics - -See: https://github.com/raspberrypi/firmware/issues/21 -Thanks to cleverca22 for fix - -dwc_otg: mask correct interrupts after transaction error recovery - -The dwc_otg driver will unmask certain interrupts on a transaction -that previously halted in the error state in order to reset the -QTD error count. The various fine-grained interrupt handlers do not -consider that other interrupts besides themselves were unmasked. - -By disabling the two other interrupts only ever enabled in DMA mode -for this purpose, we can avoid unnecessary function calls in the -IRQ handler. This will also prevent an unneccesary FIQ interrupt -from being generated if the FIQ is enabled. - -dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ - -In the case of a transaction to a device that had previously aborted -due to an error, several interrupts are enabled to reset the error -count when a device responds. This has the side-effect of making the -FIQ thrash because the hardware will generate multiple instances of -a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK -on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the -associated interrupts. - -Additionally, on non-split transactions make sure that only unmasked -interrupts are cleared. This caused a hard-to-trigger but serious -race condition when you had the combination of an endpoint awaiting -error recovery and a transaction completed on an endpoint - due to -the sequencing and timing of interrupts generated by the dwc_otg core, -it was possible to confuse the IRQ handler. - -Fix function tracing - -dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue - -dwc_otg: prevent OOPSes during device disconnects - -The dwc_otg_urb_enqueue function is thread-unsafe. In particular the -access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and -friends does not occur within a critical section and so if a device -was unplugged during activity there was a high chance that the -usbcore hub_thread would try to disable the endpoint with partially- -formed entries in the URB queue. This would result in BUG() or null -pointer dereferences. - -Fix so that access of urb->hcpriv, enqueuing to the hardware and -adding to usbcore endpoint URB lists is contained within a single -critical section. - -dwc_otg: prevent BUG() in TT allocation if hub address is > 16 - -A fixed-size array is used to track TT allocation. This was -previously set to 16 which caused a crash because -dwc_otg_hcd_allocate_port would read past the end of the array. - -This was hit if a hub was plugged in which enumerated as addr > 16, -due to previous device resets or unplugs. - -Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows -to a large size if 128 hub addresses are supported. This field is -for debug only for tracking which frame an allocate happened in. - -dwc_otg: make channel halts with unknown state less damaging - -If the IRQ received a channel halt interrupt through the FIQ -with no other bits set, the IRQ would not release the host -channel and never complete the URB. - -Add catchall handling to treat as a transaction error and retry. - -dwc_otg: fiq_split: use TTs with more granularity - -This fixes certain issues with split transaction scheduling. - -- Isochronous multi-packet OUT transactions now hog the TT until - they are completed - this prevents hubs aborting transactions - if they get a periodic start-split out-of-order -- Don't perform TT allocation on non-periodic endpoints - this - allows simultaneous use of the TT's bulk/control and periodic - transaction buffers - -This commit will mainly affect USB audio playback. - -dwc_otg: fix potential sleep while atomic during urb enqueue - -Fixes a regression introduced with eb1b482a. Kmalloc called from -dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have -the GPF_ATOMIC flag set. Force this flag when inside the larger -critical section. - -dwc_otg: make fiq_split_enable imply fiq_fix_enable - -Failing to set up the FIQ correctly would result in -"IRQ 32: nobody cared" errors in dmesg. - -dwc_otg: prevent crashes on host port disconnects - -Fix several issues resulting in crashes or inconsistent state -if a Model A root port was disconnected. - -- Clean up queue heads properly in kill_urbs_in_qh_list by - removing the empty QHs from the schedule lists -- Set the halt status properly to prevent IRQ handlers from - using freed memory -- Add fiq_split related cleanup for saved registers -- Make microframe scheduling reclaim host channels if - active during a disconnect -- Abort URBs with -ESHUTDOWN status response, informing - device drivers so they respond in a more correct fashion - and don't try to resubmit URBs -- Prevent IRQ handlers from attempting to handle channel - interrupts if the associated URB was dequeued (and the - driver state was cleared) - -dwc_otg: prevent leaking URBs during enqueue - -A dwc_otg_urb would get leaked if the HCD enqueue function -failed for any reason. Free the URB at the appropriate points. - -dwc_otg: Enable NAK holdoff for control split transactions - -Certain low-speed devices take a very long time to complete a -data or status stage of a control transaction, producing NAK -responses until they complete internal processing - the USB2.0 -spec limit is up to 500mS. This causes the same type of interrupt -storm as seen with USB-serial dongles prior to c8edb238. - -In certain circumstances, usually while booting, this interrupt -storm could cause SD card timeouts. - -dwc_otg: Fix for occasional lockup on boot when doing a USB reset - -dwc_otg: Don't issue traffic to LS devices in FS mode - -Issuing low-speed packets when the root port is in full-speed mode -causes the root port to stop responding. Explicitly fail when -enqueuing URBs to a LS endpoint on a FS bus. - -Fix ARM architecture issue with local_irq_restore() - -If local_fiq_enable() is called before a local_irq_restore(flags) where -the flags variable has the F bit set, the FIQ will be erroneously disabled. - -Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR. - -Also fix some of the hacks previously implemented for previous dwc_otg -incarnations. ---- - arch/arm/Kconfig | 1 + - arch/arm/include/asm/irqflags.h | 16 +- - arch/arm/kernel/fiqasm.S | 4 + - arch/arm/mach-bcm2708/armctrl.c | 19 +- - arch/arm/mach-bcm2708/bcm2708.c | 29 +- - arch/arm/mach-bcm2708/include/mach/irqs.h | 153 ++--- - .../usb/host/dwc_common_port/dwc_common_linux.c | 11 + - drivers/usb/host/dwc_common_port/dwc_list.h | 14 +- - drivers/usb/host/dwc_common_port/dwc_os.h | 2 + - drivers/usb/host/dwc_otg/Makefile | 1 + - drivers/usb/host/dwc_otg/dwc_otg_attr.c | 14 +- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +- - drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 1 + - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 52 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 303 +++++++-- - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 37 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 3 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 5 + - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 705 ++++++++++++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 159 +++-- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 53 +- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 ++++ - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 ++ - drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 3 + - drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 2 +- - 25 files changed, 1544 insertions(+), 251 deletions(-) - create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c - create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index fdfb230..f607868 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -381,6 +381,7 @@ config ARCH_BCM2708 - select ARM_ERRATA_411920 - select MACH_BCM2708 - select VC4 -+ select FIQ - help - This enables support for Broadcom BCM2708 boards. - -diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h -index 3b763d6..5770408 100644 ---- a/arch/arm/include/asm/irqflags.h -+++ b/arch/arm/include/asm/irqflags.h -@@ -145,12 +145,22 @@ static inline unsigned long arch_local_save_flags(void) - } - - /* -- * restore saved IRQ & FIQ state -+ * restore saved IRQ state - */ - static inline void arch_local_irq_restore(unsigned long flags) - { -- asm volatile( -- " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore" -+ unsigned long temp = 0; -+ flags &= ~(1 << 6); -+ asm volatile ( -+ " mrs %0, cpsr" -+ : "=r" (temp) -+ : -+ : "memory", "cc"); -+ /* Preserve FIQ bit */ -+ temp &= (1 << 6); -+ flags = flags | temp; -+ asm volatile ( -+ " msr cpsr_c, %0 @ local_irq_restore" - : - : "r" (flags) - : "memory", "cc"); -diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S -index 8dd26e1..eef4847 100644 ---- a/arch/arm/kernel/fiqasm.S -+++ b/arch/arm/kernel/fiqasm.S -@@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs) - mov r0, r0 @ avoid hazard prior to ARMv4 - ret lr - ENDPROC(__get_fiq_regs) -+ -+ENTRY(__FIQ_Branch) -+ mov pc, r8 -+ENDPROC(__FIQ_Branch) -diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c -index ef1c8d5..96fa9b9 100644 ---- a/arch/arm/mach-bcm2708/armctrl.c -+++ b/arch/arm/mach-bcm2708/armctrl.c -@@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_data *d) - 0 - }; - -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -- writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ if (d->irq >= FIQ_START) { -+ writel(0, __io_address(ARM_IRQ_FAST)); -+ } else { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } - } - - static void armctrl_unmask_irq(struct irq_data *d) -@@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct irq_data *d) - 0 - }; - -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -- writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ if (d->irq >= FIQ_START) { -+ unsigned int data = -+ (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); -+ } else { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } - } - - #if defined(CONFIG_PM) -@@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, - } - - armctrl_pm_register(base, irq_start, resume_sources); -+ init_FIQ(FIQ_START); - return 0; - } -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 7dd89a7f..dc59a6b 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -321,12 +321,32 @@ static struct resource bcm2708_usb_resources[] = { - .flags = IORESOURCE_MEM, - }, - [1] = { -- .start = IRQ_USB, -- .end = IRQ_USB, -+ .start = MPHI_BASE, -+ .end = MPHI_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [2] = { -+ .start = IRQ_HOSTPORT, -+ .end = IRQ_HOSTPORT, - .flags = IORESOURCE_IRQ, - }, - }; - -+bool fiq_fix_enable = true; -+ -+static struct resource bcm2708_usb_resources_no_fiq_fix[] = { -+ [0] = { -+ .start = USB_BASE, -+ .end = USB_BASE + SZ_128K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ - static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_usb_device = { -@@ -681,6 +701,11 @@ void __init bcm2708_init(void) - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -+ if (!fiq_fix_enable) -+ { -+ bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix; -+ bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix); -+ } - bcm_register_device(&bcm2708_usb_device); - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); -diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h -index 3a88a1a..45152ed 100644 ---- a/arch/arm/mach-bcm2708/include/mach/irqs.h -+++ b/arch/arm/mach-bcm2708/include/mach/irqs.h -@@ -106,87 +106,90 @@ - #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) - #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) - -+#define FIQ_START HARD_IRQS -+ - /* - * FIQ interrupts definitions are the same as the INT definitions. - */ --#define FIQ_TIMER0 INT_TIMER0 --#define FIQ_TIMER1 INT_TIMER1 --#define FIQ_TIMER2 INT_TIMER2 --#define FIQ_TIMER3 INT_TIMER3 --#define FIQ_CODEC0 INT_CODEC0 --#define FIQ_CODEC1 INT_CODEC1 --#define FIQ_CODEC2 INT_CODEC2 --#define FIQ_JPEG INT_JPEG --#define FIQ_ISP INT_ISP --#define FIQ_USB INT_USB --#define FIQ_3D INT_3D --#define FIQ_TRANSPOSER INT_TRANSPOSER --#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 --#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 --#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 --#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 --#define FIQ_DMA0 INT_DMA0 --#define FIQ_DMA1 INT_DMA1 --#define FIQ_DMA2 INT_DMA2 --#define FIQ_DMA3 INT_DMA3 --#define FIQ_DMA4 INT_DMA4 --#define FIQ_DMA5 INT_DMA5 --#define FIQ_DMA6 INT_DMA6 --#define FIQ_DMA7 INT_DMA7 --#define FIQ_DMA8 INT_DMA8 --#define FIQ_DMA9 INT_DMA9 --#define FIQ_DMA10 INT_DMA10 --#define FIQ_DMA11 INT_DMA11 --#define FIQ_DMA12 INT_DMA12 --#define FIQ_AUX INT_AUX --#define FIQ_ARM INT_ARM --#define FIQ_VPUDMA INT_VPUDMA --#define FIQ_HOSTPORT INT_HOSTPORT --#define FIQ_VIDEOSCALER INT_VIDEOSCALER --#define FIQ_CCP2TX INT_CCP2TX --#define FIQ_SDC INT_SDC --#define FIQ_DSI0 INT_DSI0 --#define FIQ_AVE INT_AVE --#define FIQ_CAM0 INT_CAM0 --#define FIQ_CAM1 INT_CAM1 --#define FIQ_HDMI0 INT_HDMI0 --#define FIQ_HDMI1 INT_HDMI1 --#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 --#define FIQ_I2CSPISLV INT_I2CSPISLV --#define FIQ_DSI1 INT_DSI1 --#define FIQ_PWA0 INT_PWA0 --#define FIQ_PWA1 INT_PWA1 --#define FIQ_CPR INT_CPR --#define FIQ_SMI INT_SMI --#define FIQ_GPIO0 INT_GPIO0 --#define FIQ_GPIO1 INT_GPIO1 --#define FIQ_GPIO2 INT_GPIO2 --#define FIQ_GPIO3 INT_GPIO3 --#define FIQ_I2C INT_I2C --#define FIQ_SPI INT_SPI --#define FIQ_I2SPCM INT_I2SPCM --#define FIQ_SDIO INT_SDIO --#define FIQ_UART INT_UART --#define FIQ_SLIMBUS INT_SLIMBUS --#define FIQ_VEC INT_VEC --#define FIQ_CPG INT_CPG --#define FIQ_RNG INT_RNG --#define FIQ_ARASANSDIO INT_ARASANSDIO --#define FIQ_AVSPMON INT_AVSPMON -+#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) -+#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) -+#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) -+#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) -+#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) -+#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) -+#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) -+#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) -+#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) -+#define FIQ_USB (FIQ_START+INTERRUPT_USB) -+#define FIQ_3D (FIQ_START+INTERRUPT_3D) -+#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) -+#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) -+#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) -+#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) -+#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) -+#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) -+#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) -+#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) -+#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) -+#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) -+#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) -+#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) -+#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) -+#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) -+#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) -+#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) -+#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) -+#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) -+#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) -+#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) -+#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) -+#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) -+#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) -+#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) -+#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) -+#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) -+#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) -+#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) -+#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) -+#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) -+#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) -+#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) -+#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) -+#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) -+#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) -+#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) -+#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) -+#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) -+#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) -+#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) -+#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) -+#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) -+#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) -+#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) -+#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) -+#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) -+#define FIQ_UART (FIQ_START+INTERRUPT_UART) -+#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) -+#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) -+#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) -+#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) -+#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) -+#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) - --#define FIQ_ARM_TIMER INT_ARM_TIMER --#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX --#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 --#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 --#define FIQ_VPU0_HALTED INT_VPU0_HALTED --#define FIQ_VPU1_HALTED INT_VPU1_HALTED --#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 --#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 --#define FIQ_PENDING1 INT_PENDING1 --#define FIQ_PENDING2 INT_PENDING2 -+#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) -+#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) -+#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) -+#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) -+#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) -+#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) -+#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) -+#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) -+#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) -+#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) - - #define HARD_IRQS (64 + 21) --#define GPIO_IRQ_START (HARD_IRQS) -+#define FIQ_IRQS (64 + 21) -+#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) - #define GPIO_IRQS (32*5) - #define SPARE_ALLOC_IRQS 64 - #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) -diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -index 1668f10..5c50a8b 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c -+++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -580,7 +580,13 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) - - void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) - { -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ local_fiq_disable(); - writel((readl(reg) & ~clear_mask) | set_mask, reg); -+ local_fiq_enable(); -+ local_irq_restore(flags); - } - - #if 0 -@@ -995,6 +1001,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task) - tasklet_schedule(&task->t); - } - -+void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task) -+{ -+ tasklet_hi_schedule(&task->t); -+} -+ - - /* workqueues - - run in process context (can sleep) -diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h -index 89cc325..4ce560d 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_list.h -+++ b/drivers/usb/host/dwc_common_port/dwc_list.h -@@ -384,17 +384,17 @@ struct { \ - #define DWC_TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - #define DWC_TAILQ_EMPTY(head) \ -- (TAILQ_FIRST(head) == TAILQ_END(head)) -+ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head)) - - #define DWC_TAILQ_FOREACH(var, head, field) \ -- for((var) = TAILQ_FIRST(head); \ -- (var) != TAILQ_END(head); \ -- (var) = TAILQ_NEXT(var, field)) -+ for ((var) = DWC_TAILQ_FIRST(head); \ -+ (var) != DWC_TAILQ_END(head); \ -+ (var) = DWC_TAILQ_NEXT(var, field)) - - #define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ -- for((var) = TAILQ_LAST(head, headname); \ -- (var) != TAILQ_END(head); \ -- (var) = TAILQ_PREV(var, headname, field)) -+ for ((var) = DWC_TAILQ_LAST(head, headname); \ -+ (var) != DWC_TAILQ_END(head); \ -+ (var) = DWC_TAILQ_PREV(var, headname, field)) - - /* - * Tail queue functions. -diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h -index 8117731..a2bbe23 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_os.h -+++ b/drivers/usb/host/dwc_common_port/dwc_os.h -@@ -982,6 +982,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task); - extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); - #define dwc_task_schedule DWC_TASK_SCHEDULE - -+extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task); -+#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE - - /** @name Timer - * -diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile -index 236c47c..a56f193 100644 ---- a/drivers/usb/host/dwc_otg/Makefile -+++ b/drivers/usb/host/dwc_otg/Makefile -@@ -36,6 +36,7 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o - dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o - dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o - dwc_otg-objs += dwc_otg_adp.o -+dwc_otg-objs += dwc_otg_mphi_fix.o - ifneq ($(CFI),) - dwc_otg-objs += dwc_otg_cfi.o - endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_attr.c b/drivers/usb/host/dwc_otg/dwc_otg_attr.c -index fab2961..9da0c92 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_attr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c -@@ -909,7 +909,7 @@ static ssize_t regdump_show(struct device *_dev, - return sprintf(buf, "Register Dump\n"); - } - --DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); -+DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0); - - /** - * Dump global registers and either host or device registers (depending on the -@@ -920,12 +920,12 @@ static ssize_t spramdump_show(struct device *_dev, - { - dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev); - -- dwc_otg_dump_spram(otg_dev->core_if); -+ //dwc_otg_dump_spram(otg_dev->core_if); - - return sprintf(buf, "SPRAM Dump\n"); - } - --DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); -+DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0); - - /** - * Dump the current hcd state. -@@ -940,7 +940,7 @@ static ssize_t hcddump_show(struct device *_dev, - return sprintf(buf, "HCD Dump\n"); - } - --DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); -+DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0); - - /** - * Dump the average frame remaining at SOF. This can be used to -@@ -958,7 +958,7 @@ static ssize_t hcd_frrem_show(struct device *_dev, - return sprintf(buf, "HCD Dump Frame Remaining\n"); - } - --DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); -+DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0); - - /** - * Displays the time required to read the GNPTXFSIZ register many times (the -@@ -986,7 +986,7 @@ static ssize_t rd_reg_test_show(struct device *_dev, - RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); - } - --DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); -+DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0); - - /** - * Displays the time required to write the GNPTXFSIZ register many times (the -@@ -1014,7 +1014,7 @@ static ssize_t wr_reg_test_show(struct device *_dev, - RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); - } - --DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); -+DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0); - - #ifdef CONFIG_USB_DWC_OTG_LPM - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 59fc862..2f8b3bd 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -45,6 +45,7 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_pcd.h" - #include "dwc_otg_hcd.h" -+#include "dwc_otg_mphi_fix.h" - - #ifdef DEBUG - inline const char *op_state_str(dwc_otg_core_if_t * core_if) -@@ -1318,7 +1319,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) - /** - * This function returns the Core Interrupt register. - */ --static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk) - { - gahbcfg_data_t gahbcfg = {.d32 = 0 }; - gintsts_data_t gintsts; -@@ -1335,26 +1336,45 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) - gintmsk_common.b.lpmtranrcvd = 1; - #endif - gintmsk_common.b.restoredone = 1; -- /** @todo: The port interrupt occurs while in device -- * mode. Added code to CIL to clear the interrupt for now! -- */ -- gintmsk_common.b.portintr = 1; -- -+ if(dwc_otg_is_device_mode(core_if)) -+ { -+ /** @todo: The port interrupt occurs while in device -+ * mode. Added code to CIL to clear the interrupt for now! -+ */ -+ gintmsk_common.b.portintr = 1; -+ } - gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); - gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -+ { -+ unsigned long flags; -+ -+ // Re-enable the saved interrupts -+ local_irq_save(flags); -+ local_fiq_disable(); -+ gintmsk.d32 |= gintmsk_common.d32; -+ gintsts_saved.d32 &= ~gintmsk_common.d32; -+ reenable_gintmsk->d32 = gintmsk.d32; -+ local_irq_restore(flags); -+ } -+ - gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); - - #ifdef DEBUG - /* if any common interrupts set */ - if (gintsts.d32 & gintmsk_common.d32) { -- DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", -+ DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n", - gintsts.d32, gintmsk.d32); - } - #endif -- if (gahbcfg.b.glblintrmsk) -+ if (!fiq_fix_enable){ -+ if (gahbcfg.b.glblintrmsk) -+ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -+ else -+ return 0; -+ } -+ else { - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -- else -- return 0; -+ } - - } - -@@ -1386,6 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - { - int retval = 0; - gintsts_data_t gintsts; -+ gintmsk_data_t reenable_gintmsk; - gpwrdn_data_t gpwrdn = {.d32 = 0 }; - dwc_otg_device_t *otg_dev = dev; - dwc_otg_core_if_t *core_if = otg_dev->core_if; -@@ -1407,7 +1428,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - - if (core_if->hibernation_suspend <= 0) { -- gintsts.d32 = dwc_otg_read_common_intr(core_if); -+ gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk); - - if (gintsts.b.modemismatch) { - retval |= dwc_otg_handle_mode_mismatch_intr(core_if); -@@ -1504,8 +1525,12 @@ int32_t dwc_otg_handle_common_intr(void *dev) - gintsts.b.portintr = 1; - DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); - retval |= 1; -+ reenable_gintmsk.b.portintr = 1; - - } -+ -+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32); -+ - } else { - DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -index 8900318..ccc24e0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -@@ -49,6 +49,7 @@ static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new) - return old; - } - -+#define DBG_USER (0x1) - /** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ - #define DBG_CIL (0x2) - /** When debug level has the DBG_CILV bit set, display CIL Verbose debug -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -index ac2c846..f06c3d22 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -64,6 +64,8 @@ bool microframe_schedule=true; - - static const char dwc_driver_name[] = "dwc_otg"; - -+extern void* dummy_send; -+ - extern int pcd_init( - #ifdef LM_INTERFACE - struct lm_device *_dev -@@ -238,6 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = { - .adp_enable = -1, - }; - -+//Global variable to switch the fiq fix on or off (declared in bcm2708.c) -+extern bool fiq_fix_enable; -+// Global variable to enable the split transaction fix -+bool fiq_split_enable = true; -+//Global variable to switch the nak holdoff on or off -+bool nak_holdoff_enable = true; -+ -+ - /** - * This function shows the Driver Version. - */ -@@ -779,17 +789,33 @@ static int dwc_otg_driver_probe( - _dev->resource->start, - _dev->resource->end - _dev->resource->start + 1); - #if 1 -- if (!request_mem_region(_dev->resource->start, -- _dev->resource->end - _dev->resource->start + 1, -+ if (!request_mem_region(_dev->resource[0].start, -+ _dev->resource[0].end - _dev->resource[0].start + 1, - "dwc_otg")) { - dev_dbg(&_dev->dev, "error reserving mapped memory\n"); - retval = -EFAULT; - goto fail; - } - -- dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start, -- _dev->resource->end - -- _dev->resource->start+1); -+ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, -+ _dev->resource[0].end - -+ _dev->resource[0].start+1); -+ if (fiq_fix_enable) -+ { -+ if (!request_mem_region(_dev->resource[1].start, -+ _dev->resource[1].end - _dev->resource[1].start + 1, -+ "dwc_otg")) { -+ dev_dbg(&_dev->dev, "error reserving mapped memory\n"); -+ retval = -EFAULT; -+ goto fail; -+ } -+ -+ dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, -+ _dev->resource[1].end - -+ _dev->resource[1].start + 1); -+ dummy_send = (void *) kmalloc(16, GFP_ATOMIC); -+ } -+ - #else - { - struct map_desc desc = { -@@ -1044,6 +1070,12 @@ static int __init dwc_otg_driver_init(void) - int retval = 0; - int error; - struct device_driver *drv; -+ -+ if(fiq_split_enable && !fiq_fix_enable) { -+ printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n"); -+ fiq_fix_enable = 1; -+ } -+ - printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, - DWC_DRIVER_VERSION, - #ifdef LM_INTERFACE -@@ -1063,6 +1095,9 @@ static int __init dwc_otg_driver_init(void) - printk(KERN_ERR "%s retval=%d\n", __func__, retval); - return retval; - } -+ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled"); - - error = driver_create_file(drv, &driver_attr_version); - #ifdef DEBUG -@@ -1343,6 +1378,13 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); - module_param(microframe_schedule, bool, 0444); - MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); - -+module_param(fiq_fix_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix"); -+module_param(nak_holdoff_enable, bool, 0444); -+MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff"); -+module_param(fiq_split_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions"); -+ - /** @page "Module Parameters" - * - * The following parameters may be specified when starting the module. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 1e89549..986d361 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -40,10 +40,14 @@ - * header file. - */ - -+#include -+#include -+ - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" - --extern bool microframe_schedule; -+extern bool microframe_schedule, nak_holdoff_enable; - - //#define DEBUG_HOST_CHANNELS - #ifdef DEBUG_HOST_CHANNELS -@@ -53,6 +57,13 @@ static int last_sel_trans_num_avail_hc_at_start = 0; - static int last_sel_trans_num_avail_hc_at_end = 0; - #endif /* DEBUG_HOST_CHANNELS */ - -+extern int g_next_sched_frame, g_np_count, g_np_sent; -+ -+extern haint_data_t haint_saved; -+extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; -+extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; -+extern gintsts_data_t ginsts_saved; -+ - dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) - { - return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); -@@ -162,31 +173,43 @@ static void del_timers(dwc_otg_hcd_t * hcd) - - /** - * Processes all the URBs in a single list of QHs. Completes them with -- * -ETIMEDOUT and frees the QTD. -+ * -ESHUTDOWN and frees the QTD. - */ - static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) - { -- dwc_list_link_t *qh_item; -+ dwc_list_link_t *qh_item, *qh_tmp; - dwc_otg_qh_t *qh; - dwc_otg_qtd_t *qtd, *qtd_tmp; - -- DWC_LIST_FOREACH(qh_item, qh_list) { -+ DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) { - qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); - DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, - &qh->qtd_list, qtd_list_entry) { - qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - if (qtd->urb != NULL) { - hcd->fops->complete(hcd, qtd->urb->priv, -- qtd->urb, -DWC_E_TIMEOUT); -+ qtd->urb, -DWC_E_SHUTDOWN); - dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); - } - - } -+ if(qh->channel) { -+ /* Using hcchar.chen == 1 is not a reliable test. -+ * It is possible that the channel has already halted -+ * but not yet been through the IRQ handler. -+ */ -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); -+ if(microframe_schedule) -+ hcd->available_host_channels++; -+ qh->channel = NULL; -+ } -+ dwc_otg_hcd_qh_remove(hcd, qh); - } - } - - /** -- * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic -+ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic - * and periodic schedules. The QTD associated with each URB is removed from - * the schedule and freed. This function may be called when a disconnect is - * detected or when the HCD is being stopped. -@@ -272,7 +295,8 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - */ - dwc_otg_hcd->flags.b.port_connect_status_change = 1; - dwc_otg_hcd->flags.b.port_connect_status = 0; -- -+ if(fiq_fix_enable) -+ local_fiq_disable(); - /* - * Shutdown any transfers in process by clearing the Tx FIFO Empty - * interrupt mask and status bits and disabling subsequent host -@@ -368,8 +392,22 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - channel->qh = NULL; - } - } -+ if(fiq_split_enable) { -+ for(i=0; i < 128; i++) { -+ dwc_otg_hcd->hub_port[i] = 0; -+ } -+ haint_saved.d32 = 0; -+ for(i=0; i < MAX_EPS_CHANNELS; i++) { -+ hcint_saved[i].d32 = 0; -+ hcintmsk_saved[i].d32 = 0; -+ } -+ } -+ - } - -+ if(fiq_fix_enable) -+ local_fiq_enable(); -+ - if (dwc_otg_hcd->fops->disconnect) { - dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); - } -@@ -407,6 +445,7 @@ static int dwc_otg_hcd_sleep_cb(void *p) - } - #endif - -+ - /** - * HCD Callback function for Remote Wakeup. - * -@@ -457,10 +496,12 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle, - int atomic_alloc) - { -- dwc_irqflags_t flags; - int retval = 0; -+ uint8_t needs_scheduling = 0; -+ dwc_otg_transaction_type_e tr_type; - dwc_otg_qtd_t *qtd; - gintmsk_data_t intr_mask = {.d32 = 0 }; -+ hprt0_data_t hprt0 = { .d32 = 0 }; - - #ifdef DEBUG /* integrity checks (Broadcom) */ - if (NULL == hcd->core_if) { -@@ -475,6 +516,16 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - return -DWC_E_NO_DEVICE; - } - -+ /* Some core configurations cannot support LS traffic on a FS root port */ -+ if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) && -+ (hcd->core_if->hwcfg2.b.fs_phy_type == 1) && -+ (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) { -+ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); -+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) { -+ return -DWC_E_NO_DEVICE; -+ } -+ } -+ - qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc); - if (qtd == NULL) { - DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); -@@ -490,32 +541,27 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - return -DWC_E_NO_MEMORY; - } - #endif -- retval = -- dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); -+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -+ if(!intr_mask.b.sofintr) needs_scheduling = 1; -+ if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) -+ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -+ needs_scheduling = 0; -+ -+ retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); - // creates a new queue in ep_handle if it doesn't exist already - if (retval < 0) { - DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " - "Error status %d\n", retval); - dwc_otg_hcd_qtd_free(qtd); -- } else { -- qtd->qh = *ep_handle; -+ return retval; - } -- intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -- if (!intr_mask.b.sofintr && retval == 0) { -- dwc_otg_transaction_type_e tr_type; -- if ((qtd->qh->ep_type == UE_BULK) -- && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { -- /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -- return 0; -- } -- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ -+ if(needs_scheduling) { - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); - } -- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); - } -- - return retval; - } - -@@ -524,6 +570,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - { - dwc_otg_qh_t *qh; - dwc_otg_qtd_t *urb_qtd; -+ BUG_ON(!hcd); -+ BUG_ON(!dwc_otg_urb); - - #ifdef DEBUG /* integrity checks (Broadcom) */ - -@@ -540,14 +588,17 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - return -DWC_E_INVALID; - } - urb_qtd = dwc_otg_urb->qtd; -+ BUG_ON(!urb_qtd); - if (urb_qtd->qh == NULL) { - DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); - return -DWC_E_INVALID; - } - #else - urb_qtd = dwc_otg_urb->qtd; -+ BUG_ON(!urb_qtd); - #endif - qh = urb_qtd->qh; -+ BUG_ON(!qh); - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - if (urb_qtd->in_process) { - dump_channel_info(hcd, qh); -@@ -571,6 +622,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - */ - dwc_otg_hc_halt(hcd->core_if, qh->channel, - DWC_OTG_HC_XFER_URB_DEQUEUE); -+ -+ dwc_otg_hcd_release_port(hcd, qh); - } - } - -@@ -687,6 +740,33 @@ static void reset_tasklet_func(void *data) - dwc_otg_hcd->flags.b.port_reset_change = 1; - } - -+static void completion_tasklet_func(void *ptr) -+{ -+ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr; -+ struct urb *urb; -+ urb_tq_entry_t *item; -+ dwc_irqflags_t flags; -+ -+ /* This could just be spin_lock_irq */ -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) { -+ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list); -+ urb = item->urb; -+ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item, -+ urb_tq_entries); -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ DWC_FREE(item); -+ -+ usb_hcd_giveback_urb(hcd->priv, urb, urb->status); -+ -+ fiq_print(FIQDBG_PORTHUB, "COMPLETE"); -+ -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ return; -+} -+ - static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) - { - dwc_list_link_t *item; -@@ -819,12 +899,14 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) - } else if (dwc_otg_hcd->status_buf != NULL) { - DWC_FREE(dwc_otg_hcd->status_buf); - } -+ DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock); - DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); - /* Set core_if's lock pointer to NULL */ - dwc_otg_hcd->core_if->lock = NULL; - - DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); - DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); -+ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); - - #ifdef DWC_DEV_SRPCAP - if (dwc_otg_hcd->core_if->power_down == 2 && -@@ -874,7 +956,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - DWC_LIST_INIT(&hcd->periodic_sched_ready); - DWC_LIST_INIT(&hcd->periodic_sched_assigned); - DWC_LIST_INIT(&hcd->periodic_sched_queued); -- -+ DWC_TAILQ_INIT(&hcd->completed_urb_list); - /* - * Create a host channel descriptor for each host channel implemented - * in the controller. Initialize the channel descriptor array. -@@ -912,6 +994,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - - /* Initialize reset tasklet. */ - hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); -+ -+ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet", -+ completion_tasklet_func, hcd); - #ifdef DWC_DEV_SRPCAP - if (hcd->core_if->power_down == 2) { - /* Initialize Power on timer for Host power up in case hibernation */ -@@ -944,6 +1029,12 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - hcd->frame_list = NULL; - hcd->frame_list_dma = 0; - hcd->periodic_qh_count = 0; -+ -+ DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port)); -+#ifdef FIQ_DEBUG -+ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc)); -+#endif -+ - out: - return retval; - } -@@ -1089,7 +1180,12 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - uint32_t hub_addr, port_addr; - hc->do_split = 1; - hc->xact_pos = qtd->isoc_split_pos; -- hc->complete_split = qtd->complete_split; -+ /* We don't need to do complete splits anymore */ -+ if(fiq_split_enable) -+ hc->complete_split = qtd->complete_split = 0; -+ else -+ hc->complete_split = qtd->complete_split; -+ - hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); - hc->hub_addr = (uint8_t) hub_addr; - hc->port_addr = (uint8_t) port_addr; -@@ -1236,6 +1332,65 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->qh = qh; - } - -+/* -+** Check the transaction to see if the port / hub has already been assigned for -+** a split transaction -+** -+** Return 0 - Port is already in use -+*/ -+int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+{ -+ uint32_t hub_addr, port_addr; -+ -+ if(!fiq_split_enable) -+ return 0; -+ -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ -+ if(hcd->hub_port[hub_addr] & (1 << port_addr)) -+ { -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count); -+ -+ qh->skip_count++; -+ -+ if(qh->skip_count > 40000) -+ { -+ printk_once(KERN_ERR "Error: Having to skip port allocation"); -+ local_fiq_disable(); -+ BUG(); -+ return 0; -+ } -+ return 1; -+ } -+ else -+ { -+ qh->skip_count = 0; -+ hcd->hub_port[hub_addr] |= 1 << port_addr; -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd); -+#endif -+ return 0; -+ } -+} -+void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+{ -+ uint32_t hub_addr, port_addr; -+ -+ if(!fiq_split_enable) -+ return; -+ -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ -+ hcd->hub_port[hub_addr] &= ~(1 << port_addr); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1; -+#endif -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+ -+} -+ -+ - /** - * This function selects transactions from the HCD transfer schedule and - * assigns them to available host channels. It is called from HCD interrupt -@@ -1249,9 +1404,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - { - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -+ dwc_otg_qtd_t *qtd; - int num_channels; - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; - dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; - - #ifdef DEBUG_SOF -@@ -1269,11 +1425,29 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - - while (qh_ptr != &hcd->periodic_sched_ready && - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { -+ -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ -+ if(qh->do_split) { -+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ if(!(qh->ep_type == UE_ISOCHRONOUS && -+ (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || -+ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) { -+ if(dwc_otg_hcd_allocate_port(hcd, qh)) -+ { -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1); -+ continue; -+ } -+ } -+ } -+ - if (microframe_schedule) { - // Make sure we leave one channel for non periodic transactions. - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels <= 1) { - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -+ if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh); - break; - } - hcd->available_host_channels--; -@@ -1294,8 +1468,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -- -- ret_val = DWC_OTG_TRANSACTION_PERIODIC; - } - - /* -@@ -1310,6 +1482,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - num_channels - hcd->periodic_channels) && - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { - -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ -+ /* -+ * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission -+ * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed -+ * cheeky devices that just hold off using NAKs -+ */ -+ if (nak_holdoff_enable && qh->do_split) { -+ if (qh->nak_frame != 0xffff && -+ dwc_full_frame_num(qh->nak_frame) == -+ dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) { -+ /* -+ * Revisit: Need to avoid trampling on periodic scheduling. -+ * Currently we are safe because g_np_count != g_np_sent whenever we hit this, -+ * but if this behaviour is changed then periodic endpoints will get a slower -+ * polling rate. -+ */ -+ g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM; -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ continue; -+ } else { -+ qh->nak_frame = 0xffff; -+ } -+ } -+ - if (microframe_schedule) { - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels < 1) { -@@ -1322,7 +1519,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - last_sel_trans_num_nonper_scheduled++; - #endif /* DEBUG_HOST_CHANNELS */ - } -- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - - assign_and_init_hc(hcd, qh); - -@@ -1336,21 +1532,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - -- if (ret_val == DWC_OTG_TRANSACTION_NONE) { -- ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; -- } else { -- ret_val = DWC_OTG_TRANSACTION_ALL; -- } -+ g_np_sent++; - - if (!microframe_schedule) - hcd->non_periodic_channels++; - } - -+ if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) -+ ret_val |= DWC_OTG_TRANSACTION_PERIODIC; -+ -+ if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) -+ ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC; -+ -+ - #ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels; - #endif /* DEBUG_HOST_CHANNELS */ -- -- DWC_SPINLOCK_FREE(channel_lock); - return ret_val; - } - -@@ -1464,6 +1661,15 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - -+ // Do not send a split start transaction any later than frame .6 -+ // Note, we have to schedule a periodic in .5 to make it go in .6 -+ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ { -+ qh_ptr = qh_ptr->next; -+ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ continue; -+ } -+ - /* - * Set a flag if we're queuing high-bandwidth in slave mode. - * The flag prevents any halts to get into the request queue in -@@ -1593,6 +1799,15 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, - qh_list_entry); -+ -+ // Do not send a split start transaction any later than frame .5 -+ // non periodic transactions will start immediately in this uframe -+ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ { -+ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ break; -+ } -+ - status = - queue_transaction(hcd, qh->channel, - tx_status.b.nptxfspcavail); -@@ -3118,17 +3333,13 @@ dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, - else - dwc_otg_urb = DWC_ALLOC(size); - -- if (NULL != dwc_otg_urb) -- dwc_otg_urb->packet_count = iso_desc_count; -+ if (dwc_otg_urb) -+ dwc_otg_urb->packet_count = iso_desc_count; - else { -- dwc_otg_urb->packet_count = 0; -- if (size != 0) { -- DWC_ERROR("**** DWC OTG HCD URB alloc - " -- "%salloc of %db failed\n", -- atomic_alloc?"atomic ":"", size); -- } -- } -- -+ DWC_ERROR("**** DWC OTG HCD URB alloc - " -+ "%salloc of %db failed\n", -+ atomic_alloc?"atomic ":"", size); -+ } - return dwc_otg_urb; - } - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -index bb4f67a..0007fa1 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -168,10 +168,10 @@ typedef enum dwc_otg_control_phase { - - /** Transaction types. */ - typedef enum dwc_otg_transaction_type { -- DWC_OTG_TRANSACTION_NONE, -- DWC_OTG_TRANSACTION_PERIODIC, -- DWC_OTG_TRANSACTION_NON_PERIODIC, -- DWC_OTG_TRANSACTION_ALL -+ DWC_OTG_TRANSACTION_NONE = 0, -+ DWC_OTG_TRANSACTION_PERIODIC = 1, -+ DWC_OTG_TRANSACTION_NON_PERIODIC = 2, -+ DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC - } dwc_otg_transaction_type_e; - - struct dwc_otg_qh; -@@ -321,6 +321,11 @@ typedef struct dwc_otg_qh { - */ - uint16_t sched_frame; - -+ /* -+ ** Frame a NAK was received on this queue head, used to minimise NAK retransmission -+ */ -+ uint16_t nak_frame; -+ - /** (micro)frame at which last start split was initialized. */ - uint16_t start_split_frame; - -@@ -365,10 +370,19 @@ typedef struct dwc_otg_qh { - - uint16_t speed; - uint16_t frame_usecs[8]; -+ -+ uint32_t skip_count; - } dwc_otg_qh_t; - - DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); - -+typedef struct urb_tq_entry { -+ struct urb *urb; -+ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries; -+} urb_tq_entry_t; -+ -+DWC_TAILQ_HEAD(urb_list, urb_tq_entry); -+ - /** - * This structure holds the state of the HCD, including the non-periodic and - * periodic schedules. -@@ -546,9 +560,12 @@ struct dwc_otg_hcd { - /* Tasket to do a reset */ - dwc_tasklet_t *reset_tasklet; - -+ dwc_tasklet_t *completion_tasklet; -+ struct urb_list completed_urb_list; -+ - /* */ - dwc_spinlock_t *lock; -- -+ dwc_spinlock_t *channel_lock; - /** - * Private data that could be used by OS wrapper. - */ -@@ -559,6 +576,12 @@ struct dwc_otg_hcd { - /** Frame List */ - uint32_t *frame_list; - -+ /** Hub - Port assignment */ -+ int hub_port[128]; -+#ifdef FIQ_DEBUG -+ int hub_port_alloc[2048]; -+#endif -+ - /** Frame List DMA address */ - dma_addr_t frame_list_dma; - -@@ -589,6 +612,10 @@ extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t - extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - dwc_otg_transaction_type_e tr_type); - -+int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); -+void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); -+ -+ - /** @} */ - - /** @name Interrupt Handler Functions */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -index 274967b..ee920c4 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -@@ -276,7 +276,7 @@ void dump_frame_list(dwc_otg_hcd_t * hcd) - static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - { - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; - - dwc_hc_t *hc = qh->channel; - if (dwc_qh_is_non_per(qh)) { -@@ -306,7 +306,6 @@ static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - dwc_memset(qh->desc_list, 0x00, - sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); - } -- DWC_SPINLOCK_FREE(channel_lock); - } - - /** -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -index 4823167..fb57db0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -@@ -113,6 +113,11 @@ extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd); - */ - extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); - -+/** This function is used to handle the fast interrupt -+ * -+ */ -+extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void); -+ - /** - * Returns private data set by - * dwc_otg_hcd_set_priv_data function. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index b41e164..64d33a5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -34,6 +34,12 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" -+ -+#include -+#include -+#include -+ - - extern bool microframe_schedule; - -@@ -41,38 +47,487 @@ extern bool microframe_schedule; - * This file contains the implementation of the HCD Interrupt handlers. - */ - -+/* -+ * Some globals to communicate between the FIQ and INTERRUPT -+ */ -+ -+void * dummy_send; -+mphi_regs_t c_mphi_regs; -+volatile void *dwc_regs_base; -+int fiq_done, int_done; -+ -+gintsts_data_t gintsts_saved = {.d32 = 0}; -+hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; -+hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; -+int split_out_xfersize[MAX_EPS_CHANNELS]; -+haint_data_t haint_saved; -+ -+int g_next_sched_frame, g_np_count, g_np_sent; -+static int mphi_int_count = 0 ; -+ -+hcchar_data_t nak_hcchar; -+hctsiz_data_t nak_hctsiz; -+hcsplt_data_t nak_hcsplt; -+int nak_count; -+ -+int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -+int split_start_frame[MAX_EPS_CHANNELS]; -+int queued_port[MAX_EPS_CHANNELS]; -+ -+#ifdef FIQ_DEBUG -+char buffer[1000*16]; -+int wptr; -+void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) -+{ -+ FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB; -+ va_list args; -+ char text[17]; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ local_fiq_disable(); -+ if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) -+ { -+ snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); -+ va_start(args, fmt); -+ vsnprintf(text+8, 9, fmt, args); -+ va_end(args); -+ -+ memcpy(buffer + wptr, text, 16); -+ wptr = (wptr + 16) % sizeof(buffer); -+ } -+ local_irq_restore(flags); -+} -+#endif -+ -+void notrace fiq_queue_request(int channel, int odd_frame) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) }; -+ -+ if(hcsplt.b.spltena == 0) -+ { -+ fiq_print(FIQDBG_ERR, "SPLTENA "); -+ BUG(); -+ } -+ -+ if(hcchar.b.epdir == 1) -+ { -+ fiq_print(FIQDBG_SCHED, "IN Ch %d", channel); -+ } -+ else -+ { -+ hctsiz.b.xfersize = 0; -+ fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel); -+ } -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32); -+ -+ hcsplt.b.compsplt = 1; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32); -+ -+ // Send the Split complete -+ hcchar.b.chen = 1; -+ hcchar.b.oddfrm = odd_frame ? 1 : 0; -+ -+ // Post this for transmit on the next frame for periodic or this frame for non-periodic -+ fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN"); -+ -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32); -+} -+ -+static int last_sof = -1; -+ -+/* -+** Function to handle the start of frame interrupt, choose whether we need to do anything and -+** therefore trigger the main interrupt -+** -+** returns int != 0 - interrupt has been handled -+*/ -+int diff; -+ -+int notrace fiq_sof_handle(hfnum_data_t hfnum) -+{ -+ int handled = 0; -+ int i; -+ -+ // Just check that once we're running we don't miss a SOF -+ /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff))) -+ { -+ fiq_print(FIQDBG_ERR, "LASTSOF "); -+ fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7); -+ fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7); -+ BUG(); -+ }*/ -+ -+ // Only start remembering the last sof when the interrupt has been -+ // enabled (we don't check the mask to come in here...) -+ if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3)) -+ last_sof = hfnum.b.frnum; -+ -+ for(i = 0; i < MAX_EPS_CHANNELS; i++) -+ { -+ if(complete_sched[i] != -1) -+ { -+ if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0)) -+ { -+ fiq_queue_request(i, hfnum.b.frnum & 1); -+ complete_sched[i] = -1; -+ } -+ } -+ -+ if(complete_sched[i] != -1) -+ { -+ // This is because we've seen a split complete occur with no start... -+ // most likely because missed the complete 0x3fff frames ago! -+ -+ diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ; -+ if(diff > 32 && diff < 0x3f00) -+ { -+ fiq_print(FIQDBG_ERR, "SPLTMISS"); -+ BUG(); -+ } -+ } -+ } -+ -+ if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -+ { -+ /* -+ * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet -+ * g_next_sched_frame is the next frame we have periodic packets for -+ * -+ * if neither of these are required for this frame then just clear the interrupt -+ */ -+ handled = 1; -+ -+ } -+ -+ return handled; -+} -+ -+int notrace port_id(hcsplt_data_t hcsplt) -+{ -+ return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8); -+} -+ -+int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -+ hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) }; -+ hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)}; -+ -+ hcint_saved[channel].d32 |= hcint.d32; -+ hcintmsk_saved[channel].d32 = hcintmsk.d32; -+ -+ if(hcsplt.b.spltena) -+ { -+ fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt)); -+ if(hcint.b.chhltd) -+ { -+ fiq_print(FIQDBG_SCHED, "CH HLT %d", channel); -+ fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]); -+ } -+ if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_ERR, "CHAN ERR"); -+ } -+ if(hcint.b.xfercomp) -+ { -+ // Clear the port allocation and transmit anything also on this port -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "XFERCOMP"); -+ } -+ if(hcint.b.nak) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "NAK"); -+ } -+ if(hcint.b.ack && !hcsplt.b.compsplt) -+ { -+ int i; -+ -+ // Do not complete isochronous out transactions -+ if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "ISOC_OUT"); -+ } -+ else -+ { -+ // Make sure we check the port / hub combination that we sent this split on. -+ // Do not queue a second request to the same port -+ for(i = 0; i < MAX_EPS_CHANNELS; i++) -+ { -+ if(port_id(hcsplt) == queued_port[i]) -+ { -+ fiq_print(FIQDBG_ERR, "PORTERR "); -+ //BUG(); -+ } -+ } -+ -+ split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7; -+ -+ // Note, the size of an OUT is in the start split phase, not -+ // the complete split -+ split_out_xfersize[channel] = hctsiz.b.xfersize; -+ -+ hcint_saved[channel].b.chhltd = 0; -+ hcint_saved[channel].b.ack = 0; -+ -+ queued_port[channel] = port_id(hcsplt); -+ -+ if(hcchar.b.eptype & 1) -+ { -+ // Send the periodic complete in the same oddness frame as the ACK went... -+ fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -+ // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -+ } -+ else -+ { -+ // Schedule the split complete to occur later -+ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2); -+ fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -+ } -+ } -+ } -+ if(hcint.b.nyet) -+ { -+ fiq_print(FIQDBG_ERR, "NYETERR1"); -+ //BUG(); -+ // Can transmit a split complete up to uframe .0 of the next frame -+ if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8)) -+ { -+ // Send it next frame -+ if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc -+ { -+ fiq_print(FIQDBG_SCHED, "NYT:SEND"); -+ fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -+ } -+ else -+ { -+ // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP) -+ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -+ fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -+ } -+ hcint_saved[channel].b.chhltd = 0; -+ hcint_saved[channel].b.nyet = 0; -+ } -+ else -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_ERR, "NYETERR2"); -+ //BUG(); -+ } -+ } -+ } -+ else -+ { -+ /* -+ * If we have any of NAK, ACK, Datatlgerr active on a -+ * non-split channel, the sole reason is to reset error -+ * counts for a previously broken transaction. The FIQ -+ * will thrash on NAK IN and ACK OUT in particular so -+ * handle it "once" and allow the IRQ to do the rest. -+ */ -+ hcint.d32 &= hcintmsk.d32; -+ if(hcint.b.nak) -+ { -+ hcintmsk.b.nak = 0; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -+ } -+ if (hcint.b.ack) -+ { -+ hcintmsk.b.ack = 0; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -+ } -+ } -+ -+ // Clear the interrupt, this will also clear the HAINT bit -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32); -+ return hcint_saved[channel].d32 == 0; -+} -+ -+gintsts_data_t gintsts; -+gintmsk_data_t gintmsk; -+// triggered: The set of interrupts that were triggered -+// handled: The set of interrupts that have been handled (no IRQ is -+// required) -+// keep: The set of interrupts we want to keep unmasked even though we -+// want to trigger an IRQ to handle it (SOF and HCINTR) -+gintsts_data_t triggered, handled, keep; -+hfnum_data_t hfnum; -+ -+void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void) -+{ -+ -+ /* entry takes care to store registers we will be treading on here */ -+ asm __volatile__ ( -+ "mov ip, sp ;" -+ /* stash FIQ and normal regs */ -+ "stmdb sp!, {r0-r12, lr};" -+ /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ -+ "sub fp, ip, #512 ;" -+ ); -+ -+ // Cannot put local variables at the beginning of the function -+ // because otherwise 'C' will play with the stack pointer. any locals -+ // need to be inside the following block -+ do -+ { -+ fiq_done++; -+ gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14); -+ gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18); -+ hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408); -+ triggered.d32 = gintsts.d32 & gintmsk.d32; -+ handled.d32 = 0; -+ keep.d32 = 0; -+ fiq_print(FIQDBG_INT, "FIQ "); -+ fiq_print(FIQDBG_INT, "%08x", gintsts.d32); -+ fiq_print(FIQDBG_INT, "%08x", gintmsk.d32); -+ if(gintsts.d32) -+ { -+ // If port enabled -+ if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5) -+ { -+ if(gintsts.b.sofintr) -+ { -+ if(fiq_sof_handle(hfnum)) -+ { -+ handled.b.sofintr = 1; /* Handled in FIQ */ -+ } -+ else -+ { -+ /* Keer interrupt unmasked */ -+ keep.b.sofintr = 1; -+ } -+ { -+ // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing -+ // a start of frame interrupt -+ gintsts_data_t gintsts = { .b.sofintr = 1 }; -+ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -+ } -+ } -+ -+ if(fiq_split_enable && gintsts.b.hcintr) -+ { -+ int i; -+ haint_data_t haint; -+ haintmsk_data_t haintmsk; -+ -+ haint.d32 = FIQ_READ(dwc_regs_base + 0x414); -+ haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418); -+ haint.d32 &= haintmsk.d32; -+ haint_saved.d32 |= haint.d32; -+ -+ fiq_print(FIQDBG_INT, "hcintr"); -+ fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414)); -+ -+ // Go through each channel that has an enabled interrupt -+ for(i = 0; i < 16; i++) -+ if((haint.d32 >> i) & 1) -+ if(fiq_hcintr_handle(i, hfnum)) -+ haint_saved.d32 &= ~(1 << i); /* this was handled */ -+ -+ /* If we've handled all host channel interrupts then don't trigger the interrupt */ -+ if(haint_saved.d32 == 0) -+ { -+ handled.b.hcintr = 1; -+ } -+ else -+ { -+ /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */ -+ keep.b.hcintr = 1; -+ } -+ -+ { -+ gintsts_data_t gintsts = { .b.hcintr = 1 }; -+ -+ // Always clear the channel interrupt -+ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -+ } -+ } -+ } -+ else -+ { -+ last_sof = -1; -+ } -+ } -+ -+ // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep -+ gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32)); -+ // Save those that were triggered but not handled -+ gintsts_saved.d32 |= triggered.d32 & ~handled.d32; -+ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -+ -+ // Clear and save any unhandled interrupts and trigger the interrupt -+ if(gintsts_saved.d32) -+ { -+ /* To enable the MPHI interrupt (INT 32) -+ */ -+ FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); -+ FIQ_WRITE( c_mphi_regs.outddb, (1 << 29)); -+ -+ mphi_int_count++; -+ } -+ } -+ while(0); -+ -+ mb(); -+ -+ /* exit back to normal mode restoring everything */ -+ asm __volatile__ ( -+ /* return FIQ regs back to pristine state -+ * and get normal regs back -+ */ -+ "ldmia sp!, {r0-r12, lr};" -+ -+ /* return */ -+ "subs pc, lr, #4;" -+ ); -+} -+ - /** This function handles interrupts for the HCD. */ - int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int retval = 0; -+ static int last_time; - - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - gintsts_data_t gintsts; -+ gintmsk_data_t gintmsk; -+ hfnum_data_t hfnum; -+ - #ifdef DEBUG - dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; - -- //GRAYG: debugging -- if (NULL == global_regs) { -- DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p " -- "core_if=%p\n", -- dwc_otg_hcd, global_regs); -- return retval; -- } - #endif - -+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); -+ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -+ - /* Exit from ISR if core is hibernated */ - if (core_if->hibernation_suspend == 1) { -- return retval; -+ goto exit_handler_routine; - } - DWC_SPINLOCK(dwc_otg_hcd->lock); - /* Check if HOST Mode */ - if (dwc_otg_is_host_mode(core_if)) { -- gintsts.d32 = dwc_otg_read_core_intr(core_if); -+ local_fiq_disable(); -+ gintmsk.d32 |= gintsts_saved.d32; -+ gintsts.d32 |= gintsts_saved.d32; -+ gintsts_saved.d32 = 0; -+ local_fiq_enable(); - if (!gintsts.d32) { -- DWC_SPINUNLOCK(dwc_otg_hcd->lock); -- return 0; -+ goto exit_handler_routine; - } -+ gintsts.d32 &= gintmsk.d32; -+ - #ifdef DEBUG -+ // We should be OK doing this because the common interrupts should already have been serviced - /* Don't print debug message in the interrupt handler on SOF */ - #ifndef DEBUG_SOF - if (gintsts.d32 != DWC_SOF_INTR_MASK) -@@ -88,10 +543,16 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n", - gintsts.d32, core_if); - #endif -- -- if (gintsts.b.sofintr) { -+ hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); -+ if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -+ { -+ /* Note, we should never get here if the FIQ is doing it's job properly*/ - retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); - } -+ else if (gintsts.b.sofintr) { -+ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); -+ } -+ - if (gintsts.b.rxstsqlvl) { - retval |= - dwc_otg_hcd_handle_rx_status_q_level_intr -@@ -106,7 +567,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - /** @todo Implement i2cintr handler. */ - } - if (gintsts.b.portintr) { -+ -+ gintmsk_data_t gintmsk = { .b.portintr = 1}; - retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); -+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); - } - if (gintsts.b.hcintr) { - retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); -@@ -138,11 +602,48 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - #endif - - } -+ -+exit_handler_routine: -+ -+ if (fiq_fix_enable) -+ { -+ local_fiq_disable(); -+ // Make sure that we don't clear the interrupt if we've still got pending work to do -+ if(gintsts_saved.d32 == 0) -+ { -+ /* Clear the MPHI interrupt */ -+ DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16)); -+ if (mphi_int_count >= 60) -+ { -+ DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16))); -+ while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17))) -+ ; -+ DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31)); -+ mphi_int_count = 0; -+ } -+ int_done++; -+ } -+ -+ // Unmask handled interrupts -+ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -+ //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1); -+ -+ local_fiq_enable(); -+ -+ if((jiffies / HZ) > last_time) -+ { -+ /* Once a second output the fiq and irq numbers, useful for debug */ -+ last_time = jiffies / HZ; -+ DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done); -+ } -+ } -+ - DWC_SPINUNLOCK(dwc_otg_hcd->lock); - return retval; - } - - #ifdef DWC_TRACK_MISSED_SOFS -+ - #warning Compiling code to track missed SOFs - #define FRAME_NUM_ARRAY_SIZE 1000 - /** -@@ -188,7 +689,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - dwc_list_link_t *qh_entry; - dwc_otg_qh_t *qh; - dwc_otg_transaction_type_e tr_type; -- gintsts_data_t gintsts = {.d32 = 0 }; -+ int did_something = 0; -+ int32_t next_sched_frame = -1; - - hfnum.d32 = - DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -@@ -212,17 +714,31 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); - qh_entry = qh_entry->next; - if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { -+ - /* - * Move QH to the ready list to be executed next - * (micro)frame. - */ - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); -+ -+ did_something = 1; -+ } -+ else -+ { -+ if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame)) -+ { -+ next_sched_frame = qh->sched_frame; -+ } - } - } -+ -+ g_next_sched_frame = next_sched_frame; -+ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); -+ did_something = 1; - } - - /* Clear interrupt */ -@@ -511,6 +1027,15 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - - haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); - -+ // Overwrite with saved interrupts from fiq handler -+ if(fiq_split_enable) -+ { -+ local_fiq_disable(); -+ haint.d32 = haint_saved.d32; -+ haint_saved.d32 = 0; -+ local_fiq_enable(); -+ } -+ - for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { - if (haint.b2.chint & (1 << i)) { - retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i); -@@ -551,7 +1076,10 @@ static uint32_t get_actual_xfer_length(dwc_hc_t * hc, - *short_read = (hctsiz.b.xfersize != 0); - } - } else if (hc->qh->do_split) { -- length = qtd->ssplit_out_xfer_count; -+ if(fiq_split_enable) -+ length = split_out_xfersize[hc->hc_num]; -+ else -+ length = qtd->ssplit_out_xfer_count; - } else { - length = hc->xfer_len; - } -@@ -595,7 +1123,6 @@ static int update_urb_state_xfer_comp(dwc_hc_t * hc, - DWC_OTG_HC_XFER_COMPLETE, - &short_read); - -- - /* non DWORD-aligned buffer case handling. */ - if (hc->align_buff && xfer_length && hc->ep_is_in) { - dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, -@@ -797,11 +1324,24 @@ static void release_channel(dwc_otg_hcd_t * hcd, - dwc_otg_transaction_type_e tr_type; - int free_qtd; - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; -+#ifdef FIQ_DEBUG -+ int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0; -+#endif -+ int hog_port = 0; - - DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", - __func__, hc->hc_num, halt_status, hc->xfer_len); - -+ if(fiq_split_enable && hc->do_split) { -+ if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { -+ if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || -+ hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { -+ hog_port = 1; -+ } -+ } -+ } -+ - switch (halt_status) { - case DWC_OTG_HC_XFER_URB_COMPLETE: - free_qtd = 1; -@@ -876,15 +1416,32 @@ static void release_channel(dwc_otg_hcd_t * hcd, - - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - hcd->available_host_channels++; -+ fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - } - -+ if(fiq_split_enable && hc->do_split) -+ { -+ if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr))) -+ { -+ fiq_print(FIQDBG_ERR, "PRTNOTAL"); -+ //BUG(); -+ } -+ if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC || -+ hc->ep_type == DWC_OTG_EP_TYPE_INTR)) { -+ hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1; -+#endif -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp); -+ } -+ } -+ - /* Try to queue more transfers now that there's a free channel. */ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); - } -- DWC_SPINLOCK_FREE(channel_lock); - } - - /** -@@ -1295,6 +1852,17 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - "NAK Received--\n", hc->hc_num); - - /* -+ * When we get bulk NAKs then remember this so we holdoff on this qh until -+ * the beginning of the next frame -+ */ -+ switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { -+ case UE_BULK: -+ case UE_CONTROL: -+ if (nak_holdoff_enable) -+ hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); -+ } -+ -+ /* - * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and - * interrupt. Re-start the SSPLIT transfer. - */ -@@ -1316,7 +1884,11 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - * transfers in DMA mode for the sole purpose of - * resetting the error count after a transaction error - * occurs. The core will continue transferring data. -+ * Disable other interrupts unmasked for the same -+ * reason. - */ -+ disable_hc_int(hc_regs, datatglerr); -+ disable_hc_int(hc_regs, ack); - qtd->error_count = 0; - goto handle_nak_done; - } -@@ -1428,6 +2000,15 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd, - halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); - } - } else { -+ /* -+ * An unmasked ACK on a non-split DMA transaction is -+ * for the sole purpose of resetting error counts. Disable other -+ * interrupts unmasked for the same reason. -+ */ -+ if(hcd->core_if->dma_enable) { -+ disable_hc_int(hc_regs, datatglerr); -+ disable_hc_int(hc_regs, nak); -+ } - qtd->error_count = 0; - - if (hc->qh->ping_state) { -@@ -1490,8 +2071,10 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, - hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { - int frnum = dwc_otg_hcd_get_frame_number(hcd); - -+ // With the FIQ running we only ever see the failed NYET - if (dwc_full_frame_num(frnum) != -- dwc_full_frame_num(hc->qh->sched_frame)) { -+ dwc_full_frame_num(hc->qh->sched_frame) || -+ fiq_split_enable) { - /* - * No longer in the same full speed frame. - * Treat this as a transaction error. -@@ -1778,13 +2361,28 @@ static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd, - dwc_otg_qtd_t * qtd) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " -- "Data Toggle Error--\n", hc->hc_num); -+ "Data Toggle Error on %s transfer--\n", -+ hc->hc_num, (hc->ep_is_in ? "IN" : "OUT")); - -- if (hc->ep_is_in) { -+ /* Data toggles on split transactions cause the hc to halt. -+ * restart transfer */ -+ if(hc->qh->do_split) -+ { -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ } else if (hc->ep_is_in) { -+ /* An unmasked data toggle error on a non-split DMA transaction is -+ * for the sole purpose of resetting error counts. Disable other -+ * interrupts unmasked for the same reason. -+ */ -+ if(hcd->core_if->dma_enable) { -+ disable_hc_int(hc_regs, ack); -+ disable_hc_int(hc_regs, nak); -+ } - qtd->error_count = 0; -- } else { -- DWC_ERROR("Data Toggle Error on OUT transfer," -- "channel %d\n", hc->hc_num); - } - - disable_hc_int(hc_regs, datatglerr); -@@ -1862,10 +2460,10 @@ static inline int halt_status_ok(dwc_otg_hcd_t * hcd, - static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd) -+ dwc_otg_qtd_t * qtd, -+ hcint_data_t hcint, -+ hcintmsk_data_t hcintmsk) - { -- hcint_data_t hcint; -- hcintmsk_data_t hcintmsk; - int out_nak_enh = 0; - - /* For core with OUT NAK enhancement, the flow for high- -@@ -1897,8 +2495,11 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - } - - /* Read the HCINTn register to determine the cause for the halt. */ -- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ if(!fiq_split_enable) -+ { -+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ } - - if (hcint.b.xfercomp) { - /** @todo This is here because of a possible hardware bug. Spec -@@ -1937,6 +2538,8 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - handle_hc_babble_intr(hcd, hc, hc_regs, qtd); - } else if (hcint.b.frmovrun) { - handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.datatglerr) { -+ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); - } else if (!out_nak_enh) { - if (hcint.b.nyet) { - /* -@@ -1986,12 +2589,24 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - DWC_READ_REG32(&hcd-> - core_if->core_global_regs-> - gintsts)); -+ /* Failthrough: use 3-strikes rule */ -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); - } - - } - } else { - DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", - hcint.d32); -+ /* Failthrough: use 3-strikes rule */ -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); - } - } - -@@ -2009,13 +2624,15 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd) -+ dwc_otg_qtd_t * qtd, -+ hcint_data_t hcint, -+ hcintmsk_data_t hcintmsk) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " - "Channel Halted--\n", hc->hc_num); - - if (hcd->core_if->dma_enable) { -- handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); -+ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk); - } else { - #ifdef DEBUG - if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { -@@ -2032,7 +2649,7 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - { - int retval = 0; -- hcint_data_t hcint; -+ hcint_data_t hcint, hcint_orig; - hcintmsk_data_t hcintmsk; - dwc_hc_t *hc; - dwc_otg_hc_regs_t *hc_regs; -@@ -2042,15 +2659,33 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - - hc = dwc_otg_hcd->hc_ptr_array[num]; - hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; -+ if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { -+ /* We are responding to a channel disable. Driver -+ * state is cleared - our qtd has gone away. -+ */ -+ release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); -+ return 1; -+ } - qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); - - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcint_orig = hcint; - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); - DWC_DEBUGPL(DBG_HCDV, - " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", - hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); - hcint.d32 = hcint.d32 & hcintmsk.d32; - -+ if(fiq_split_enable) -+ { -+ // replace with the saved interrupts from the fiq handler -+ local_fiq_disable(); -+ hcint_orig.d32 = hcint_saved[num].d32; -+ hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32; -+ hcint_saved[num].d32 = 0; -+ local_fiq_enable(); -+ } -+ - if (!dwc_otg_hcd->core_if->dma_enable) { - if (hcint.b.chhltd && hcint.d32 != 0x2) { - hcint.b.chhltd = 0; -@@ -2068,7 +2703,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - hcint.b.nyet = 0; - } - if (hcint.b.chhltd) { -- retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); -+ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]); - } - if (hcint.b.ahberr) { - retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); -@@ -2080,7 +2715,8 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.ack) { -- retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); -+ if(!hcint.b.chhltd) -+ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.nyet) { - retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); -@@ -2102,5 +2738,4 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - - return retval; - } -- - #endif /* DWC_DEVICE_ONLY */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index e4787f5..ee8eec9 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -1,3 +1,4 @@ -+ - /* ========================================================================== - * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ - * $Revision: #20 $ -@@ -50,6 +51,7 @@ - #include - #include - #include -+#include - #include - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) - #include <../drivers/usb/core/hcd.h> -@@ -67,6 +69,8 @@ - #include "dwc_otg_dbg.h" - #include "dwc_otg_driver.h" - #include "dwc_otg_hcd.h" -+#include "dwc_otg_mphi_fix.h" -+ - /** - * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is - * qualified with its direction (possible 32 endpoints per device). -@@ -76,6 +80,8 @@ - - static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; - -+extern bool fiq_fix_enable; -+ - /** @name Linux HC Driver API Functions */ - /** @{ */ - /* manage i/o requests, device state */ -@@ -259,13 +265,15 @@ static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, - - /** - * Sets the final status of an URB and returns it to the device driver. Any -- * required cleanup of the URB is performed. -+ * required cleanup of the URB is performed. The HCD lock should be held on -+ * entry. - */ - static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) - { - struct urb *urb = (struct urb *)urb_handle; -- -+ urb_tq_entry_t *new_entry; -+ int rc = 0; - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", - __func__, urb, usb_pipedevice(urb->pipe), -@@ -279,7 +287,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - } - } - } -- -+ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t)); - urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); - /* Convert status value. */ - switch (status) { -@@ -301,6 +309,9 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - case -DWC_E_OVERFLOW: - status = -EOVERFLOW; - break; -+ case -DWC_E_SHUTDOWN: -+ status = -ESHUTDOWN; -+ break; - default: - if (status) { - DWC_PRINTF("Uknown urb status %d\n", status); -@@ -342,18 +353,33 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - } - - DWC_FREE(dwc_otg_urb); -- -+ if (!new_entry) { -+ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); -+ urb->status = -EPROTO; -+ /* don't schedule the tasklet - -+ * directly return the packet here with error. */ - #if USB_URB_EP_LINKING -- usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); - #endif -- DWC_SPINUNLOCK(hcd->lock); - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); - #else -- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); - #endif -- DWC_SPINLOCK(hcd->lock); -- -+ } else { -+ new_entry->urb = urb; -+#if USB_URB_EP_LINKING -+ rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); -+ if(0 == rc) { -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); -+ } -+#endif -+ if(0 == rc) { -+ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, -+ urb_tq_entries); -+ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet); -+ } -+ } - return 0; - } - -@@ -366,6 +392,16 @@ static struct dwc_otg_hcd_function_ops hcd_fops = { - .get_b_hnp_enable = _get_b_hnp_enable, - }; - -+static struct fiq_handler fh = { -+ .name = "usb_fiq", -+}; -+struct fiq_stack_s { -+ int magic1; -+ uint8_t stack[2048]; -+ int magic2; -+} fiq_stack; -+ -+extern mphi_regs_t c_mphi_regs; - /** - * Initializes the HCD. This function allocates memory for and initializes the - * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the -@@ -379,6 +415,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); - int retval = 0; - u64 dmamask; -+ struct pt_regs regs; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); - -@@ -396,6 +433,20 @@ int hcd_init(dwc_bus_dev_t *_dev) - pci_set_consistent_dma_mask(_dev, dmamask); - #endif - -+ if (fiq_fix_enable) -+ { -+ // Set up fiq -+ claim_fiq(&fh); -+ set_fiq_handler(__FIQ_Branch, 4); -+ memset(®s,0,sizeof(regs)); -+ regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq; -+ regs.ARM_r9 = (long)0; -+ regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4; -+ set_fiq_regs(®s); -+ fiq_stack.magic1 = 0xdeadbeef; -+ fiq_stack.magic2 = 0xaa995566; -+ } -+ - /* - * Allocate memory for the base HCD plus the DWC OTG HCD. - * Initialize the base HCD. -@@ -415,6 +466,30 @@ int hcd_init(dwc_bus_dev_t *_dev) - - hcd->regs = otg_dev->os_dep.base; - -+ if (fiq_fix_enable) -+ { -+ volatile extern void *dwc_regs_base; -+ -+ //Set the mphi periph to the required registers -+ c_mphi_regs.base = otg_dev->os_dep.mphi_base; -+ c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ -+ dwc_regs_base = otg_dev->os_dep.base; -+ -+ //Enable mphi peripheral -+ writel((1<<31),c_mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(c_mphi_regs.ctrl) & 0x80000000) -+ DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n"); -+ else -+ DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ } - /* Initialize the DWC OTG HCD. */ - dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); - if (!dwc_otg_hcd) { -@@ -607,9 +682,7 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - struct usb_host_endpoint *ep = urb->ep; - #endif --#if USB_URB_EP_LINKING - dwc_irqflags_t irqflags; --#endif - void **ref_ep_hcpriv = &ep->hcpriv; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); - dwc_otg_hcd_urb_t *dwc_otg_urb; -@@ -661,9 +734,8 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - if(dwc_otg_urb == NULL) - return -ENOMEM; - -- urb->hcpriv = dwc_otg_urb; -- if (!dwc_otg_urb && urb->number_of_packets) -- return -ENOMEM; -+ if (!dwc_otg_urb && urb->number_of_packets) -+ return -ENOMEM; - - dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), ep_type, -@@ -703,37 +775,42 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - iso_frame_desc[i].length); - } - -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -+ urb->hcpriv = dwc_otg_urb; - #if USB_URB_EP_LINKING -- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); - retval = usb_hcd_link_urb_to_ep(hcd, urb); -- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); - if (0 == retval) - #endif -- { -- retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -- /*(dwc_otg_qh_t **)*/ -- ref_ep_hcpriv, -- mem_flags == GFP_ATOMIC ? 1 : 0); -- if (0 == retval) { -- if (alloc_bandwidth) { -- allocate_bus_bandwidth(hcd, -- dwc_otg_hcd_get_ep_bandwidth( -- dwc_otg_hcd, *ref_ep_hcpriv), -- urb); -- } -- } else { -+ { -+ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -+ /*(dwc_otg_qh_t **)*/ -+ ref_ep_hcpriv, 1); -+ if (0 == retval) { -+ if (alloc_bandwidth) { -+ allocate_bus_bandwidth(hcd, -+ dwc_otg_hcd_get_ep_bandwidth( -+ dwc_otg_hcd, *ref_ep_hcpriv), -+ urb); -+ } -+ } else { -+ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); - #if USB_URB_EP_LINKING -- dwc_irqflags_t irqflags; -- DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); -- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -- usb_hcd_unlink_urb_from_ep(hcd, urb); -- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); -+ usb_hcd_unlink_urb_from_ep(hcd, urb); - #endif -- if (retval == -DWC_E_NO_DEVICE) { -- retval = -ENODEV; -- } -- } -- } -+ DWC_FREE(dwc_otg_urb); -+ urb->hcpriv = NULL; -+ if (retval == -DWC_E_NO_DEVICE) -+ retval = -ENODEV; -+ } -+ } -+#if USB_URB_EP_LINKING -+ else -+ { -+ DWC_FREE(dwc_otg_urb); -+ urb->hcpriv = NULL; -+ } -+#endif -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); - return retval; - } - -@@ -777,6 +854,8 @@ static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) - usb_hcd_unlink_urb_from_ep(hcd, urb); - #endif - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); -+ -+ - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - usb_hcd_giveback_urb(hcd, urb); - #else -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 0a1cbb7..5c22b6c 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -41,6 +41,7 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" - - extern bool microframe_schedule; - -@@ -182,6 +183,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) - if (microframe_schedule) - qh->speed = dev_speed; - -+ qh->nak_frame = 0xffff; - - if (((dev_speed == USB_SPEED_LOW) || - (dev_speed == USB_SPEED_FULL)) && -@@ -191,6 +193,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) - dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, - hub_port); - qh->do_split = 1; -+ qh->skip_count = 0; - } - - if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { -@@ -573,6 +576,9 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - return status; - } - -+ -+extern int g_next_sched_frame, g_np_count, g_np_sent; -+ - /** - * Schedules an interrupt or isochronous transfer in the periodic schedule. - * -@@ -631,8 +637,13 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); - } - else { -- /* Always start in the inactive schedule. */ -- DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); -+ if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame)) -+ { -+ g_next_sched_frame = qh->sched_frame; -+ -+ } -+ /* Always start in the inactive schedule. */ -+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); - } - - if (!microframe_schedule) { -@@ -646,6 +657,7 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - return status; - } - -+ - /** - * This function adds a QH to either the non periodic or periodic schedule if - * it is not already in the schedule. If the QH is already in the schedule, no -@@ -668,6 +680,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - /* Always start in the inactive schedule. */ - DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, - &qh->qh_list_entry); -+ g_np_count++; - } else { - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { -@@ -727,6 +740,9 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->non_periodic_qh_ptr->next; - } - DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); -+ -+ // If we've removed the last non-periodic entry then there are none left! -+ g_np_count = g_np_sent; - } else { - deschedule_periodic(hcd, qh); - hcd->periodic_qh_count--; -@@ -755,6 +771,24 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - int sched_next_periodic_split) - { - if (dwc_qh_is_non_per(qh)) { -+ -+ dwc_otg_qh_t *qh_tmp; -+ dwc_list_link_t *qh_list; -+ DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) -+ { -+ qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry); -+ if(qh_tmp == qh) -+ { -+ /* -+ * FIQ is being disabled because this one nevers gets a np_count increment -+ * This is still not absolutely correct, but it should fix itself with -+ * just an unnecessary extra interrupt -+ */ -+ g_np_sent = g_np_count; -+ } -+ } -+ -+ - dwc_otg_hcd_qh_remove(hcd, qh); - if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { - /* Add back to inactive non-periodic schedule. */ -@@ -768,6 +802,7 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - if (sched_next_periodic_split) { - - qh->sched_frame = frame_number; -+ - if (dwc_frame_num_le(frame_number, - dwc_frame_num_inc - (qh->start_split_frame, -@@ -816,6 +851,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); - } else { -+ if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame)) -+ { -+ g_next_sched_frame = qh->sched_frame; -+ } -+ - DWC_LIST_MOVE_HEAD - (&hcd->periodic_sched_inactive, - &qh->qh_list_entry); -@@ -880,6 +920,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb) - * QH to place the QTD into. If it does not find a QH, then it will create a - * new QH. If the QH to which the QTD is added is not currently scheduled, it - * is placed into the proper schedule based on its EP type. -+ * HCD lock must be held and interrupts must be disabled on entry - * - * @param[in] qtd The QTD to add - * @param[in] hcd The DWC HCD structure -@@ -892,8 +933,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc) - { - int retval = 0; -- dwc_irqflags_t flags; -- - dwc_otg_hcd_urb_t *urb = qtd->urb; - - /* -@@ -903,18 +942,16 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - if (*qh == NULL) { - *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); - if (*qh == NULL) { -- retval = -1; -+ retval = -DWC_E_NO_MEMORY; - goto done; - } - } -- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); - retval = dwc_otg_hcd_qh_add(hcd, *qh); - if (retval == 0) { - DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, - qtd_list_entry); -+ qtd->qh = *qh; - } -- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -- - done: - - return retval; -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -new file mode 100755 -index 0000000..50b94a8 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -@@ -0,0 +1,113 @@ -+#include "dwc_otg_regs.h" -+#include "dwc_otg_dbg.h" -+ -+void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n" -+ "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n" -+ "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n" -+ "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n" -+ "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n" -+ "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n" -+ "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n" -+ "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n" -+ "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -+ function_name, -+ gintsts.b.curmode, -+ gintsts.b.modemismatch, -+ gintsts.b.otgintr, -+ gintsts.b.sofintr, -+ gintsts.b.rxstsqlvl, -+ gintsts.b.nptxfempty, -+ gintsts.b.ginnakeff, -+ gintsts.b.goutnakeff, -+ gintsts.b.ulpickint, -+ gintsts.b.i2cintr, -+ gintsts.b.erlysuspend, -+ gintsts.b.usbsuspend, -+ gintsts.b.usbreset, -+ gintsts.b.enumdone, -+ gintsts.b.isooutdrop, -+ gintsts.b.eopframe, -+ gintsts.b.restoredone, -+ gintsts.b.epmismatch, -+ gintsts.b.inepint, -+ gintsts.b.outepintr, -+ gintsts.b.incomplisoin, -+ gintsts.b.incomplisoout, -+ gintsts.b.fetsusp, -+ gintsts.b.resetdet, -+ gintsts.b.portintr, -+ gintsts.b.hcintr, -+ gintsts.b.ptxfempty, -+ gintsts.b.lpmtranrcvd, -+ gintsts.b.conidstschng, -+ gintsts.b.disconnect, -+ gintsts.b.sessreqintr, -+ gintsts.b.wkupintr); -+ return; -+} -+ -+void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n" -+ "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n" -+ "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n" -+ "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n" -+ "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n" -+ "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n" -+ "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n" -+ "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n" -+ "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -+ function_name, -+ gintmsk.b.modemismatch, -+ gintmsk.b.otgintr, -+ gintmsk.b.sofintr, -+ gintmsk.b.rxstsqlvl, -+ gintmsk.b.nptxfempty, -+ gintmsk.b.ginnakeff, -+ gintmsk.b.goutnakeff, -+ gintmsk.b.ulpickint, -+ gintmsk.b.i2cintr, -+ gintmsk.b.erlysuspend, -+ gintmsk.b.usbsuspend, -+ gintmsk.b.usbreset, -+ gintmsk.b.enumdone, -+ gintmsk.b.isooutdrop, -+ gintmsk.b.eopframe, -+ gintmsk.b.restoredone, -+ gintmsk.b.epmismatch, -+ gintmsk.b.inepintr, -+ gintmsk.b.outepintr, -+ gintmsk.b.incomplisoin, -+ gintmsk.b.incomplisoout, -+ gintmsk.b.fetsusp, -+ gintmsk.b.resetdet, -+ gintmsk.b.portintr, -+ gintmsk.b.hcintr, -+ gintmsk.b.ptxfempty, -+ gintmsk.b.lpmtranrcvd, -+ gintmsk.b.conidstschng, -+ gintmsk.b.disconnect, -+ gintmsk.b.sessreqintr, -+ gintmsk.b.wkupintr); -+ return; -+} -+ -+void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n" -+ "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n" -+ "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n" -+ "mvic: %1i\n", -+ function_name, -+ gotgint.b.sesenddet, -+ gotgint.b.sesreqsucstschng, -+ gotgint.b.hstnegsucstschng, -+ gotgint.b.hstnegdet, -+ gotgint.b.adevtoutchng, -+ gotgint.b.debdone, -+ gotgint.b.mvic); -+ -+ return; -+} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -new file mode 100755 -index 0000000..ca17379 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -@@ -0,0 +1,48 @@ -+#ifndef __DWC_OTG_MPHI_FIX_H__ -+#define __DWC_OTG_MPHI_FIX_H__ -+#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_)) -+#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_)) -+ -+typedef struct { -+ volatile void* base; -+ volatile void* ctrl; -+ volatile void* outdda; -+ volatile void* outddb; -+ volatile void* intstat; -+} mphi_regs_t; -+ -+void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name); -+void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name); -+void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name); -+ -+extern gintsts_data_t gintsts_saved; -+ -+#ifdef DEBUG -+#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__) -+#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__) -+#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__) -+ -+#else -+#define DWC_DBG_PRINT_CORE_INT(_arg_) -+#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) -+#define DWC_DBG_PRINT_OTG_INT(_arg_) -+ -+#endif -+ -+typedef enum { -+ FIQDBG_SCHED = (1 << 0), -+ FIQDBG_INT = (1 << 1), -+ FIQDBG_ERR = (1 << 2), -+ FIQDBG_PORTHUB = (1 << 3), -+} FIQDBG_T; -+ -+void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...); -+#ifdef FIQ_DEBUG -+#define fiq_print _fiq_print -+#else -+#define fiq_print(x, y, ...) -+#endif -+ -+extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable; -+ -+#endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -index e46d9bb..6b2c7d0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -@@ -97,6 +97,9 @@ typedef struct os_dependent { - /** Register offset for Diagnostic API */ - uint32_t reg_offset; - -+ /** Base address for MPHI peripheral */ -+ void *mphi_base; -+ - #ifdef LM_INTERFACE - struct lm_device *lmdev; - #elif defined(PCI_INTERFACE) -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -index 1b1f83c..c8590b5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -@@ -4276,7 +4276,7 @@ do { \ - && (pcd->ep0state == EP0_OUT_DATA_PHASE)) - status.d32 = core_if->dev_if->out_desc_addr->status.d32; - if (pcd->ep0state == EP0_OUT_STATUS_PHASE) -- status.d32 = status.d32 = core_if->dev_if-> -+ status.d32 = core_if->dev_if-> - out_desc_addr->status.d32; - - if (status.b.sr) { - -From 5cd92267e90f236fd69402f1c98eef1613d2688a Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 19 Mar 2014 12:58:23 +0000 -Subject: [PATCH 027/172] dwc_otg: fiq_fsm: Base commit for driver rewrite - -This commit removes the previous FIQ fixes entirely and adds fiq_fsm. - -This rewrite features much more complete support for split transactions -and takes into account several OTG hardware bugs. High-speed -isochronous transactions are also capable of being performed by fiq_fsm. - -All driver options have been removed and replaced with: - - dwc_otg.fiq_enable (bool) - - dwc_otg.fiq_fsm_enable (bool) - - dwc_otg.fiq_fsm_mask (bitmask) - - dwc_otg.nak_holdoff (unsigned int) - -Defaults are specified such that fiq_fsm behaves similarly to the -previously implemented FIQ fixes. - -fiq_fsm: Push error recovery into the FIQ when fiq_fsm is used - -If the transfer associated with a QTD failed due to a bus error, the HCD -would retry the transfer up to 3 times (implementing the USB2.0 -three-strikes retry in software). - -Due to the masking mechanism used by fiq_fsm, it is only possible to pass -a single interrupt through to the HCD per-transfer. - -In this instance host channels would fall off the radar because the error -reset would function, but the subsequent channel halt would be lost. - -Push the error count reset into the FIQ handler. - -fiq_fsm: Implement timeout mechanism - -For full-speed endpoints with a large packet size, interrupt latency -runs the risk of the FIQ starting a transaction too late in a full-speed -frame. If the device is still transmitting data when EOF2 for the -downstream frame occurs, the hub will disable the port. This change is -not reflected in the hub status endpoint and the device becomes -unresponsive. - -Prevent high-bandwidth transactions from being started too late in a -frame. The mechanism is not guaranteed: a combination of bit stuffing -and hub latency may still result in a device overrunning. - -fiq_fsm: fix bounce buffer utilisation for Isochronous OUT - -Multi-packet isochronous OUT transactions were subject to a few bounday -bugs. Fix them. - -Audio playback is now much more robust: however, an issue stands with -devices that have adaptive sinks - ALSA plays samples too fast. - -dwc_otg: Return full-speed frame numbers in HS mode - -The frame counter increments on every *microframe* in high-speed mode. -Most device drivers expect this number to be in full-speed frames - this -caused considerable confusion to e.g. snd_usb_audio which uses the -frame counter to estimate the number of samples played. - -fiq_fsm: save PID on completion of interrupt OUT transfers - -Also add edge case handling for interrupt transports. - -Note that for periodic split IN, data toggles are unimplemented in the -OTG host hardware - it unconditionally accepts any PID. - -fiq_fsm: add missing case for fiq_fsm_tt_in_use() - -Certain combinations of bitrate and endpoint activity could -result in a periodic transaction erroneously getting started -while the previous Isochronous OUT was still active. - -fiq_fsm: clear hcintmsk for aborted transactions - -Prevents the FIQ from erroneously handling interrupts -on a timed out channel. - -fiq_fsm: enable by default - -fiq_fsm: fix dequeues for non-periodic split transactions - -If a dequeue happened between the SSPLIT and CSPLIT phases of the -transaction, the HCD would never receive an interrupt. - -fiq_fsm: Disable by default - -fiq_fsm: Handle HC babble errors - -The HCTSIZ transfer size field raises a babble interrupt if -the counter wraps. Handle the resulting interrupt in this case. - -dwc_otg: fix interrupt registration for fiq_enable=0 - -Additionally make the module parameter conditional for wherever -hcd->fiq_state is touched. - -fiq_fsm: Enable by default ---- - arch/arm/mach-bcm2708/bcm2708.c | 24 +- - drivers/usb/host/dwc_otg/Makefile | 3 +- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +- - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 51 +- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1290 ++++++++++++++++++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 353 +++++++ - drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 775 +++++++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 11 + - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 999 ++++++++++---------- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 113 ++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 41 +- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 --- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 - - drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 8 +- - 15 files changed, 2991 insertions(+), 965 deletions(-) - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S - delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c - delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index dc59a6b..a740344 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -330,22 +330,13 @@ static struct resource bcm2708_usb_resources[] = { - .end = IRQ_HOSTPORT, - .flags = IORESOURCE_IRQ, - }, -+ [3] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, - }; - --bool fiq_fix_enable = true; -- --static struct resource bcm2708_usb_resources_no_fiq_fix[] = { -- [0] = { -- .start = USB_BASE, -- .end = USB_BASE + SZ_128K - 1, -- .flags = IORESOURCE_MEM, -- }, -- [1] = { -- .start = IRQ_USB, -- .end = IRQ_USB, -- .flags = IORESOURCE_IRQ, -- }, --}; - - static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - -@@ -701,11 +692,6 @@ void __init bcm2708_init(void) - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -- if (!fiq_fix_enable) -- { -- bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix; -- bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix); -- } - bcm_register_device(&bcm2708_usb_device); - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); -diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile -index a56f193..e7bdd12 100644 ---- a/drivers/usb/host/dwc_otg/Makefile -+++ b/drivers/usb/host/dwc_otg/Makefile -@@ -36,7 +36,8 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o - dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o - dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o - dwc_otg-objs += dwc_otg_adp.o --dwc_otg-objs += dwc_otg_mphi_fix.o -+dwc_otg-objs += dwc_otg_fiq_fsm.o -+dwc_otg-objs += dwc_otg_fiq_stub.o - ifneq ($(CFI),) - dwc_otg-objs += dwc_otg_cfi.o - endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 2f8b3bd..065807f 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -45,7 +45,6 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_pcd.h" - #include "dwc_otg_hcd.h" --#include "dwc_otg_mphi_fix.h" - - #ifdef DEBUG - inline const char *op_state_str(dwc_otg_core_if_t * core_if) -@@ -1319,7 +1318,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) - /** - * This function returns the Core Interrupt register. - */ --static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk) -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd) - { - gahbcfg_data_t gahbcfg = {.d32 = 0 }; - gintsts_data_t gintsts; -@@ -1345,16 +1344,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - } - gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); - gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -- { -- unsigned long flags; -- -- // Re-enable the saved interrupts -- local_irq_save(flags); -+ if(fiq_enable) { - local_fiq_disable(); -- gintmsk.d32 |= gintmsk_common.d32; -- gintsts_saved.d32 &= ~gintmsk_common.d32; -- reenable_gintmsk->d32 = gintmsk.d32; -- local_irq_restore(flags); -+ /* Pull in the interrupts that the FIQ has masked */ -+ gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ -+ reenable_gintmsk->d32 |= gintmsk.d32; -+ reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ reenable_gintmsk->d32 &= gintmsk_common.d32; -+ local_fiq_enable(); - } - - gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); -@@ -1366,13 +1364,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - gintsts.d32, gintmsk.d32); - } - #endif -- if (!fiq_fix_enable){ -+ if (!fiq_enable){ - if (gahbcfg.b.glblintrmsk) - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); - else - return 0; -- } -- else { -+ } else { -+ /* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface. -+ * Can't trust the global interrupt mask bit in this case. -+ */ - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); - } - -@@ -1406,7 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - { - int retval = 0; - gintsts_data_t gintsts; -- gintmsk_data_t reenable_gintmsk; -+ gintmsk_data_t gintmsk_reenable = { .d32 = 0 }; - gpwrdn_data_t gpwrdn = {.d32 = 0 }; - dwc_otg_device_t *otg_dev = dev; - dwc_otg_core_if_t *core_if = otg_dev->core_if; -@@ -1428,7 +1428,10 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - - if (core_if->hibernation_suspend <= 0) { -- gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk); -+ /* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end -+ * of this handler - god only knows why it's done like this -+ */ -+ gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd); - - if (gintsts.b.modemismatch) { - retval |= dwc_otg_handle_mode_mismatch_intr(core_if); -@@ -1525,11 +1528,16 @@ int32_t dwc_otg_handle_common_intr(void *dev) - gintsts.b.portintr = 1; - DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); - retval |= 1; -- reenable_gintmsk.b.portintr = 1; -+ gintmsk_reenable.b.portintr = 1; - - } -- -- DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32); -+ /* Did we actually handle anything? if so, unmask the interrupt */ -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); -+ if (retval) { -+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); -+ } - - } else { - DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); -@@ -1583,6 +1591,5 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - if (core_if->lock) - DWC_SPINUNLOCK(core_if->lock); -- - return retval; - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -index f06c3d22..dc7cd32 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -56,6 +56,7 @@ - #include "dwc_otg_core_if.h" - #include "dwc_otg_pcd_if.h" - #include "dwc_otg_hcd_if.h" -+#include "dwc_otg_fiq_fsm.h" - - #define DWC_DRIVER_VERSION "3.00a 10-AUG-2012" - #define DWC_DRIVER_DESC "HS OTG USB Controller driver" -@@ -64,7 +65,6 @@ bool microframe_schedule=true; - - static const char dwc_driver_name[] = "dwc_otg"; - --extern void* dummy_send; - - extern int pcd_init( - #ifdef LM_INTERFACE -@@ -240,13 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = { - .adp_enable = -1, - }; - --//Global variable to switch the fiq fix on or off (declared in bcm2708.c) --extern bool fiq_fix_enable; -+//Global variable to switch the fiq fix on or off -+bool fiq_enable = 1; - // Global variable to enable the split transaction fix --bool fiq_split_enable = true; --//Global variable to switch the nak holdoff on or off --bool nak_holdoff_enable = true; -+bool fiq_fsm_enable = true; -+//Bulk split-transaction NAK holdoff in microframes -+uint16_t nak_holdoff = 8; - -+unsigned short fiq_fsm_mask = 0x07; - - /** - * This function shows the Driver Version. -@@ -800,7 +801,7 @@ static int dwc_otg_driver_probe( - dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, - _dev->resource[0].end - - _dev->resource[0].start+1); -- if (fiq_fix_enable) -+ if (fiq_enable) - { - if (!request_mem_region(_dev->resource[1].start, - _dev->resource[1].end - _dev->resource[1].start + 1, -@@ -813,7 +814,6 @@ static int dwc_otg_driver_probe( - dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, - _dev->resource[1].end - - _dev->resource[1].start + 1); -- dummy_send = (void *) kmalloc(16, GFP_ATOMIC); - } - - #else -@@ -902,9 +902,9 @@ static int dwc_otg_driver_probe( - */ - - #if defined(PLATFORM_INTERFACE) -- devirq = platform_get_irq(_dev, 0); -+ devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1); - #else -- devirq = _dev->irq; -+ devirq = _dev->irq; - #endif - DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", - devirq); -@@ -1071,9 +1071,9 @@ static int __init dwc_otg_driver_init(void) - int error; - struct device_driver *drv; - -- if(fiq_split_enable && !fiq_fix_enable) { -- printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n"); -- fiq_fix_enable = 1; -+ if(fiq_fsm_enable && !fiq_enable) { -+ printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n"); -+ fiq_enable = 1; - } - - printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, -@@ -1095,9 +1095,9 @@ static int __init dwc_otg_driver_init(void) - printk(KERN_ERR "%s retval=%d\n", __func__, retval); - return retval; - } -- printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled"); -- printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled"); -- printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled"); - - error = driver_create_file(drv, &driver_attr_version); - #ifdef DEBUG -@@ -1378,12 +1378,19 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); - module_param(microframe_schedule, bool, 0444); - MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); - --module_param(fiq_fix_enable, bool, 0444); --MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix"); --module_param(nak_holdoff_enable, bool, 0444); --MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff"); --module_param(fiq_split_enable, bool, 0444); --MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions"); -+module_param(fiq_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_enable, "Enable the FIQ"); -+module_param(nak_holdoff, ushort, 0644); -+MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8"); -+module_param(fiq_fsm_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask"); -+module_param(fiq_fsm_mask, ushort, 0444); -+MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n" -+ "Bit 0 : Non-periodic split transactions\n" -+ "Bit 1 : Periodic split transactions\n" -+ "Bit 2 : High-speed multi-transfer isochronous\n" -+ "All other bits should be set 0."); -+ - - /** @page "Module Parameters" - * -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -new file mode 100644 -index 0000000..1be6e71 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -0,0 +1,1290 @@ -+/* -+ * dwc_otg_fiq_fsm.c - The finite state machine FIQ -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This FIQ implements functionality that performs split transactions on -+ * the dwc_otg hardware without any outside intervention. A split transaction -+ * is "queued" by nominating a specific host channel to perform the entirety -+ * of a split transaction. This FIQ will then perform the microframe-precise -+ * scheduling required in each phase of the transaction until completion. -+ * -+ * The FIQ functionality is glued into the Synopsys driver via the entry point -+ * in the FSM enqueue function, and at the exit point in handling a HC interrupt -+ * for a FSM-enabled channel. -+ * -+ * NB: Large parts of this implementation have architecture-specific code. -+ * For porting this functionality to other ARM machines, the minimum is required: -+ * - An interrupt controller allowing the top-level dwc USB interrupt to be routed -+ * to the FIQ -+ * - A method of forcing a software generated interrupt from FIQ mode that then -+ * triggers an IRQ entry (with the dwc USB handler called by this IRQ number) -+ * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same -+ * processor core - there is no locking between the FIQ and IRQ (aside from -+ * local_fiq_disable) -+ * -+ */ -+ -+#include "dwc_otg_fiq_fsm.h" -+ -+ -+char buffer[1000*16]; -+int wptr; -+void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...) -+{ -+ enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR; -+ va_list args; -+ char text[17]; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) }; -+ -+ if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR) -+ { -+ snprintf(text, 9, " %4d:%1u ", hfnum.b.frnum/8, hfnum.b.frnum & 7); -+ va_start(args, fmt); -+ vsnprintf(text+8, 9, fmt, args); -+ va_end(args); -+ -+ memcpy(buffer + wptr, text, 16); -+ wptr = (wptr + 16) % sizeof(buffer); -+ } -+} -+ -+/** -+ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction -+ * @channel: channel to re-enable -+ */ -+static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) }; -+ -+ hcchar.b.chen = 0; -+ if (st->channel[n].hcchar_copy.b.eptype & 0x1) { -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ /* Hardware bug workaround: update the ssplit index */ -+ if (st->channel[n].hcsplt_copy.b.spltena) -+ st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; -+ -+ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; -+ } -+ -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); -+ hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ hcchar.b.chen = 1; -+ -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); -+ fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force); -+} -+ -+/** -+ * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage -+ * @st: Pointer to the channel's state -+ * @n : channel number -+ * -+ * Change host channel registers to perform a complete-split transaction. Being mindful of the -+ * endpoint direction, set control regs up correctly. -+ */ -+static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n) -+{ -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ -+ hcsplt.b.compsplt = 1; -+ if (st->channel[n].hcchar_copy.b.epdir == 1) { -+ // If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket. -+ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; -+ } else { -+ // If OUT, the CSPLIT result contains handshake only. -+ hctsiz.b.xfersize = 0; -+ } -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+ mb(); -+} -+ -+static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n) -+{ -+ /* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */ -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ -+ if (st->channel[n].hcchar_copy.b.epdir == 0) { -+ return st->channel[n].hctsiz_copy.b.xfersize; -+ } else { -+ return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize; -+ } -+ -+} -+ -+ -+/** -+ * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT -+ * -+ * Of use only for IN periodic transfers. -+ */ -+static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n) -+{ -+ hcdma_data_t hcdma; -+ int i = st->channel[n].dma_info.index; -+ int len; -+ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; -+ -+ len = fiq_get_xfer_len(st, n); -+ fiq_print(FIQDBG_INT, st, "LEN: %03d", len); -+ st->channel[n].dma_info.slot_len[i] = len; -+ i++; -+ if (i > 6) -+ BUG(); -+ -+ hcdma.d32 = (dma_addr_t) &blob->channel[n].index[i].buf[0]; -+ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ st->channel[n].dma_info.index = i; -+ return 0; -+} -+ -+/** -+ * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ -+ */ -+static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n) -+{ -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; -+ hctsiz.b.pktcnt = 1; -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+} -+ -+/** -+ * fiq_iso_out_advance() - update DMA address and split position bits -+ * for isochronous OUT transactions. -+ * -+ * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and -+ * Split-BEGIN states are not handled - this is done when the transaction was queued. -+ * -+ * This function must only be called from the FIQ_ISO_OUT_ACTIVE state. -+ */ -+static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n) -+{ -+ hcsplt_data_t hcsplt; -+ hctsiz_data_t hctsiz; -+ hcdma_data_t hcdma; -+ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; -+ int last = 0; -+ int i = st->channel[n].dma_info.index; -+ -+ fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i); -+ i++; -+ if (i == 4) -+ last = 1; -+ if (st->channel[n].dma_info.slot_len[i+1] == 255) -+ last = 1; -+ -+ /* New DMA address - address of bounce buffer referred to in index */ -+ hcdma.d32 = (uint32_t) &blob->channel[n].index[i].buf[0]; -+ //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n)); -+ //hcdma.d32 += st->channel[n].dma_info.slot_len[i]; -+ fiq_print(FIQDBG_INT, st, "LAST: %01d ", last); -+ fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]); -+ hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT); -+ hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ); -+ hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID; -+ /* Set up new packet length */ -+ hctsiz.b.pktcnt = 1; -+ hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i]; -+ fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32); -+ -+ st->channel[n].dma_info.index++; -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ return last; -+} -+ -+/** -+ * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT -+ * -+ * Despite the limitations of the DWC core, we can force a microframe pipeline of -+ * isochronous OUT start-split transactions while waiting for a corresponding other-type -+ * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it -+ * is very unlikely that filling the start-split FIFO will cause data loss. -+ * This allows much better interleaving of transactions in an order-independent way- -+ * there is no requirement to prioritise isochronous, just a state-space search has -+ * to be performed on each periodic start-split complete interrupt. -+ */ -+static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n) -+{ -+ int hub_addr = st->channel[n].hub_addr; -+ int port_addr = st->channel[n].port_addr; -+ int i, poked = 0; -+ for (i = 0; i < num_channels; i++) { -+ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ if (st->channel[i].hub_addr == hub_addr && -+ st->channel[i].port_addr == port_addr) { -+ switch (st->channel[i].fsm) { -+ case FIQ_PER_ISO_OUT_PENDING: -+ if (st->channel[i].nrpackets == 1) { -+ st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ fiq_fsm_restart_channel(st, i, 0); -+ poked = 1; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ if (poked) -+ break; -+ } -+ return poked; -+} -+ -+/** -+ * fiq_fsm_tt_in_use() - search for host channels using this TT -+ * @n: Channel to use as reference -+ * -+ */ -+int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n) -+{ -+ int hub_addr = st->channel[n].hub_addr; -+ int port_addr = st->channel[n].port_addr; -+ int i, in_use = 0; -+ for (i = 0; i < num_channels; i++) { -+ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ switch (st->channel[i].fsm) { -+ /* TT is reserved for channels that are in the middle of a periodic -+ * split transaction. -+ */ -+ case FIQ_PER_SSPLIT_STARTED: -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ //case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ case FIQ_PER_ISO_OUT_LAST: -+ if (st->channel[i].hub_addr == hub_addr && -+ st->channel[i].port_addr == port_addr) { -+ in_use = 1; -+ } -+ break; -+ default: -+ break; -+ } -+ if (in_use) -+ break; -+ } -+ return in_use; -+} -+ -+/** -+ * fiq_fsm_more_csplits() - determine whether additional CSPLITs need -+ * to be issued for this IN transaction. -+ * -+ * We cannot tell the inbound PID of a data packet due to hardware limitations. -+ * we need to make an educated guess as to whether we need to queue another CSPLIT -+ * or not. A no-brainer is when we have received enough data to fill the endpoint -+ * size, but for endpoints that give variable-length data then we have to resort -+ * to heuristics. -+ * -+ * We also return whether this is the last CSPLIT to be queued, again based on -+ * heuristics. This is to allow a 1-uframe overlap of periodic split transactions. -+ * Note: requires at least 1 CSPLIT to have been performed prior to being called. -+ */ -+ -+/* -+ * We need some way of guaranteeing if a returned periodic packet of size X -+ * has a DATA0 PID. -+ * The heuristic value of 144 bytes assumes that the received data has maximal -+ * bit-stuffing and the clock frequency of the transmitting device is at the lowest -+ * permissible limit. If the transfer length results in a final packet size -+ * 144 < p <= 188, then an erroneous CSPLIT will be issued. -+ * Also used to ensure that an endpoint will nominally only return a single -+ * complete-split worth of data. -+ */ -+#define DATA0_PID_HEURISTIC 144 -+ -+static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last) -+{ -+ -+ int i; -+ int total_len = 0; -+ int more_needed = 1; -+ struct fiq_channel_state *st = &state->channel[n]; -+ -+ for (i = 0; i < st->dma_info.index; i++) { -+ total_len += st->dma_info.slot_len[i]; -+ } -+ -+ *probably_last = 0; -+ -+ if (st->hcchar_copy.b.eptype == 0x3) { -+ /* -+ * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data -+ * then this is definitely the last CSPLIT. -+ */ -+ *probably_last = 1; -+ } else { -+ /* Isoc IN. This is a bit risky if we are the first transaction: -+ * we may have been held off slightly. */ -+ if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) { -+ more_needed = 0; -+ } -+ /* If in the next uframe we will receive enough data to fill the endpoint, -+ * then only issue 1 more csplit. -+ */ -+ if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC) -+ *probably_last = 1; -+ } -+ -+ if (total_len >= st->hctsiz_copy.b.xfersize || -+ i == 6 || total_len == 0) -+ /* Note: due to bit stuffing it is possible to have > 6 CSPLITs for -+ * a single endpoint. Accepting more would completely break our scheduling mechanism though -+ * - in these extreme cases we will pass through a truncated packet. -+ */ -+ more_needed = 0; -+ -+ return more_needed; -+} -+ -+/** -+ * fiq_fsm_too_late() - Test transaction for lateness -+ * -+ * If a SSPLIT for a large IN transaction is issued too late in a frame, -+ * the hub will disable the port to the device and respond with ERR handshakes. -+ * The hub status endpoint will not reflect this change. -+ * Returns 1 if we will issue a SSPLIT that will result in a device babble. -+ */ -+int notrace fiq_fsm_too_late(struct fiq_state *st, int n) -+{ -+ int uframe; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ uframe = hfnum.b.frnum & 0x7; -+ if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) { -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+ -+/** -+ * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline -+ * -+ * Search pending transactions in the start-split pending state and queue them. -+ * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4). -+ * Note: we specifically don't do isochronous OUT transactions first because better -+ * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT. -+ */ -+static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels) -+{ -+ int n; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ if ((hfnum.b.frnum & 0x7) == 5) -+ return; -+ for (n = 0; n < num_channels; n++) { -+ if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) { -+ /* Check to see if any other transactions are using this TT */ -+ if(!fiq_fsm_tt_in_use(st, num_channels, n)) { -+ if (!fiq_fsm_too_late(st, n)) { -+ st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; -+ fiq_print(FIQDBG_INT, st, "NEXTPER "); -+ fiq_fsm_restart_channel(st, n, 0); -+ } else { -+ st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ } -+ break; -+ } -+ } -+ } -+ for (n = 0; n < num_channels; n++) { -+ if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) { -+ if (!fiq_fsm_tt_in_use(st, num_channels, n)) { -+ fiq_print(FIQDBG_INT, st, "NEXTISO "); -+ st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ fiq_fsm_restart_channel(st, n, 0); -+ break; -+ } -+ } -+ } -+} -+ -+/** -+ * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data -+ * @state: Pointer to fiq_state -+ * @n: Channel transaction is active on -+ * @hcint: Copy of host channel interrupt register -+ * -+ * Returns 0 if there are no more transactions for this HC to do, 1 -+ * otherwise. -+ */ -+static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint) -+{ -+ struct fiq_channel_state *st = &state->channel[n]; -+ int xfer_len = 0, nrpackets = 0; -+ hcdma_data_t hcdma; -+ fiq_print(FIQDBG_INT, state, "HSISO %02d", n); -+ -+ xfer_len = fiq_get_xfer_len(state, n); -+ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len; -+ -+ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32; -+ -+ st->hs_isoc_info.index++; -+ if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) { -+ return 0; -+ } -+ -+ /* grab the next DMA address offset from the array */ -+ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset; -+ FIQ_WRITE(state->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ -+ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as -+ * the core needs to be told to send the correct number. Caution: for IN transfers, -+ * this is always set to the maximum size of the endpoint. */ -+ xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length; -+ /* Integer divide in a FIQ: fun. FIXME: make this not suck */ -+ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; -+ if (nrpackets == 0) -+ nrpackets = 1; -+ st->hcchar_copy.b.multicnt = nrpackets; -+ st->hctsiz_copy.b.pktcnt = nrpackets; -+ -+ /* Initial PID also needs to be set */ -+ if (st->hcchar_copy.b.epdir == 0) { -+ st->hctsiz_copy.b.xfersize = xfer_len; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_MDATA; -+ break; -+ } -+ -+ } else { -+ switch (st->hcchar_copy.b.multicnt) { -+ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA1; -+ break; -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA2; -+ break; -+ } -+ } -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32); -+ /* Channel is enabled on hcint handler exit */ -+ fiq_print(FIQDBG_INT, state, "HSISOOUT"); -+ return 1; -+} -+ -+ -+/** -+ * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler -+ * @state: Pointer to the state struct passed from banked FIQ mode registers. -+ * @num_channels: set according to the DWC hardware configuration -+ * -+ * The SOF handler in FSM mode has two functions -+ * 1. Hold off SOF from causing schedule advancement in IRQ context if there's -+ * nothing to do -+ * 2. Advance certain FSM states that require either a microframe delay, or a microframe -+ * of holdoff. -+ * -+ * The second part is architecture-specific to mach-bcm2835 - -+ * a sane interrupt controller would have a mask register for ARM interrupt sources -+ * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt -+ * number (USB) can be enabled. This means that certain parts of the USB specification -+ * that require "wait a little while, then issue another packet" cannot be fulfilled with -+ * the timing granularity required to achieve optimal throughout. The workaround is to use -+ * the SOF "timer" (125uS) to perform this task. -+ */ -+static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels) -+{ -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) }; -+ int n; -+ int kick_irq = 0; -+ -+ if ((hfnum.b.frnum & 0x7) == 1) { -+ /* We cannot issue csplits for transactions in the last frame past (n+1).1 -+ * Check to see if there are any transactions that are stale. -+ * Boot them out. -+ */ -+ for (n = 0; n < num_channels; n++) { -+ switch (state->channel[n].fsm) { -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_CSPLIT_LAST: -+ /* Check if we are no longer in the same full-speed frame. */ -+ if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) < -+ (hfnum.b.frnum & ~0x7)) -+ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ break; -+ default: -+ break; -+ } -+ } -+ } -+ -+ for (n = 0; n < num_channels; n++) { -+ switch (state->channel[n].fsm) { -+ -+ case FIQ_NP_SSPLIT_RETRY: -+ case FIQ_NP_IN_CSPLIT_RETRY: -+ case FIQ_NP_OUT_CSPLIT_RETRY: -+ fiq_fsm_restart_channel(state, n, 0); -+ break; -+ -+ case FIQ_HS_ISOC_SLEEPING: -+ state->channel[n].fsm = FIQ_HS_ISOC_TURBO; -+ fiq_fsm_restart_channel(state, n, 0); -+ break; -+ -+ case FIQ_PER_SSPLIT_QUEUED: -+ if ((hfnum.b.frnum & 0x7) == 5) -+ break; -+ if(!fiq_fsm_tt_in_use(state, num_channels, n)) { -+ if (!fiq_fsm_too_late(state, n)) { -+ fiq_print(FIQDBG_INT, st, "SOF GO %01d", n); -+ fiq_fsm_restart_channel(state, n, 0); -+ state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ /* Transaction cannot be started without risking a device babble error */ -+ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ state->haintmsk_saved.b2.chint &= ~(1 << n); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); -+ kick_irq |= 1; -+ } -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_PENDING: -+ /* Ordinarily, this should be poked after the SSPLIT -+ * complete interrupt for a competing transfer on the same -+ * TT. Doesn't happen for aborted transactions though. -+ */ -+ if ((hfnum.b.frnum & 0x7) >= 5) -+ break; -+ if (!fiq_fsm_tt_in_use(state, num_channels, n)) { -+ /* Hardware bug. SOF can sometimes occur after the channel halt interrupt -+ * that caused this. -+ */ -+ fiq_fsm_restart_channel(state, n, 0); -+ fiq_print(FIQDBG_INT, state, "SOF ISOC"); -+ if (state->channel[n].nrpackets == 1) { -+ state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_WAIT: -+ /* we are guaranteed to be in this state if and only if the SSPLIT interrupt -+ * occurred when the bus transaction occurred. The SOF interrupt reversal bug -+ * will utterly bugger this up though. -+ */ -+ if (hfnum.b.frnum != state->channel[n].expected_uframe) { -+ fiq_print(FIQDBG_INT, state, "SOFCS %d ", n); -+ state->channel[n].fsm = FIQ_PER_CSPLIT_POLL; -+ fiq_fsm_restart_channel(state, n, 0); -+ fiq_fsm_start_next_periodic(state, num_channels); -+ -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ case FIQ_DEQUEUE_ISSUED: -+ /* Ugly: we have to force a HCD interrupt. -+ * Poke the mask for the channel in question. -+ * We will take a fake SOF because of this, but -+ * that's OK. -+ */ -+ state->haintmsk_saved.b2.chint &= ~(1 << n); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); -+ kick_irq |= 1; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ if (state->kick_np_queues || -+ dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum)) -+ kick_irq |= 1; -+ -+ return !kick_irq; -+} -+ -+ -+/** -+ * fiq_fsm_do_hcintr() - FSM host channel interrupt handler -+ * @state: Pointer to the FIQ state struct -+ * @num_channels: Number of channels as per hardware config -+ * @n: channel for which HAINT(i) was raised -+ * -+ * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well. -+ */ -+static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n) -+{ -+ hcint_data_t hcint; -+ hcintmsk_data_t hcintmsk; -+ hcint_data_t hcint_probe; -+ hcchar_data_t hcchar; -+ int handled = 0; -+ int restart = 0; -+ int last_csplit = 0; -+ int start_next_periodic = 0; -+ struct fiq_channel_state *st = &state->channel[n]; -+ hfnum_data_t hfnum; -+ -+ hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT); -+ hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK); -+ hcint_probe.d32 = hcint.d32 & hcintmsk.d32; -+ -+ if (st->fsm != FIQ_PASSTHROUGH) { -+ fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm); -+ fiq_print(FIQDBG_INT, state, "%08x", hcint.d32); -+ } -+ -+ switch (st->fsm) { -+ -+ case FIQ_PASSTHROUGH: -+ case FIQ_DEQUEUE_ISSUED: -+ /* doesn't belong to us, kick it upstairs */ -+ break; -+ -+ case FIQ_PASSTHROUGH_ERRORSTATE: -+ /* We are here to emulate the error recovery mechanism of the dwc HCD. -+ * Several interrupts are unmasked if a previous transaction failed - it's -+ * death for the FIQ to attempt to handle them as the channel isn't halted. -+ * Emulate what the HCD does in this situation: mask and continue. -+ * The FSM has no other state setup so this has to be handled out-of-band. -+ */ -+ fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); -+ if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { -+ fiq_print(FIQDBG_ERR, state, "RESET %02d", n); -+ st->nr_errors = 0; -+ hcintmsk.b.nak = 0; -+ hcintmsk.b.ack = 0; -+ hcintmsk.b.datatglerr = 0; -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32); -+ return 1; -+ } -+ if (hcint_probe.b.chhltd) { -+ fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n); -+ fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32); -+ return 0; -+ } -+ break; -+ -+ /* Non-periodic state groups */ -+ case FIQ_NP_SSPLIT_STARTED: -+ case FIQ_NP_SSPLIT_RETRY: -+ /* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */ -+ if (hcint.b.ack) { -+ /* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction -+ * will start shortly. SOF needs to kick the transaction to prevent a NYET flood. -+ */ -+ if(st->hcchar_copy.b.epdir == 1) -+ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; -+ else -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ st->nr_errors = 0; -+ handled = 1; -+ fiq_fsm_setup_csplit(state, n); -+ } else if (hcint.b.nak) { -+ // No buffer space in TT. Retry on a uframe boundary. -+ st->fsm = FIQ_NP_SSPLIT_RETRY; -+ handled = 1; -+ } else if (hcint.b.xacterr) { -+ // The only other one we care about is xacterr. This implies HS bus error - retry. -+ st->nr_errors++; -+ st->fsm = FIQ_NP_SSPLIT_RETRY; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else { -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ handled = 0; -+ restart = 0; -+ } -+ break; -+ -+ case FIQ_NP_IN_CSPLIT_RETRY: -+ /* Received a CSPLIT done interrupt. -+ * Expected Data/NAK/STALL/NYET for IN. -+ */ -+ if (hcint.b.xfercomp) { -+ /* For IN, data is present. */ -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nak) { -+ /* no endpoint data. Punt it upstairs */ -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nyet) { -+ /* CSPLIT NYET - retry on a uframe boundary. */ -+ handled = 1; -+ st->nr_errors = 0; -+ } else if (hcint.b.datatglerr) { -+ /* data toggle errors do not set the xfercomp bit. */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else if (hcint.b.xacterr) { -+ /* HS error. Retry immediate */ -+ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else if (hcint.b.stall || hcint.b.bblerr) { -+ /* A STALL implies either a LS bus error or a genuine STALL. */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else { -+ /* Hardware bug. It's possible in some cases to -+ * get a channel halt with nothing else set when -+ * the response was a NYET. Treat as local 3-strikes retry. -+ */ -+ hcint_data_t hcint_test = hcint; -+ hcint_test.b.chhltd = 0; -+ if (!hcint_test.d32) { -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ } -+ } else { -+ /* Bail out if something unexpected happened */ -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } -+ } -+ break; -+ -+ case FIQ_NP_OUT_CSPLIT_RETRY: -+ /* Received a CSPLIT done interrupt. -+ * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/ -+ if (hcint.b.xfercomp) { -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nak) { -+ // The HCD will implement the holdoff on frame boundaries. -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nyet) { -+ // Hub still processing. -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ handled = 1; -+ st->nr_errors = 0; -+ //restart = 1; -+ } else if (hcint.b.xacterr) { -+ /* HS error. retry immediate */ -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else if (hcint.b.stall) { -+ /* LS bus error or genuine stall */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else { -+ /* -+ * Hardware bug. It's possible in some cases to get a -+ * channel halt with nothing else set when the response was a NYET. -+ * Treat as local 3-strikes retry. -+ */ -+ hcint_data_t hcint_test = hcint; -+ hcint_test.b.chhltd = 0; -+ if (!hcint_test.d32) { -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ } -+ } else { -+ // Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it. -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } -+ } -+ break; -+ -+ /* Periodic split states (except isoc out) */ -+ case FIQ_PER_SSPLIT_STARTED: -+ /* Expect an ACK or failure for SSPLIT */ -+ if (hcint.b.ack) { -+ /* -+ * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs. -+ * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1 -+ * point for microframe n-3, the packet will not appear on the bus until microframe n. -+ * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus -+ * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1 -+ * coincident with SOF for n+1. -+ * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place. -+ * These appear to be caused by timing/clock crossing bugs within the core itself. -+ * State machine workaround. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ fiq_fsm_setup_csplit(state, n); -+ /* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct -+ * time. If not, then we're in the next SOF. -+ */ -+ if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) { -+ fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n); -+ st->expected_uframe = hfnum.b.frnum; -+ st->fsm = FIQ_PER_CSPLIT_WAIT; -+ } else { -+ fiq_print(FIQDBG_INT, state, "CSPOL %01d", n); -+ /* For isochronous IN endpoints, -+ * we need to hold off if we are expecting a lot of data */ -+ if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) { -+ start_next_periodic = 1; -+ } -+ /* Danger will robinson: we are in a broken state. If our first interrupt after -+ * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable -+ * lag. Unmask the NYET interrupt. -+ */ -+ st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; -+ st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1; -+ restart = 1; -+ } -+ handled = 1; -+ } else if (hcint.b.xacterr) { -+ /* 3-strikes retry is enabled, we have hit our max nr_errors */ -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ start_next_periodic = 1; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ start_next_periodic = 1; -+ } -+ /* We can now queue the next isochronous OUT transaction, if one is pending. */ -+ if(fiq_fsm_tt_next_isoc(state, num_channels, n)) { -+ fiq_print(FIQDBG_INT, state, "NEXTISO "); -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_NYET1: -+ /* First CSPLIT attempt was a NYET. If we get a subsequent NYET, -+ * we are too late and the TT has dropped its CSPLIT fifo. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ start_next_periodic = 1; -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ st->fsm = FIQ_PER_CSPLIT_POLL; -+ st->nr_errors = 0; -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ handled = 1; -+ restart = 1; -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ /* Doh. Data lost. */ -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_BROKEN_NYET1: -+ /* -+ * we got here because our host channel is in the delayed-interrupt -+ * state and we cannot take a NYET interrupt any later than when it -+ * occurred. Disable then re-enable the channel if this happens to force -+ * CSPLITs to occur at the right time. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ fiq_print(FIQDBG_INT, state, "BROK: %01d ", n); -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ start_next_periodic = 1; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ st->fsm = FIQ_PER_CSPLIT_POLL; -+ handled = 1; -+ restart = 1; -+ start_next_periodic = 1; -+ /* Reload HCTSIZ for the next transfer */ -+ fiq_fsm_reload_hctsiz(state, n); -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ start_next_periodic = 1; -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ /* Local 3-strikes retry is handled by the core. This is a ERR response.*/ -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_POLL: -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ start_next_periodic = 1; -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ handled = 1; -+ restart = 1; -+ /* Reload HCTSIZ for the next transfer */ -+ fiq_fsm_reload_hctsiz(state, n); -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ /* Are we a NYET after the first data packet? */ -+ if (st->nrpackets == 0) { -+ st->fsm = FIQ_PER_CSPLIT_NYET1; -+ handled = 1; -+ restart = 1; -+ } else { -+ /* We got a NYET when polling CSPLITs. Can happen -+ * if our heuristic fails, or if someone disables us -+ * for any significant length of time. -+ */ -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ /* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/ -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_HS_ISOC_TURBO: -+ if (fiq_fsm_update_hs_isoc(state, n, hcint)) { -+ /* more transactions to come */ -+ handled = 1; -+ restart = 1; -+ fiq_print(FIQDBG_INT, state, "HSISO M "); -+ } else { -+ st->fsm = FIQ_HS_ISOC_DONE; -+ fiq_print(FIQDBG_INT, state, "HSISO F "); -+ } -+ break; -+ -+ case FIQ_HS_ISOC_ABORTED: -+ /* This abort is called by the driver rewriting the state mid-transaction -+ * which allows the dequeue mechanism to work more effectively. -+ */ -+ break; -+ -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ if (hcint.b.ack) { -+ if(fiq_iso_out_advance(state, num_channels, n)) { -+ /* last OUT transfer */ -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ /* -+ * Assuming the periodic FIFO in the dwc core -+ * actually does its job properly, we can queue -+ * the next ssplit now and in theory, the wire -+ * transactions will be in-order. -+ */ -+ // No it doesn't. It appears to process requests in host channel order. -+ //start_next_periodic = 1; -+ } -+ handled = 1; -+ restart = 1; -+ } else { -+ /* -+ * Isochronous transactions carry on regardless. Log the error -+ * and continue. -+ */ -+ //explode += 1; -+ st->nr_errors++; -+ if(fiq_iso_out_advance(state, num_channels, n)) { -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ //start_next_periodic = 1; -+ } -+ handled = 1; -+ restart = 1; -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_LAST: -+ if (hcint.b.ack) { -+ /* All done here */ -+ st->fsm = FIQ_PER_ISO_OUT_DONE; -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_DONE; -+ st->nr_errors++; -+ } -+ start_next_periodic = 1; -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ /* SOF kicked us because we overran. */ -+ start_next_periodic = 1; -+ break; -+ -+ default: -+ break; -+ } -+ -+ if (handled) { -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32); -+ } else { -+ /* Copy the regs into the state so the IRQ knows what to do */ -+ st->hcint_copy.d32 = hcint.d32; -+ } -+ -+ if (restart) { -+ /* Restart always implies handled. */ -+ if (restart == 2) { -+ /* For complete-split INs, the show must go on. -+ * Force a channel restart */ -+ fiq_fsm_restart_channel(state, n, 1); -+ } else { -+ fiq_fsm_restart_channel(state, n, 0); -+ } -+ } -+ if (start_next_periodic) { -+ fiq_fsm_start_next_periodic(state, num_channels); -+ } -+ if (st->fsm != FIQ_PASSTHROUGH) -+ fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm); -+ -+ return handled; -+} -+ -+ -+/** -+ * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ -+ * @state: pointer to state struct passed from the banked FIQ mode registers. -+ * @num_channels: set according to the DWC hardware configuration -+ * @dma: pointer to DMA bounce buffers for split transaction slots -+ * -+ * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode -+ * inside an EHCI or similar host controller regarding split transactions. The DWC core -+ * interrupts each and every time a split transaction packet is received or sent successfully. -+ * This results in either an interrupt storm when everything is working "properly", or -+ * the interrupt latency of the system in general breaks time-sensitive periodic split -+ * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ -+ * solves these problems. -+ * -+ * Return: void -+ */ -+void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) -+{ -+ gintsts_data_t gintsts, gintsts_handled; -+ gintmsk_data_t gintmsk; -+ //hfnum_data_t hfnum; -+ haint_data_t haint, haint_handled; -+ haintmsk_data_t haintmsk; -+ int kick_irq = 0; -+ -+ gintsts_handled.d32 = 0; -+ haint_handled.d32 = 0; -+ -+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); -+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -+ gintsts.d32 &= gintmsk.d32; -+ -+ if (gintsts.b.sofintr) { -+ /* For FSM mode, SOF is required to keep the state machine advance for -+ * certain stages of the periodic pipeline. It's death to mask this -+ * interrupt in that case. -+ */ -+ -+ if (!fiq_fsm_do_sof(state, num_channels)) { -+ /* Kick IRQ once. Queue advancement means that all pending transactions -+ * will get serviced when the IRQ finally executes. -+ */ -+ if (state->gintmsk_saved.b.sofintr == 1) -+ kick_irq |= 1; -+ state->gintmsk_saved.b.sofintr = 0; -+ } -+ gintsts_handled.b.sofintr = 1; -+ } -+ -+ if (gintsts.b.hcintr) { -+ int i; -+ haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT); -+ haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK); -+ haint.d32 &= haintmsk.d32; -+ haint_handled.d32 = 0; -+ for (i=0; ihaintmsk_saved.b2.chint &= ~(1 << i); -+ } else { -+ /* do_hcintr cleaned up after itself, but clear haint */ -+ haint_handled.b2.chint |= (1 << i); -+ } -+ } -+ } -+ -+ if (haint_handled.b2.chint) { -+ FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32); -+ } -+ -+ if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) { -+ /* -+ * This is necessary to avoid multiple retriggers of the MPHI in the case -+ * where interrupts are held off and HCINTs start to pile up. -+ * Only wake up the IRQ if a new interrupt came in, was not handled and was -+ * masked. -+ */ -+ haintmsk.d32 &= state->haintmsk_saved.d32; -+ FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32); -+ kick_irq |= 1; -+ } -+ /* Top-Level interrupt - always handled because it's level-sensitive */ -+ gintsts_handled.b.hcintr = 1; -+ } -+ -+ -+ /* Clear the bits in the saved register that were not handled but were triggered. */ -+ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); -+ -+ /* FIQ didn't handle something - mask has changed - write new mask */ -+ if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) { -+ gintmsk.d32 &= state->gintmsk_saved.d32; -+ gintmsk.b.sofintr = 1; -+ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); -+// fiq_print(FIQDBG_INT, state, "KICKGINT"); -+// fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32); -+// fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32); -+ kick_irq |= 1; -+ } -+ -+ if (gintsts_handled.d32) { -+ /* Only applies to edge-sensitive bits in GINTSTS */ -+ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); -+ } -+ -+ /* We got an interrupt, didn't handle it. */ -+ if (kick_irq) { -+ state->mphi_int_count++; -+ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); -+ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); -+ -+ } -+ state->fiq_done++; -+ mb(); -+} -+ -+ -+/** -+ * dwc_otg_fiq_nop() - FIQ "lite" -+ * @state: pointer to state struct passed from the banked FIQ mode registers. -+ * -+ * The "nop" handler does not intervene on any interrupts other than SOF. -+ * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals -+ * with non-periodic/periodic queues) needs to be kicked. -+ * -+ * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second. -+ * -+ * Return: void -+ */ -+void notrace dwc_otg_fiq_nop(struct fiq_state *state) -+{ -+ gintsts_data_t gintsts, gintsts_handled; -+ gintmsk_data_t gintmsk; -+ hfnum_data_t hfnum; -+ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); -+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -+ gintsts.d32 &= gintmsk.d32; -+ gintsts_handled.d32 = 0; -+ -+ if (gintsts.b.sofintr) { -+ if (!state->kick_np_queues && -+ dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) { -+ /* SOF handled, no work to do, just ACK interrupt */ -+ gintsts_handled.b.sofintr = 1; -+ } else { -+ /* Kick IRQ */ -+ state->gintmsk_saved.b.sofintr = 0; -+ } -+ } -+ -+ /* Reset handled interrupts */ -+ if(gintsts_handled.d32) { -+ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); -+ } -+ -+ /* Clear the bits in the saved register that were not handled but were triggered. */ -+ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); -+ -+ /* We got an interrupt, didn't handle it and want to mask it */ -+ if (~(state->gintmsk_saved.d32)) { -+ state->mphi_int_count++; -+ gintmsk.d32 &= state->gintmsk_saved.d32; -+ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); -+ /* Force a clear before another dummy send */ -+ FIQ_WRITE(state->mphi_regs.intstat, (1<<29)); -+ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); -+ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); -+ -+ } -+ state->fiq_done++; -+ mb(); -+} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -new file mode 100644 -index 0000000..5c7707f ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -@@ -0,0 +1,353 @@ -+/* -+ * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This FIQ implements functionality that performs split transactions on -+ * the dwc_otg hardware without any outside intervention. A split transaction -+ * is "queued" by nominating a specific host channel to perform the entirety -+ * of a split transaction. This FIQ will then perform the microframe-precise -+ * scheduling required in each phase of the transaction until completion. -+ * -+ * The FIQ functionality has been surgically implanted into the Synopsys -+ * vendor-provided driver. -+ * -+ */ -+ -+#ifndef DWC_OTG_FIQ_FSM_H_ -+#define DWC_OTG_FIQ_FSM_H_ -+ -+#include "dwc_otg_regs.h" -+#include "dwc_otg_cil.h" -+#include "dwc_otg_hcd.h" -+#include -+#include -+#include -+#include -+ -+#if 0 -+#define FLAME_ON(x) \ -+do { \ -+ int gpioreg; \ -+ \ -+ gpioreg = readl(__io_address(0x20200000+0x8)); \ -+ gpioreg &= ~(7 << (x-20)*3); \ -+ gpioreg |= 0x1 << (x-20)*3; \ -+ writel(gpioreg, __io_address(0x20200000+0x8)); \ -+ \ -+ writel(1< 1, SOF wakes up the isochronous FSM */ -+ FIQ_HS_ISOC_SLEEPING = 24, -+ FIQ_HS_ISOC_DONE = 25, -+ FIQ_HS_ISOC_ABORTED = 26, -+ FIQ_DEQUEUE_ISSUED = 30, -+ FIQ_TEST = 32, -+}; -+ -+struct fiq_stack { -+ int magic1; -+ uint8_t stack[2048]; -+ int magic2; -+}; -+ -+ -+/** -+ * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel) -+ * @index: Number of slots reported used for IN transactions / number of slots -+ * transmitted for an OUT transaction -+ * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused) -+ * -+ * Split transaction transfers can have variable length depending on other bus -+ * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore -+ * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers -+ * can happen per-frame. -+ */ -+struct fiq_dma_info { -+ u8 index; -+ u8 slot_len[6]; -+}; -+ -+struct __attribute__((packed)) fiq_split_dma_slot { -+ u8 buf[188]; -+}; -+ -+struct fiq_dma_channel { -+ struct __attribute__((packed)) fiq_split_dma_slot index[6]; -+}; -+ -+struct fiq_dma_blob { -+ struct __attribute__((packed)) fiq_dma_channel channel[0]; -+}; -+ -+/** -+ * struct fiq_hs_isoc_info - USB2.0 isochronous data -+ * @iso_frame: Pointer to the array of OTG URB iso_frame_descs. -+ * @nrframes: Total length of iso_frame_desc array -+ * @index: Current index (FIQ-maintained) -+ * -+ */ -+struct fiq_hs_isoc_info { -+ struct dwc_otg_hcd_iso_packet_desc *iso_desc; -+ unsigned int nrframes; -+ unsigned int index; -+}; -+ -+/** -+ * struct fiq_channel_state - FIQ state machine storage -+ * @fsm: Current state of the channel as understood by the FIQ -+ * @nr_errors: Number of transaction errors on this split-transaction -+ * @hub_addr: SSPLIT/CSPLIT destination hub -+ * @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub -+ * @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For -+ * split-IN, number of CSPLIT data packets that were received. -+ * @hcchar_copy: -+ * @hcsplt_copy: -+ * @hcintmsk_copy: -+ * @hctsiz_copy: Copies of the host channel registers. -+ * For use as scratch, or for returning state. -+ * -+ * The fiq_channel_state is state storage between interrupts for a host channel. The -+ * FSM state is stored here. Members of this structure must only be set up by the -+ * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ -+ * has updated the state to either a COMPLETE state group or ABORT state group. -+ */ -+ -+struct fiq_channel_state { -+ enum fiq_fsm_state fsm; -+ unsigned int nr_errors; -+ unsigned int hub_addr; -+ unsigned int port_addr; -+ /* Hardware bug workaround: sometimes channel halt interrupts are -+ * delayed until the next SOF. Keep track of when we expected to get interrupted. */ -+ unsigned int expected_uframe; -+ /* in/out for communicating number of dma buffers used, or number of ISOC to do */ -+ unsigned int nrpackets; -+ struct fiq_dma_info dma_info; -+ struct fiq_hs_isoc_info hs_isoc_info; -+ /* Copies of HC registers - in/out communication from/to IRQ handler -+ * and for ease of channel setup. A bit of mungeing is performed - for -+ * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint. -+ */ -+ hcchar_data_t hcchar_copy; -+ hcsplt_data_t hcsplt_copy; -+ hcint_data_t hcint_copy; -+ hcintmsk_data_t hcintmsk_copy; -+ hctsiz_data_t hctsiz_copy; -+ hcdma_data_t hcdma_copy; -+}; -+ -+/** -+ * struct fiq_state - top-level FIQ state machine storage -+ * @mphi_regs: virtual address of the MPHI peripheral register file -+ * @dwc_regs_base: virtual address of the base of the DWC core register file -+ * @dma_base: physical address for the base of the DMA bounce buffers -+ * @dummy_send: Scratch area for sending a fake message to the MPHI peripheral -+ * @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled. -+ * Used for determining which interrupts fired to set off the IRQ handler. -+ * @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally. -+ * @np_count: Non-periodic transactions in the active queue -+ * @np_sent: Count of non-periodic transactions that have completed -+ * @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism, -+ * this is the next frame on which a SOF interrupt is required. Used to hold off -+ * passing SOF through to the driver until necessary. -+ * @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host -+ * channels configured into the core logic. -+ * -+ * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub. -+ * It contains top-level state information. -+ */ -+struct fiq_state { -+ mphi_regs_t mphi_regs; -+ void *dwc_regs_base; -+ dma_addr_t dma_base; -+ struct fiq_dma_blob *fiq_dmab; -+ void *dummy_send; -+ gintmsk_data_t gintmsk_saved; -+ haintmsk_data_t haintmsk_saved; -+ int mphi_int_count; -+ unsigned int fiq_done; -+ unsigned int kick_np_queues; -+ unsigned int next_sched_frame; -+#ifdef FIQ_DEBUG -+ char * buffer; -+ unsigned int bufsiz; -+#endif -+ struct fiq_channel_state channel[0]; -+}; -+ -+extern int fiq_fsm_too_late(struct fiq_state *st, int n); -+ -+extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); -+ -+extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels); -+ -+extern void dwc_otg_fiq_nop(struct fiq_state *state); -+ -+#endif /* DWC_OTG_FIQ_FSM_H_ */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S -new file mode 100644 -index 0000000..ffa8d21 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S -@@ -0,0 +1,80 @@ -+/* -+ * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+#include -+#include -+ -+ -+.text -+ -+.global _dwc_otg_fiq_stub_end; -+ -+/** -+ * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow -+ * a C-style function call with arguments from the FIQ banked registers. -+ * r0 = &hcd->fiq_state -+ * r1 = &hcd->num_channels -+ * r2 = &hcd->dma_buffers -+ * Tramples: r0, r1, r2, r4, fp, ip -+ */ -+ -+ENTRY(_dwc_otg_fiq_stub) -+ /* Stash unbanked regs - SP will have been set up for us */ -+ mov ip, sp; -+ stmdb sp!, {r0-r12, lr}; -+#ifdef FIQ_DEBUG -+ // Cycle profiling - read cycle counter at start -+ mrc p15, 0, r5, c15, c12, 1; -+#endif -+ /* r11 = fp, don't trample it */ -+ mov r4, fp; -+ /* set EABI frame size */ -+ sub fp, ip, #512; -+ -+ /* for fiq NOP mode - just need state */ -+ mov r0, r8; -+ /* r9 = num_channels */ -+ mov r1, r9; -+ /* r10 = struct *dma_bufs */ -+// mov r2, r10; -+ -+ /* r4 = &fiq_c_function */ -+ blx r4; -+#ifdef FIQ_DEBUG -+ mrc p15, 0, r4, c15, c12, 1; -+ subs r5, r5, r4; -+ // r5 is now the cycle count time for executing the FIQ. Store it somewhere? -+#endif -+ ldmia sp!, {r0-r12, lr}; -+ subs pc, lr, #4; -+_dwc_otg_fiq_stub_end: -+END(_dwc_otg_fiq_stub) -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 986d361..130096b 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -45,9 +45,10 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" -+#include "dwc_otg_fiq_fsm.h" - --extern bool microframe_schedule, nak_holdoff_enable; -+extern bool microframe_schedule; -+extern uint16_t fiq_fsm_mask, nak_holdoff; - - //#define DEBUG_HOST_CHANNELS - #ifdef DEBUG_HOST_CHANNELS -@@ -57,12 +58,6 @@ static int last_sel_trans_num_avail_hc_at_start = 0; - static int last_sel_trans_num_avail_hc_at_end = 0; - #endif /* DEBUG_HOST_CHANNELS */ - --extern int g_next_sched_frame, g_np_count, g_np_sent; -- --extern haint_data_t haint_saved; --extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; --extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; --extern gintsts_data_t ginsts_saved; - - dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) - { -@@ -295,7 +290,7 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - */ - dwc_otg_hcd->flags.b.port_connect_status_change = 1; - dwc_otg_hcd->flags.b.port_connect_status = 0; -- if(fiq_fix_enable) -+ if(fiq_enable) - local_fiq_disable(); - /* - * Shutdown any transfers in process by clearing the Tx FIFO Empty -@@ -392,20 +387,15 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - channel->qh = NULL; - } - } -- if(fiq_split_enable) { -+ if(fiq_fsm_enable) { - for(i=0; i < 128; i++) { - dwc_otg_hcd->hub_port[i] = 0; - } -- haint_saved.d32 = 0; -- for(i=0; i < MAX_EPS_CHANNELS; i++) { -- hcint_saved[i].d32 = 0; -- hcintmsk_saved[i].d32 = 0; -- } - } - - } - -- if(fiq_fix_enable) -+ if(fiq_enable) - local_fiq_enable(); - - if (dwc_otg_hcd->fops->disconnect) { -@@ -542,7 +532,7 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - } - #endif - intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -- if(!intr_mask.b.sofintr) needs_scheduling = 1; -+ if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1; - if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) - /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ - needs_scheduling = 0; -@@ -613,6 +603,7 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - if (urb_qtd->in_process && qh->channel) { - /* The QTD is in process (it has been assigned to a channel). */ - if (hcd->flags.b.port_connect_status) { -+ int n = qh->channel->hc_num; - /* - * If still connected (i.e. in host mode), halt the - * channel so it can be used for other transfers. If -@@ -620,10 +611,16 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - * written to halt the channel since the core is in - * device mode. - */ -- dwc_otg_hc_halt(hcd->core_if, qh->channel, -- DWC_OTG_HC_XFER_URB_DEQUEUE); -- -- dwc_otg_hcd_release_port(hcd, qh); -+ /* In FIQ FSM mode, we need to shut down carefully. -+ * The FIQ may attempt to restart a disabled channel */ -+ if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) { -+ qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE; -+ qh->channel->halt_pending = 1; -+ hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED; -+ } else { -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); -+ } - } - } - -@@ -759,7 +756,6 @@ static void completion_tasklet_func(void *ptr) - - usb_hcd_giveback_urb(hcd->priv, urb, urb->status); - -- fiq_print(FIQDBG_PORTHUB, "COMPLETE"); - - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); - } -@@ -854,6 +850,34 @@ void dwc_otg_hcd_power_up(void *ptr) - cil_hcd_start(core_if); - } - -+void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num) -+{ -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ struct fiq_dma_blob *blob = hcd->fiq_dmab; -+ int i; -+ -+ st->fsm = FIQ_PASSTHROUGH; -+ st->hcchar_copy.d32 = 0; -+ st->hcsplt_copy.d32 = 0; -+ st->hcint_copy.d32 = 0; -+ st->hcintmsk_copy.d32 = 0; -+ st->hctsiz_copy.d32 = 0; -+ st->hcdma_copy.d32 = 0; -+ st->nr_errors = 0; -+ st->hub_addr = 0; -+ st->port_addr = 0; -+ st->expected_uframe = 0; -+ st->nrpackets = 0; -+ st->dma_info.index = 0; -+ for (i = 0; i < 6; i++) -+ st->dma_info.slot_len[i] = 255; -+ st->hs_isoc_info.index = 0; -+ st->hs_isoc_info.iso_desc = NULL; -+ st->hs_isoc_info.nrframes = 0; -+ -+ DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128); -+} -+ - /** - * Frees secondary storage associated with the dwc_otg_hcd structure contained - * in the struct usb_hcd field. -@@ -907,6 +931,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) - DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); - DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); - DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); -+ DWC_FREE(dwc_otg_hcd->fiq_state); - - #ifdef DWC_DEV_SRPCAP - if (dwc_otg_hcd->core_if->power_down == 2 && -@@ -984,6 +1009,59 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - channel); - } - -+ if (fiq_enable) { -+ hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)); -+ if (!hcd->fiq_state) { -+ retval = -DWC_E_NO_MEMORY; -+ DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__); -+ dwc_otg_hcd_free(hcd); -+ goto out; -+ } -+ DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels))); -+ -+ for (i = 0; i < num_channels; i++) { -+ hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH; -+ } -+ hcd->fiq_state->dummy_send = DWC_ALLOC_ATOMIC(16); -+ -+ hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack)); -+ if (!hcd->fiq_stack) { -+ retval = -DWC_E_NO_MEMORY; -+ DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__); -+ dwc_otg_hcd_free(hcd); -+ goto out; -+ } -+ hcd->fiq_stack->magic1 = 0xDEADBEEF; -+ hcd->fiq_stack->magic2 = 0xD00DFEED; -+ hcd->fiq_state->gintmsk_saved.d32 = ~0; -+ hcd->fiq_state->haintmsk_saved.b2.chint = ~0; -+ -+ /* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools -+ * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels) -+ * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some -+ * moderately readable array casts. -+ */ -+ hcd->fiq_dmab = DWC_DMA_ALLOC((sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base); -+ DWC_WARN("FIQ DMA bounce buffers: virt = 0x%08x dma = 0x%08x len=%d", -+ (unsigned int)hcd->fiq_dmab, (unsigned int)hcd->fiq_state->dma_base, -+ sizeof(struct fiq_dma_channel) * num_channels); -+ -+ DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024); -+ -+ /* pointer for debug in fiq_print */ -+ hcd->fiq_state->fiq_dmab = hcd->fiq_dmab; -+ if (fiq_fsm_enable) { -+ int i; -+ for (i=0; i < hcd->core_if->core_params->host_channels; i++) { -+ dwc_otg_cleanup_fiq_channel(hcd, i); -+ } -+ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s", -+ (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", -+ (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", -+ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : ""); -+ } -+ } -+ - /* Initialize the Connection timeout timer. */ - hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", - dwc_otg_hcd_connect_timeout, 0); -@@ -1181,7 +1259,8 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->do_split = 1; - hc->xact_pos = qtd->isoc_split_pos; - /* We don't need to do complete splits anymore */ -- if(fiq_split_enable) -+// if(fiq_fsm_enable) -+ if (0) - hc->complete_split = qtd->complete_split = 0; - else - hc->complete_split = qtd->complete_split; -@@ -1332,62 +1411,487 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->qh = qh; - } - --/* --** Check the transaction to see if the port / hub has already been assigned for --** a split transaction --** --** Return 0 - Port is already in use --*/ --int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+ -+/** -+ * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ -+ * @qh: pointer to the endpoint's queue head -+ * -+ * Transaction start/end control flow is grafted onto the existing dwc_otg -+ * mechanisms, to avoid spaghettifying the functions more than they already are. -+ * This function's eligibility check is altered by debug parameter. -+ * -+ * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction. -+ */ -+ -+int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh) - { -- uint32_t hub_addr, port_addr; -+ if (qh->do_split) { -+ switch (qh->ep_type) { -+ case UE_CONTROL: -+ case UE_BULK: -+ if (fiq_fsm_mask & (1 << 0)) -+ return 1; -+ break; -+ case UE_INTERRUPT: -+ case UE_ISOCHRONOUS: -+ if (fiq_fsm_mask & (1 << 1)) -+ return 1; -+ break; -+ default: -+ break; -+ } -+ } else if (qh->ep_type == UE_ISOCHRONOUS) { -+ if (fiq_fsm_mask & (1 << 2)) { -+ /* HS ISOCH support. We test for compatibility: -+ * - DWORD aligned buffers -+ * - Must be at least 2 transfers (otherwise pointless to use the FIQ) -+ * If yes, then the fsm enqueue function will handle the state machine setup. -+ */ -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ dwc_otg_hcd_urb_t *urb = qtd->urb; -+ struct dwc_otg_hcd_iso_packet_desc (*iso_descs)[0] = &urb->iso_descs; -+ int nr_iso_frames = urb->packet_count; -+ int i; -+ uint32_t ptr; -+ -+ if (nr_iso_frames < 2) -+ return 0; -+ for (i = 0; i < nr_iso_frames; i++) { -+ ptr = urb->dma + iso_descs[i]->offset; -+ if (ptr & 0x3) { -+ printk_ratelimited("%s: Non-Dword aligned isochronous frame offset." -+ " Cannot queue FIQ-accelerated transfer to device %d endpoint %d\n", -+ __FUNCTION__, qh->channel->dev_addr, qh->channel->ep_num); -+ return 0; -+ } -+ } -+ return 1; -+ } -+ } -+ return 0; -+} - -- if(!fiq_split_enable) -- return 0; -+/** -+ * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers -+ * @hcd: Pointer to the dwc_otg_hcd struct -+ * @qh: Pointer to the endpoint's queue head -+ * -+ * Periodic split transactions are transmitted modulo 188 bytes. -+ * This necessitates slicing data up into buckets for isochronous out -+ * and fixing up the DMA address for all IN transfers. -+ * -+ * Returns 1 if the DMA bounce buffers have been used, 0 if the default -+ * HC buffer has been used. -+ */ -+int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh) -+ { -+ int frame_length, i = 0; -+ uint8_t *ptr = NULL; -+ dwc_hc_t *hc = qh->channel; -+ struct fiq_dma_blob *blob; -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc; -+ -+ for (i = 0; i < 6; i++) { -+ st->dma_info.slot_len[i] = 255; -+ } -+ st->dma_info.index = 0; -+ i = 0; -+ if (hc->ep_is_in) { -+ /* -+ * Set dma_regs to bounce buffer. FIQ will update the -+ * state depending on transaction progress. -+ */ -+ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; -+ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; -+ /* Calculate the max number of CSPLITS such that the FIQ can time out -+ * a transaction if it fails. -+ */ -+ frame_length = st->hcchar_copy.b.mps; -+ do { -+ i++; -+ frame_length -= 188; -+ } while (frame_length >= 0); -+ st->nrpackets = i; -+ return 1; -+ } else { -+ if (qh->ep_type == UE_ISOCHRONOUS) { - -- hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - -- if(hcd->hub_port[hub_addr] & (1 << port_addr)) -- { -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count); -+ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ frame_length = frame_desc->length; - -- qh->skip_count++; -+ /* Virtual address for bounce buffers */ -+ blob = hcd->fiq_dmab; - -- if(qh->skip_count > 40000) -- { -- printk_once(KERN_ERR "Error: Having to skip port allocation"); -- local_fiq_disable(); -- BUG(); -+ ptr = qtd->urb->buf + frame_desc->offset; -+ if (frame_length == 0) { -+ /* -+ * for isochronous transactions, we must still transmit a packet -+ * even if the length is zero. -+ */ -+ st->dma_info.slot_len[0] = 0; -+ st->nrpackets = 1; -+ } else { -+ do { -+ if (frame_length <= 188) { -+ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length); -+ st->dma_info.slot_len[i] = frame_length; -+ ptr += frame_length; -+ } else { -+ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188); -+ st->dma_info.slot_len[i] = 188; -+ ptr += 188; -+ } -+ i++; -+ frame_length -= 188; -+ } while (frame_length > 0); -+ st->nrpackets = i; -+ } -+ ptr = qtd->urb->buf + frame_desc->offset; -+ /* Point the HC at the DMA address of the bounce buffers */ -+ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; -+ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; -+ -+ /* fixup xfersize to the actual packet size */ -+ st->hctsiz_copy.b.pid = 0; -+ st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0]; -+ return 1; -+ } else { -+ /* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */ - return 0; - } -- return 1; - } -- else -- { -- qh->skip_count = 0; -- hcd->hub_port[hub_addr] |= 1 << port_addr; -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd); --#endif -+} -+ -+/* -+ * Pushing a periodic request into the queue near the EOF1 point -+ * in a microframe causes erroneous behaviour (frmovrun) interrupt. -+ * Usually, the request goes out on the bus causing a transfer but -+ * the core does not transfer the data to memory. -+ * This guard interval (in number of 60MHz clocks) is required which -+ * must cater for CPU latency between reading the value and enabling -+ * the channel. -+ */ -+#define PERIODIC_FRREM_BACKOFF 1000 -+ -+int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) -+{ -+ dwc_hc_t *hc = qh->channel; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ int frame; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; -+ int xfer_len, nrpackets; -+ hcdma_data_t hcdma; -+ hfnum_data_t hfnum; -+ -+ if (st->fsm != FIQ_PASSTHROUGH) - return 0; -+ -+ st->nr_errors = 0; -+ -+ st->hcchar_copy.d32 = 0; -+ st->hcchar_copy.b.mps = hc->max_packet; -+ st->hcchar_copy.b.epdir = hc->ep_is_in; -+ st->hcchar_copy.b.devaddr = hc->dev_addr; -+ st->hcchar_copy.b.epnum = hc->ep_num; -+ st->hcchar_copy.b.eptype = hc->ep_type; -+ -+ st->hcintmsk_copy.b.chhltd = 1; -+ -+ frame = dwc_otg_hcd_get_frame_number(hcd); -+ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; -+ -+ st->hcchar_copy.b.lspddev = 0; -+ /* Enable the channel later as a final register write. */ -+ -+ st->hcsplt_copy.d32 = 0; -+ -+ st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs; -+ st->hs_isoc_info.nrframes = qtd->urb->packet_count; -+ /* grab the next DMA address offset from the array */ -+ st->hcdma_copy.d32 = qtd->urb->dma; -+ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset; -+ -+ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as -+ * the core needs to be told to send the correct number. Caution: for IN transfers, -+ * this is always set to the maximum size of the endpoint. */ -+ xfer_len = st->hs_isoc_info.iso_desc[0].length; -+ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; -+ if (nrpackets == 0) -+ nrpackets = 1; -+ st->hcchar_copy.b.multicnt = nrpackets; -+ st->hctsiz_copy.b.pktcnt = nrpackets; -+ -+ /* Initial PID also needs to be set */ -+ if (st->hcchar_copy.b.epdir == 0) { -+ st->hctsiz_copy.b.xfersize = xfer_len; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_MDATA; -+ break; -+ } -+ -+ } else { -+ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA1; -+ break; -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA2; -+ break; -+ } - } -+ -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); -+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -+ local_fiq_disable(); -+ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); -+ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { -+ /* Prevent queueing near EOF1. Bad things happen if a periodic -+ * split transaction is queued very close to EOF. -+ */ -+ st->fsm = FIQ_HS_ISOC_SLEEPING; -+ } else { -+ st->fsm = FIQ_HS_ISOC_TURBO; -+ st->hcchar_copy.b.chen = 1; -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ } -+ mb(); -+ st->hcchar_copy.b.chen = 0; -+ local_fiq_enable(); -+ return 0; - } --void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+ -+ -+/** -+ * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state -+ * @hcd: Pointer to the dwc_otg_hcd struct -+ * @qh: Pointer to the endpoint's queue head -+ * -+ * This overrides the dwc_otg driver's normal method of queueing a transaction. -+ * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup -+ * for the nominated host channel. -+ * -+ * For periodic transfers, it also peeks at the FIQ state to see if an immediate -+ * start is possible. If not, then the FIQ is left to start the transfer. -+ */ -+int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - { -- uint32_t hub_addr, port_addr; -+ int start_immediate = 1, i; -+ hfnum_data_t hfnum; -+ dwc_hc_t *hc = qh->channel; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; -+ /* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */ -+ int hub_addr, port_addr, frame, uframe; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; - -- if(!fiq_split_enable) -- return; -+ if (st->fsm != FIQ_PASSTHROUGH) -+ return 0; -+ st->nr_errors = 0; -+ -+ st->hcchar_copy.d32 = 0; -+ st->hcchar_copy.b.mps = hc->max_packet; -+ st->hcchar_copy.b.epdir = hc->ep_is_in; -+ st->hcchar_copy.b.devaddr = hc->dev_addr; -+ st->hcchar_copy.b.epnum = hc->ep_num; -+ st->hcchar_copy.b.eptype = hc->ep_type; -+ if (hc->ep_type & 0x1) { -+ if (hc->ep_is_in) -+ st->hcchar_copy.b.multicnt = 3; -+ else -+ /* Docs say set this to 1, but driver sets to 0! */ -+ st->hcchar_copy.b.multicnt = 0; -+ } else { -+ st->hcchar_copy.b.multicnt = 1; -+ st->hcchar_copy.b.oddfrm = 0; -+ } -+ st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0; -+ /* Enable the channel later as a final register write. */ -+ -+ st->hcsplt_copy.d32 = 0; -+ if(qh->do_split) { -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ st->hcsplt_copy.b.compsplt = 0; -+ st->hcsplt_copy.b.spltena = 1; -+ // XACTPOS is for isoc-out only but needs initialising anyway. -+ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL; -+ if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) { -+ /* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL. -+ * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ -+ * will update as necessary. -+ */ -+ if (hc->xfer_len > 188) { -+ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN; -+ } -+ } -+ st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr; -+ st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr; -+ st->hub_addr = hub_addr; -+ st->port_addr = port_addr; -+ } - -- hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ st->hctsiz_copy.d32 = 0; -+ st->hctsiz_copy.b.dopng = 0; -+ st->hctsiz_copy.b.pid = hc->data_pid_start; - -- hcd->hub_port[hub_addr] &= ~(1 << port_addr); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1; --#endif -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+ if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { -+ hc->xfer_len = hc->max_packet; -+ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { -+ hc->xfer_len = 188; -+ } -+ st->hctsiz_copy.b.xfersize = hc->xfer_len; -+ -+ st->hctsiz_copy.b.pktcnt = 1; - -+ if (hc->ep_type & 0x1) { -+ /* -+ * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers, -+ * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array. -+ * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt -+ * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set -+ * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ -+ * must not touch internal driver state. -+ */ -+ if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) { -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); -+ } -+ } -+ } else { -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); -+ } -+ } -+ /* The FIQ depends upon no other interrupts being enabled except channel halt. -+ * Fixup channel interrupt mask. */ -+ st->hcintmsk_copy.d32 = 0; -+ st->hcintmsk_copy.b.chhltd = 1; -+ st->hcintmsk_copy.b.ahberr = 1; -+ -+ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); -+ -+ local_fiq_disable(); -+ mb(); -+ -+ if (hc->ep_type & 0x1) { -+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -+ frame = (hfnum.b.frnum & ~0x7) >> 3; -+ uframe = hfnum.b.frnum & 0x7; -+ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { -+ /* Prevent queueing near EOF1. Bad things happen if a periodic -+ * split transaction is queued very close to EOF. -+ */ -+ start_immediate = 0; -+ } else if (uframe == 5) { -+ start_immediate = 0; -+ } else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) { -+ start_immediate = 0; -+ } else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) { -+ start_immediate = 0; -+ } else { -+ /* Search through all host channels to determine if a transaction -+ * is currently in progress */ -+ for (i = 0; i < hcd->core_if->core_params->host_channels; i++) { -+ if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ switch (hcd->fiq_state->channel[i].fsm) { -+ /* TT is reserved for channels that are in the middle of a periodic -+ * split transaction. -+ */ -+ case FIQ_PER_SSPLIT_STARTED: -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ case FIQ_PER_ISO_OUT_LAST: -+ if (hcd->fiq_state->channel[i].hub_addr == hub_addr && -+ hcd->fiq_state->channel[i].port_addr == port_addr) { -+ start_immediate = 0; -+ } -+ break; -+ default: -+ break; -+ } -+ if (!start_immediate) -+ break; -+ } -+ } -+ } -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); -+ switch (hc->ep_type) { -+ case UE_CONTROL: -+ case UE_BULK: -+ st->fsm = FIQ_NP_SSPLIT_STARTED; -+ break; -+ case UE_ISOCHRONOUS: -+ if (hc->ep_is_in) { -+ if (start_immediate) { -+ st->fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; -+ } -+ } else { -+ if (start_immediate) { -+ /* Single-isoc OUT packets don't require FIQ involvement */ -+ if (st->nrpackets == 1) { -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_PENDING; -+ } -+ } -+ break; -+ case UE_INTERRUPT: -+ if (start_immediate) { -+ st->fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; -+ } -+ default: -+ break; -+ } -+ if (start_immediate) { -+ /* Set the oddfrm bit as close as possible to actual queueing */ -+ frame = dwc_otg_hcd_get_frame_number(hcd); -+ st->expected_uframe = (frame + 1) & 0x3FFF; -+ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; -+ st->hcchar_copy.b.chen = 1; -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ } -+ mb(); -+ local_fiq_enable(); -+ return 0; - } - - -@@ -1404,16 +1908,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - { - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -- dwc_otg_qtd_t *qtd; - int num_channels; - dwc_irqflags_t flags; - dwc_spinlock_t *channel_lock = hcd->channel_lock; - dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; - --#ifdef DEBUG_SOF -- DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); --#endif -- - #ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_per_scheduled = 0; - last_sel_trans_num_nonper_scheduled = 0; -@@ -1428,26 +1927,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - -- if(qh->do_split) { -- qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -- if(!(qh->ep_type == UE_ISOCHRONOUS && -- (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || -- qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) { -- if(dwc_otg_hcd_allocate_port(hcd, qh)) -- { -- qh_ptr = DWC_LIST_NEXT(qh_ptr); -- g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1); -- continue; -- } -- } -- } -- - if (microframe_schedule) { - // Make sure we leave one channel for non periodic transactions. - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels <= 1) { - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -- if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh); - break; - } - hcd->available_host_channels--; -@@ -1483,27 +1967,24 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -- - /* - * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission - * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed - * cheeky devices that just hold off using NAKs - */ -- if (nak_holdoff_enable && qh->do_split) { -- if (qh->nak_frame != 0xffff && -- dwc_full_frame_num(qh->nak_frame) == -- dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) { -- /* -- * Revisit: Need to avoid trampling on periodic scheduling. -- * Currently we are safe because g_np_count != g_np_sent whenever we hit this, -- * but if this behaviour is changed then periodic endpoints will get a slower -- * polling rate. -- */ -- g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM; -- qh_ptr = DWC_LIST_NEXT(qh_ptr); -- continue; -- } else { -- qh->nak_frame = 0xffff; -+ if (nak_holdoff && qh->do_split) { -+ if (qh->nak_frame != 0xffff) { -+ uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8); -+ uint16_t frame = dwc_otg_hcd_get_frame_number(hcd); -+ if (dwc_frame_num_le(frame, next_frame)) { -+ if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) { -+ hcd->fiq_state->next_sched_frame = next_frame; -+ } -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ continue; -+ } else { -+ qh->nak_frame = 0xFFFF; -+ } - } - } - -@@ -1532,12 +2013,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - -- g_np_sent++; - - if (!microframe_schedule) - hcd->non_periodic_channels++; - } -- -+ /* we moved a non-periodic QH to the active schedule. If the inactive queue is empty, -+ * stop the FIQ from kicking us. We could potentially still have elements here if we -+ * ran out of host channels. -+ */ -+ if (fiq_enable) { -+ if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) { -+ hcd->fiq_state->kick_np_queues = 0; -+ } else { -+ /* For each entry remaining in the NP inactive queue, -+ * if this a NAK'd retransmit then don't set the kick flag. -+ */ -+ if(nak_holdoff) { -+ DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) { -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ if (qh->nak_frame == 0xFFFF) { -+ hcd->fiq_state->kick_np_queues = 1; -+ } -+ } -+ } -+ } -+ } - if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) - ret_val |= DWC_OTG_TRANSACTION_PERIODIC; - -@@ -1582,6 +2082,12 @@ static int queue_transaction(dwc_otg_hcd_t * hcd, - hc->qh->ping_state = 0; - } - } else if (!hc->xfer_started) { -+ if (fiq_fsm_enable && hc->error_state) { -+ hcd->fiq_state->channel[hc->hc_num].nr_errors = -+ DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count; -+ hcd->fiq_state->channel[hc->hc_num].fsm = -+ FIQ_PASSTHROUGH_ERRORSTATE; -+ } - dwc_otg_hc_start_transfer(hcd->core_if, hc); - hc->qh->ping_state = 0; - } -@@ -1634,7 +2140,7 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - hptxsts_data_t tx_status; - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -- int status; -+ int status = 0; - int no_queue_space = 0; - int no_fifo_space = 0; - -@@ -1663,27 +2169,34 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - - // Do not send a split start transaction any later than frame .6 - // Note, we have to schedule a periodic in .5 to make it go in .6 -- if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) - { - qh_ptr = qh_ptr->next; -- g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; - continue; - } - -- /* -- * Set a flag if we're queuing high-bandwidth in slave mode. -- * The flag prevents any halts to get into the request queue in -- * the middle of multiple high-bandwidth packets getting queued. -- */ -- if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -- hcd->core_if->queuing_high_bandwidth = 1; -- } -- status = -- queue_transaction(hcd, qh->channel, -- tx_status.b.ptxfspcavail); -- if (status < 0) { -- no_fifo_space = 1; -- break; -+ if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { -+ if (qh->do_split) -+ fiq_fsm_queue_split_transaction(hcd, qh); -+ else -+ fiq_fsm_queue_isoc_transaction(hcd, qh); -+ } else { -+ -+ /* -+ * Set a flag if we're queueing high-bandwidth in slave mode. -+ * The flag prevents any halts to get into the request queue in -+ * the middle of multiple high-bandwidth packets getting queued. -+ */ -+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -+ hcd->core_if->queuing_high_bandwidth = 1; -+ } -+ status = queue_transaction(hcd, qh->channel, -+ tx_status.b.ptxfspcavail); -+ if (status < 0) { -+ no_fifo_space = 1; -+ break; -+ } - } - - /* -@@ -1800,25 +2313,19 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) - qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, - qh_list_entry); - -- // Do not send a split start transaction any later than frame .5 -- // non periodic transactions will start immediately in this uframe -- if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -- { -- g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -- break; -- } -- -- status = -- queue_transaction(hcd, qh->channel, -- tx_status.b.nptxfspcavail); -+ if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { -+ fiq_fsm_queue_split_transaction(hcd, qh); -+ } else { -+ status = queue_transaction(hcd, qh->channel, -+ tx_status.b.nptxfspcavail); - -- if (status > 0) { -- more_to_do = 1; -- } else if (status < 0) { -- no_fifo_space = 1; -- break; -+ if (status > 0) { -+ more_to_do = 1; -+ } else if (status < 0) { -+ no_fifo_space = 1; -+ break; -+ } - } -- - /* Advance to next QH, skipping start-of-list entry. */ - hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; - if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -index 0007fa1..43dbed9 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -40,6 +40,8 @@ - #include "dwc_otg_core_if.h" - #include "dwc_list.h" - #include "dwc_otg_cil.h" -+#include "dwc_otg_fiq_fsm.h" -+ - - /** - * @file -@@ -585,6 +587,12 @@ struct dwc_otg_hcd { - /** Frame List DMA address */ - dma_addr_t frame_list_dma; - -+ struct fiq_stack *fiq_stack; -+ struct fiq_state *fiq_state; -+ -+ /** Virtual address for split transaction DMA bounce buffers */ -+ struct fiq_dma_blob *fiq_dmab; -+ - #ifdef DEBUG - uint32_t frrem_samples; - uint64_t frrem_accum; -@@ -615,6 +623,9 @@ extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); - void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); - -+extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); -+extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh); -+extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num); - - /** @} */ - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 64d33a5..4195ff2 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -34,7 +34,6 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" - - #include - #include -@@ -47,33 +46,8 @@ extern bool microframe_schedule; - * This file contains the implementation of the HCD Interrupt handlers. - */ - --/* -- * Some globals to communicate between the FIQ and INTERRUPT -- */ -- --void * dummy_send; --mphi_regs_t c_mphi_regs; --volatile void *dwc_regs_base; - int fiq_done, int_done; - --gintsts_data_t gintsts_saved = {.d32 = 0}; --hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; --hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; --int split_out_xfersize[MAX_EPS_CHANNELS]; --haint_data_t haint_saved; -- --int g_next_sched_frame, g_np_count, g_np_sent; --static int mphi_int_count = 0 ; -- --hcchar_data_t nak_hcchar; --hctsiz_data_t nak_hctsiz; --hcsplt_data_t nak_hcsplt; --int nak_count; -- --int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; --int split_start_frame[MAX_EPS_CHANNELS]; --int queued_port[MAX_EPS_CHANNELS]; -- - #ifdef FIQ_DEBUG - char buffer[1000*16]; - int wptr; -@@ -83,12 +57,10 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) - va_list args; - char text[17]; - hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; -- unsigned long flags; - -- local_irq_save(flags); -- local_fiq_disable(); - if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) - { -+ local_fiq_disable(); - snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); - va_start(args, fmt); - vsnprintf(text+8, 9, fmt, args); -@@ -96,410 +68,21 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) - - memcpy(buffer + wptr, text, 16); - wptr = (wptr + 16) % sizeof(buffer); -+ local_fiq_enable(); - } -- local_irq_restore(flags); - } - #endif - --void notrace fiq_queue_request(int channel, int odd_frame) --{ -- hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -- hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -- hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) }; -- -- if(hcsplt.b.spltena == 0) -- { -- fiq_print(FIQDBG_ERR, "SPLTENA "); -- BUG(); -- } -- -- if(hcchar.b.epdir == 1) -- { -- fiq_print(FIQDBG_SCHED, "IN Ch %d", channel); -- } -- else -- { -- hctsiz.b.xfersize = 0; -- fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel); -- } -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32); -- -- hcsplt.b.compsplt = 1; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32); -- -- // Send the Split complete -- hcchar.b.chen = 1; -- hcchar.b.oddfrm = odd_frame ? 1 : 0; -- -- // Post this for transmit on the next frame for periodic or this frame for non-periodic -- fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN"); -- -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32); --} -- --static int last_sof = -1; -- --/* --** Function to handle the start of frame interrupt, choose whether we need to do anything and --** therefore trigger the main interrupt --** --** returns int != 0 - interrupt has been handled --*/ --int diff; -- --int notrace fiq_sof_handle(hfnum_data_t hfnum) --{ -- int handled = 0; -- int i; -- -- // Just check that once we're running we don't miss a SOF -- /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff))) -- { -- fiq_print(FIQDBG_ERR, "LASTSOF "); -- fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7); -- fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7); -- BUG(); -- }*/ -- -- // Only start remembering the last sof when the interrupt has been -- // enabled (we don't check the mask to come in here...) -- if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3)) -- last_sof = hfnum.b.frnum; -- -- for(i = 0; i < MAX_EPS_CHANNELS; i++) -- { -- if(complete_sched[i] != -1) -- { -- if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0)) -- { -- fiq_queue_request(i, hfnum.b.frnum & 1); -- complete_sched[i] = -1; -- } -- } -- -- if(complete_sched[i] != -1) -- { -- // This is because we've seen a split complete occur with no start... -- // most likely because missed the complete 0x3fff frames ago! -- -- diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ; -- if(diff > 32 && diff < 0x3f00) -- { -- fiq_print(FIQDBG_ERR, "SPLTMISS"); -- BUG(); -- } -- } -- } -- -- if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -- { -- /* -- * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet -- * g_next_sched_frame is the next frame we have periodic packets for -- * -- * if neither of these are required for this frame then just clear the interrupt -- */ -- handled = 1; -- -- } -- -- return handled; --} -- --int notrace port_id(hcsplt_data_t hcsplt) --{ -- return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8); --} -- --int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum) --{ -- hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -- hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -- hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) }; -- hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) }; -- hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)}; -- -- hcint_saved[channel].d32 |= hcint.d32; -- hcintmsk_saved[channel].d32 = hcintmsk.d32; -- -- if(hcsplt.b.spltena) -- { -- fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt)); -- if(hcint.b.chhltd) -- { -- fiq_print(FIQDBG_SCHED, "CH HLT %d", channel); -- fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]); -- } -- if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_ERR, "CHAN ERR"); -- } -- if(hcint.b.xfercomp) -- { -- // Clear the port allocation and transmit anything also on this port -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "XFERCOMP"); -- } -- if(hcint.b.nak) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "NAK"); -- } -- if(hcint.b.ack && !hcsplt.b.compsplt) -- { -- int i; -- -- // Do not complete isochronous out transactions -- if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "ISOC_OUT"); -- } -- else -- { -- // Make sure we check the port / hub combination that we sent this split on. -- // Do not queue a second request to the same port -- for(i = 0; i < MAX_EPS_CHANNELS; i++) -- { -- if(port_id(hcsplt) == queued_port[i]) -- { -- fiq_print(FIQDBG_ERR, "PORTERR "); -- //BUG(); -- } -- } -- -- split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7; -- -- // Note, the size of an OUT is in the start split phase, not -- // the complete split -- split_out_xfersize[channel] = hctsiz.b.xfersize; -- -- hcint_saved[channel].b.chhltd = 0; -- hcint_saved[channel].b.ack = 0; -- -- queued_port[channel] = port_id(hcsplt); -- -- if(hcchar.b.eptype & 1) -- { -- // Send the periodic complete in the same oddness frame as the ACK went... -- fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -- // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -- } -- else -- { -- // Schedule the split complete to occur later -- complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2); -- fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -- } -- } -- } -- if(hcint.b.nyet) -- { -- fiq_print(FIQDBG_ERR, "NYETERR1"); -- //BUG(); -- // Can transmit a split complete up to uframe .0 of the next frame -- if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8)) -- { -- // Send it next frame -- if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc -- { -- fiq_print(FIQDBG_SCHED, "NYT:SEND"); -- fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -- } -- else -- { -- // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP) -- complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -- fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -- } -- hcint_saved[channel].b.chhltd = 0; -- hcint_saved[channel].b.nyet = 0; -- } -- else -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_ERR, "NYETERR2"); -- //BUG(); -- } -- } -- } -- else -- { -- /* -- * If we have any of NAK, ACK, Datatlgerr active on a -- * non-split channel, the sole reason is to reset error -- * counts for a previously broken transaction. The FIQ -- * will thrash on NAK IN and ACK OUT in particular so -- * handle it "once" and allow the IRQ to do the rest. -- */ -- hcint.d32 &= hcintmsk.d32; -- if(hcint.b.nak) -- { -- hcintmsk.b.nak = 0; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -- } -- if (hcint.b.ack) -- { -- hcintmsk.b.ack = 0; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -- } -- } -- -- // Clear the interrupt, this will also clear the HAINT bit -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32); -- return hcint_saved[channel].d32 == 0; --} -- --gintsts_data_t gintsts; --gintmsk_data_t gintmsk; --// triggered: The set of interrupts that were triggered --// handled: The set of interrupts that have been handled (no IRQ is --// required) --// keep: The set of interrupts we want to keep unmasked even though we --// want to trigger an IRQ to handle it (SOF and HCINTR) --gintsts_data_t triggered, handled, keep; --hfnum_data_t hfnum; -- --void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void) --{ -- -- /* entry takes care to store registers we will be treading on here */ -- asm __volatile__ ( -- "mov ip, sp ;" -- /* stash FIQ and normal regs */ -- "stmdb sp!, {r0-r12, lr};" -- /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ -- "sub fp, ip, #512 ;" -- ); -- -- // Cannot put local variables at the beginning of the function -- // because otherwise 'C' will play with the stack pointer. any locals -- // need to be inside the following block -- do -- { -- fiq_done++; -- gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14); -- gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18); -- hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408); -- triggered.d32 = gintsts.d32 & gintmsk.d32; -- handled.d32 = 0; -- keep.d32 = 0; -- fiq_print(FIQDBG_INT, "FIQ "); -- fiq_print(FIQDBG_INT, "%08x", gintsts.d32); -- fiq_print(FIQDBG_INT, "%08x", gintmsk.d32); -- if(gintsts.d32) -- { -- // If port enabled -- if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5) -- { -- if(gintsts.b.sofintr) -- { -- if(fiq_sof_handle(hfnum)) -- { -- handled.b.sofintr = 1; /* Handled in FIQ */ -- } -- else -- { -- /* Keer interrupt unmasked */ -- keep.b.sofintr = 1; -- } -- { -- // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing -- // a start of frame interrupt -- gintsts_data_t gintsts = { .b.sofintr = 1 }; -- FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -- } -- } -- -- if(fiq_split_enable && gintsts.b.hcintr) -- { -- int i; -- haint_data_t haint; -- haintmsk_data_t haintmsk; -- -- haint.d32 = FIQ_READ(dwc_regs_base + 0x414); -- haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418); -- haint.d32 &= haintmsk.d32; -- haint_saved.d32 |= haint.d32; -- -- fiq_print(FIQDBG_INT, "hcintr"); -- fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414)); -- -- // Go through each channel that has an enabled interrupt -- for(i = 0; i < 16; i++) -- if((haint.d32 >> i) & 1) -- if(fiq_hcintr_handle(i, hfnum)) -- haint_saved.d32 &= ~(1 << i); /* this was handled */ -- -- /* If we've handled all host channel interrupts then don't trigger the interrupt */ -- if(haint_saved.d32 == 0) -- { -- handled.b.hcintr = 1; -- } -- else -- { -- /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */ -- keep.b.hcintr = 1; -- } -- -- { -- gintsts_data_t gintsts = { .b.hcintr = 1 }; -- -- // Always clear the channel interrupt -- FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -- } -- } -- } -- else -- { -- last_sof = -1; -- } -- } -- -- // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep -- gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32)); -- // Save those that were triggered but not handled -- gintsts_saved.d32 |= triggered.d32 & ~handled.d32; -- FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -- -- // Clear and save any unhandled interrupts and trigger the interrupt -- if(gintsts_saved.d32) -- { -- /* To enable the MPHI interrupt (INT 32) -- */ -- FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); -- FIQ_WRITE( c_mphi_regs.outddb, (1 << 29)); -- -- mphi_int_count++; -- } -- } -- while(0); -- -- mb(); -- -- /* exit back to normal mode restoring everything */ -- asm __volatile__ ( -- /* return FIQ regs back to pristine state -- * and get normal regs back -- */ -- "ldmia sp!, {r0-r12, lr};" -- -- /* return */ -- "subs pc, lr, #4;" -- ); --} -- - /** This function handles interrupts for the HCD. */ - int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int retval = 0; - static int last_time; -- - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - gintsts_data_t gintsts; - gintmsk_data_t gintmsk; - hfnum_data_t hfnum; -+ haintmsk_data_t haintmsk; - - #ifdef DEBUG - dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; -@@ -516,15 +99,29 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - DWC_SPINLOCK(dwc_otg_hcd->lock); - /* Check if HOST Mode */ - if (dwc_otg_is_host_mode(core_if)) { -- local_fiq_disable(); -- gintmsk.d32 |= gintsts_saved.d32; -- gintsts.d32 |= gintsts_saved.d32; -- gintsts_saved.d32 = 0; -- local_fiq_enable(); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ /* Pull in from the FIQ's disabled mask */ -+ gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); -+ dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; -+ } -+ -+ if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) { -+ gintsts.b.hcintr = 1; -+ } -+ -+ /* Danger will robinson: fake a SOF if necessary */ -+ if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) { -+ gintsts.b.sofintr = 1; -+ } -+ gintsts.d32 &= gintmsk.d32; -+ -+ if (fiq_enable) -+ local_fiq_enable(); -+ - if (!gintsts.d32) { - goto exit_handler_routine; - } -- gintsts.d32 &= gintmsk.d32; - - #ifdef DEBUG - // We should be OK doing this because the common interrupts should already have been serviced -@@ -544,12 +141,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - gintsts.d32, core_if); - #endif - hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); -- if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -- { -- /* Note, we should never get here if the FIQ is doing it's job properly*/ -- retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); -- } -- else if (gintsts.b.sofintr) { -+ if (gintsts.b.sofintr) { - retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); - } - -@@ -604,37 +196,43 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - } - - exit_handler_routine: -- -- if (fiq_fix_enable) -- { -+ if (fiq_enable) { -+ gintmsk_data_t gintmsk_new; -+ haintmsk_data_t haintmsk_new; - local_fiq_disable(); -- // Make sure that we don't clear the interrupt if we've still got pending work to do -- if(gintsts_saved.d32 == 0) -- { -- /* Clear the MPHI interrupt */ -- DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16)); -- if (mphi_int_count >= 60) -- { -- DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16))); -- while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17))) -- ; -- DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31)); -- mphi_int_count = 0; -- } -- int_done++; -+ gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; -+ if(fiq_fsm_enable) -+ haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; -+ else -+ haintmsk_new.d32 = 0x0000FFFF; -+ -+ /* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */ -+ if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) { -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16)); -+ if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) { -+ fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR"); -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16))); -+ while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17))) -+ ; -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31)); -+ dwc_otg_hcd->fiq_state->mphi_int_count = 0; -+ } -+ int_done++; - } -- -- // Unmask handled interrupts -- FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -- //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1); -- -+ haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); -+ /* Re-enable interrupts that the FIQ masked (first time round) */ -+ FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); - local_fiq_enable(); - -- if((jiffies / HZ) > last_time) -- { -+ if ((jiffies / HZ) > last_time) { -+ //dwc_otg_qh_t *qh; -+ //dwc_list_link_t *cur; - /* Once a second output the fiq and irq numbers, useful for debug */ - last_time = jiffies / HZ; -- DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done); -+ // DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d", -+ // dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels, -+ // dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done); -+ //printk(KERN_WARNING "Periodic queues:\n"); - } - } - -@@ -686,6 +284,7 @@ static inline void track_missed_sofs(uint16_t curr_frame_number) - int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - { - hfnum_data_t hfnum; -+ gintsts_data_t gintsts = { .d32 = 0 }; - dwc_list_link_t *qh_entry; - dwc_otg_qh_t *qh; - dwc_otg_transaction_type_e tr_type; -@@ -732,8 +331,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - } - } - } -- -- g_next_sched_frame = next_sched_frame; -+ if (fiq_enable) -+ hcd->fiq_state->next_sched_frame = next_sched_frame; - - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { -@@ -741,10 +340,11 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - did_something = 1; - } - -- /* Clear interrupt */ -- gintsts.b.sofintr = 1; -- DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -- -+ /* Clear interrupt - but do not trample on the FIQ sof */ -+ if (!fiq_fsm_enable) { -+ gintsts.b.sofintr = 1; -+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -+ } - return 1; - } - -@@ -1020,19 +620,21 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int i; - int retval = 0; -- haint_data_t haint; -+ haint_data_t haint = { .d32 = 0 } ; - - /* Clear appropriate bits in HCINTn to clear the interrupt bit in - * GINTSTS */ - -- haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); -+ if (!fiq_fsm_enable) -+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); - - // Overwrite with saved interrupts from fiq handler -- if(fiq_split_enable) -+ if(fiq_fsm_enable) - { -+ /* check the mask? */ - local_fiq_disable(); -- haint.d32 = haint_saved.d32; -- haint_saved.d32 = 0; -+ haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); -+ dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; - local_fiq_enable(); - } - -@@ -1076,9 +678,7 @@ static uint32_t get_actual_xfer_length(dwc_hc_t * hc, - *short_read = (hctsiz.b.xfersize != 0); - } - } else if (hc->qh->do_split) { -- if(fiq_split_enable) -- length = split_out_xfersize[hc->hc_num]; -- else -+ //length = split_out_xfersize[hc->hc_num]; - length = qtd->ssplit_out_xfer_count; - } else { - length = hc->xfer_len; -@@ -1325,19 +925,17 @@ static void release_channel(dwc_otg_hcd_t * hcd, - int free_qtd; - dwc_irqflags_t flags; - dwc_spinlock_t *channel_lock = hcd->channel_lock; --#ifdef FIQ_DEBUG -- int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0; --#endif -+ - int hog_port = 0; - - DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", - __func__, hc->hc_num, halt_status, hc->xfer_len); - -- if(fiq_split_enable && hc->do_split) { -+ if(fiq_fsm_enable && hc->do_split) { - if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { - if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || - hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { -- hog_port = 1; -+ hog_port = 0; - } - } - } -@@ -1394,6 +992,8 @@ static void release_channel(dwc_otg_hcd_t * hcd, - * function clears the channel interrupt enables and conditions, so - * there's no need to clear the Channel Halted interrupt separately. - */ -+ if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH) -+ dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num); - dwc_otg_hc_cleanup(hcd->core_if, hc); - DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); - -@@ -1416,27 +1016,10 @@ static void release_channel(dwc_otg_hcd_t * hcd, - - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - hcd->available_host_channels++; -- fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - } - -- if(fiq_split_enable && hc->do_split) -- { -- if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr))) -- { -- fiq_print(FIQDBG_ERR, "PRTNOTAL"); -- //BUG(); -- } -- if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC || -- hc->ep_type == DWC_OTG_EP_TYPE_INTR)) { -- hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1; --#endif -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp); -- } -- } -- - /* Try to queue more transfers now that there's a free channel. */ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { -@@ -1858,7 +1441,7 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { - case UE_BULK: - case UE_CONTROL: -- if (nak_holdoff_enable) -+ if (nak_holdoff && qtd->qh->do_split) - hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); - } - -@@ -2074,7 +1657,7 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, - // With the FIQ running we only ever see the failed NYET - if (dwc_full_frame_num(frnum) != - dwc_full_frame_num(hc->qh->sched_frame) || -- fiq_split_enable) { -+ fiq_fsm_enable) { - /* - * No longer in the same full speed frame. - * Treat this as a transaction error. -@@ -2460,12 +2043,11 @@ static inline int halt_status_ok(dwc_otg_hcd_t * hcd, - static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd, -- hcint_data_t hcint, -- hcintmsk_data_t hcintmsk) -+ dwc_otg_qtd_t * qtd) - { - int out_nak_enh = 0; -- -+ hcint_data_t hcint; -+ hcintmsk_data_t hcintmsk; - /* For core with OUT NAK enhancement, the flow for high- - * speed CONTROL/BULK OUT is handled a little differently. - */ -@@ -2495,11 +2077,9 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - } - - /* Read the HCINTn register to determine the cause for the halt. */ -- if(!fiq_split_enable) -- { -- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -- } -+ -+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); - - if (hcint.b.xfercomp) { - /** @todo This is here because of a possible hardware bug. Spec -@@ -2624,15 +2204,13 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd, -- hcint_data_t hcint, -- hcintmsk_data_t hcintmsk) -+ dwc_otg_qtd_t * qtd) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " - "Channel Halted--\n", hc->hc_num); - - if (hcd->core_if->dma_enable) { -- handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk); -+ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); - } else { - #ifdef DEBUG - if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { -@@ -2645,11 +2223,372 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - return 1; - } - -+ -+/** -+ * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on -+ * FIQ transfer completion -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * 1. Un-mangle the status as recorded in each iso_frame_desc status -+ * 2. Copy it from the dwc_otg_urb into the real URB -+ */ -+void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) -+{ -+ struct dwc_otg_hcd_urb *dwc_urb = qtd->urb; -+ int nr_frames = dwc_urb->packet_count; -+ int i; -+ hcint_data_t frame_hcint; -+ -+ for (i = 0; i < nr_frames; i++) { -+ frame_hcint.d32 = dwc_urb->iso_descs[i].status; -+ if (frame_hcint.b.xfercomp) { -+ dwc_urb->iso_descs[i].status = 0; -+ dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length; -+ } else if (frame_hcint.b.frmovrun) { -+ if (qh->ep_is_in) -+ dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES; -+ else -+ dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else if (frame_hcint.b.xacterr) { -+ dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else if (frame_hcint.b.bblerr) { -+ dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else { -+ /* Something went wrong */ -+ dwc_urb->iso_descs[i].status = -1; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ dwc_urb->error_count++; -+ } -+ } -+ //printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n", -+ // __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count); -+ hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0); -+ release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+} -+ -+/** -+ * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state. -+ * Returns total length of data or -1 if the buffers were not used. -+ * -+ */ -+int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) -+{ -+ dwc_hc_t *hc = qh->channel; -+ struct fiq_dma_blob *blob = hcd->fiq_dmab; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ uint8_t *ptr = NULL; -+ int index = 0, len = 0; -+ int i = 0; -+ if (hc->ep_is_in) { -+ /* Copy data out of the DMA bounce buffers to the URB's buffer. -+ * The align_buf is ignored as this is ignored on FSM enqueue. */ -+ ptr = qtd->urb->buf; -+ if (qh->ep_type == UE_ISOCHRONOUS) { -+ /* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */ -+ index = qtd->isoc_frame_index; -+ ptr += qtd->urb->iso_descs[index].offset; -+ } else { -+ /* Need to increment by actual_length for interrupt IN */ -+ ptr += qtd->urb->actual_length; -+ } -+ -+ for (i = 0; i < st->dma_info.index; i++) { -+ len += st->dma_info.slot_len[i]; -+ dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]); -+ ptr += st->dma_info.slot_len[i]; -+ } -+ return len; -+ } else { -+ /* OUT endpoints - nothing to do. */ -+ return -1; -+ } -+ -+} -+/** -+ * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt -+ * from a channel handled in the FIQ -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * If a host channel interrupt was received by the IRQ and this was a channel -+ * used by the FIQ, the execution flow for transfer completion is substantially -+ * different from the normal (messy) path. This function and its friends handles -+ * channel cleanup and transaction completion from a FIQ transaction. -+ */ -+int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) -+{ -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ dwc_hc_t *hc = hcd->hc_ptr_array[num]; -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); -+ dwc_otg_qh_t *qh = hc->qh; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; -+ hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; -+ int hostchannels = 0; -+ int ret = 0; -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); -+ -+ hostchannels = hcd->available_host_channels; -+ switch (st->fsm) { -+ case FIQ_TEST: -+ break; -+ -+ case FIQ_DEQUEUE_ISSUED: -+ /* hc_halt was called. QTD no longer exists. */ -+ /* TODO: for a nonperiodic split transaction, need to issue a -+ * CLEAR_TT_BUFFER hub command if we were in the start-split phase. -+ */ -+ release_channel(hcd, hc, NULL, hc->halt_status); -+ ret = 1; -+ break; -+ -+ case FIQ_NP_SPLIT_DONE: -+ /* Nonperiodic transaction complete. */ -+ if (!hc->ep_is_in) { -+ qtd->ssplit_out_xfer_count = hc->xfer_len; -+ } -+ if (hcint.b.xfercomp) { -+ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.nak) { -+ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); -+ } -+ ret = 1; -+ break; -+ -+ case FIQ_NP_SPLIT_HS_ABORTED: -+ /* A HS abort is a 3-strikes on the HS bus at any point in the transaction. -+ * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that -+ * because there's no guarantee which order a non-periodic split happened in. -+ * We could end up clearing a perfectly good transaction out of the buffer. -+ */ -+ if (hcint.b.xacterr) { -+ qtd->error_count += st->nr_errors; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.ahberr) { -+ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ local_fiq_disable(); -+ BUG(); -+ } -+ break; -+ -+ case FIQ_NP_SPLIT_LS_ABORTED: -+ /* A few cases can cause this - either an unknown state on a SSPLIT or -+ * STALL/data toggle error response on a CSPLIT */ -+ if (hcint.b.stall) { -+ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.datatglerr) { -+ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.bblerr) { -+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.ahberr) { -+ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ local_fiq_disable(); -+ BUG(); -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_DONE: -+ /* Isoc IN or Interrupt IN/OUT */ -+ -+ /* Flow control here is different from the normal execution by the driver. -+ * We need to completely ignore most of the driver's method of handling -+ * split transactions and do it ourselves. -+ */ -+ if (hc->ep_type == UE_INTERRUPT) { -+ if (hcint.b.nak) { -+ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); -+ } else if (hc->ep_is_in) { -+ int len; -+ len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num); -+ //printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length); -+ qtd->urb->actual_length += len; -+ if (qtd->urb->actual_length >= qtd->urb->length) { -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ /* Interrupt transfer not complete yet - is it a short read? */ -+ if (len < hc->max_packet) { -+ /* Interrupt transaction complete */ -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ /* Further transactions required */ -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ } else { -+ /* Interrupt OUT complete. */ -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ qtd->urb->actual_length += hc->xfer_len; -+ if (qtd->urb->actual_length >= qtd->urb->length) { -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ } else { -+ /* ISOC IN complete. */ -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ int len = 0; -+ /* Record errors, update qtd. */ -+ if (st->nr_errors) { -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ } else { -+ frame_desc->status = 0; -+ /* Unswizzle dma */ -+ len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num); -+ frame_desc->actual_length = len; -+ } -+ qtd->isoc_frame_index++; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_DONE: { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ if (st->nr_errors) { -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ } else { -+ frame_desc->status = 0; -+ frame_desc->actual_length = frame_desc->length; -+ } -+ qtd->isoc_frame_index++; -+ qtd->isoc_split_offset = 0; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_NYET_ABORTED: -+ /* Doh. lost the data. */ -+ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " -+ "- FIQ reported NYET. Data may have been lost.\n", -+ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); -+ if (hc->ep_type == UE_ISOCHRONOUS) { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ qtd->isoc_frame_index++; -+ qtd->isoc_split_offset = 0; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ case FIQ_HS_ISOC_DONE: -+ /* The FIQ has performed a whole pile of isochronous transactions. -+ * The status is recorded as the interrupt state should the transaction -+ * fail. -+ */ -+ dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num); -+ break; -+ -+ case FIQ_PER_SPLIT_LS_ABORTED: -+ if (hcint.b.xacterr) { -+ /* Hub has responded with an ERR packet. Device -+ * has been unplugged or the port has been disabled. -+ * TODO: need to issue a reset to the hub port. */ -+ qtd->error_count += 3; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.stall) { -+ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.bblerr) { -+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed " -+ "- FIQ reported FSM=%d. Data may have been lost.\n", -+ st->fsm, hc->dev_addr, hc->ep_num); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_HS_ABORTED: -+ /* Either the SSPLIT phase suffered transaction errors or something -+ * unexpected happened. -+ */ -+ qtd->error_count += 3; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ /* Couldn't complete in the nominated frame */ -+ printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " -+ "- FIQ timed out. Data may have been lost.\n", -+ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); -+ if (hc->ep_type == UE_ISOCHRONOUS) { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ frame_desc->actual_length = 0; -+ if (hc->ep_is_in) { -+ frame_desc->status = -DWC_E_NO_STREAM_RES; -+ } else { -+ frame_desc->status = -DWC_E_COMMUNICATION; -+ } -+ qtd->isoc_frame_index++; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ default: -+ local_fiq_disable(); -+ DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm); -+ BUG(); -+ } -+ //if (hostchannels != hcd->available_host_channels) { -+ /* should have incremented by now! */ -+ // BUG(); -+// } -+ return ret; -+} -+ - /** Handles interrupt for a specific Host Channel */ - int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - { - int retval = 0; -- hcint_data_t hcint, hcint_orig; -+ hcint_data_t hcint; - hcintmsk_data_t hcintmsk; - dwc_hc_t *hc; - dwc_otg_hc_regs_t *hc_regs; -@@ -2668,24 +2607,32 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - } - qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); - -+ /* -+ * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ. -+ * Execution path is fundamentally different for the channels after a FIQ has completed -+ * a split transaction. -+ */ -+ if (fiq_fsm_enable) { -+ switch (dwc_otg_hcd->fiq_state->channel[num].fsm) { -+ case FIQ_PASSTHROUGH: -+ break; -+ case FIQ_PASSTHROUGH_ERRORSTATE: -+ /* Hook into the error count */ -+ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); -+ if (dwc_otg_hcd->fiq_state->channel[num].nr_errors) { -+ qtd->error_count = 0; -+ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); -+ } -+ break; -+ default: -+ dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num); -+ return 1; -+ } -+ } -+ - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcint_orig = hcint; - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -- DWC_DEBUGPL(DBG_HCDV, -- " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", -- hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); - hcint.d32 = hcint.d32 & hcintmsk.d32; -- -- if(fiq_split_enable) -- { -- // replace with the saved interrupts from the fiq handler -- local_fiq_disable(); -- hcint_orig.d32 = hcint_saved[num].d32; -- hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32; -- hcint_saved[num].d32 = 0; -- local_fiq_enable(); -- } -- - if (!dwc_otg_hcd->core_if->dma_enable) { - if (hcint.b.chhltd && hcint.d32 != 0x2) { - hcint.b.chhltd = 0; -@@ -2703,7 +2650,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - hcint.b.nyet = 0; - } - if (hcint.b.chhltd) { -- retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]); -+ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.ahberr) { - retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index ee8eec9..98e1dc5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -58,6 +58,7 @@ - #else - #include - #endif -+#include - - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) - #define USB_URB_EP_LINKING 1 -@@ -69,7 +70,8 @@ - #include "dwc_otg_dbg.h" - #include "dwc_otg_driver.h" - #include "dwc_otg_hcd.h" --#include "dwc_otg_mphi_fix.h" -+ -+extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end; - - /** - * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is -@@ -80,7 +82,7 @@ - - static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; - --extern bool fiq_fix_enable; -+extern bool fiq_enable; - - /** @name Linux HC Driver API Functions */ - /** @{ */ -@@ -351,7 +353,6 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - urb); - } - } -- - DWC_FREE(dwc_otg_urb); - if (!new_entry) { - DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); -@@ -395,13 +396,9 @@ static struct dwc_otg_hcd_function_ops hcd_fops = { - static struct fiq_handler fh = { - .name = "usb_fiq", - }; --struct fiq_stack_s { -- int magic1; -- uint8_t stack[2048]; -- int magic2; --} fiq_stack; - --extern mphi_regs_t c_mphi_regs; -+ -+ - /** - * Initializes the HCD. This function allocates memory for and initializes the - * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the -@@ -433,20 +430,6 @@ int hcd_init(dwc_bus_dev_t *_dev) - pci_set_consistent_dma_mask(_dev, dmamask); - #endif - -- if (fiq_fix_enable) -- { -- // Set up fiq -- claim_fiq(&fh); -- set_fiq_handler(__FIQ_Branch, 4); -- memset(®s,0,sizeof(regs)); -- regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq; -- regs.ARM_r9 = (long)0; -- regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4; -- set_fiq_regs(®s); -- fiq_stack.magic1 = 0xdeadbeef; -- fiq_stack.magic2 = 0xaa995566; -- } -- - /* - * Allocate memory for the base HCD plus the DWC OTG HCD. - * Initialize the base HCD. -@@ -466,30 +449,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - - hcd->regs = otg_dev->os_dep.base; - -- if (fiq_fix_enable) -- { -- volatile extern void *dwc_regs_base; -- -- //Set the mphi periph to the required registers -- c_mphi_regs.base = otg_dev->os_dep.mphi_base; -- c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -- c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -- c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -- c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -- -- dwc_regs_base = otg_dev->os_dep.base; - -- //Enable mphi peripheral -- writel((1<<31),c_mphi_regs.ctrl); --#ifdef DEBUG -- if (readl(c_mphi_regs.ctrl) & 0x80000000) -- DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n"); -- else -- DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n"); --#endif -- // Enable FIQ interrupt from USB peripheral -- enable_fiq(INTERRUPT_VC_USB); -- } - /* Initialize the DWC OTG HCD. */ - dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); - if (!dwc_otg_hcd) { -@@ -503,6 +463,55 @@ int hcd_init(dwc_bus_dev_t *_dev) - goto error2; - } - -+ if (fiq_enable) -+ { -+ if (claim_fiq(&fh)) { -+ DWC_ERROR("Can't claim FIQ"); -+ goto error2; -+ } -+ -+ DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -+ -+ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -+ memset(®s,0,sizeof(regs)); -+ -+ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -+ if (fiq_fsm_enable) { -+ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -+ //regs.ARM_r10 = dwc_otg_hcd->dma; -+ regs.ARM_fp = (long) dwc_otg_fiq_fsm; -+ } else { -+ regs.ARM_fp = (long) dwc_otg_fiq_nop; -+ } -+ -+ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -+ -+// __show_regs(®s); -+ set_fiq_regs(®s); -+ -+ //Set the mphi periph to the required registers -+ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -+ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -+ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -+ //Enable mphi peripheral -+ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -+ DWC_WARN("MPHI periph has been enabled"); -+ else -+ DWC_WARN("MPHI periph has NOT been enabled"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ local_fiq_enable(); -+ } -+ -+ - otg_dev->hcd->otg_dev = otg_dev; - hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) -@@ -518,9 +527,9 @@ int hcd_init(dwc_bus_dev_t *_dev) - * IRQ line, and calls hcd_start method. - */ - #ifdef PLATFORM_INTERFACE -- retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED | IRQF_DISABLED); -+ retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED | IRQF_DISABLED); - #else -- retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); -+ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); - #endif - if (retval < 0) { - goto error2; -@@ -617,9 +626,13 @@ void hcd_stop(struct usb_hcd *hcd) - /** Returns the current frame number. */ - static int get_frame_number(struct usb_hcd *hcd) - { -+ hprt0_data_t hprt0; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); -- -- return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); -+ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); -+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3; -+ else -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); - } - - #ifdef DEBUG -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 5c22b6c..17d3030 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -41,7 +41,6 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" - - extern bool microframe_schedule; - -@@ -577,7 +576,6 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - } - - --extern int g_next_sched_frame, g_np_count, g_np_sent; - - /** - * Schedules an interrupt or isochronous transfer in the periodic schedule. -@@ -637,9 +635,9 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); - } - else { -- if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame)) -+ if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame))) - { -- g_next_sched_frame = qh->sched_frame; -+ hcd->fiq_state->next_sched_frame = qh->sched_frame; - - } - /* Always start in the inactive schedule. */ -@@ -680,7 +678,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - /* Always start in the inactive schedule. */ - DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, - &qh->qh_list_entry); -- g_np_count++; -+ //hcd->fiq_state->kick_np_queues = 1; - } else { - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { -@@ -740,13 +738,12 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->non_periodic_qh_ptr->next; - } - DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); -- -- // If we've removed the last non-periodic entry then there are none left! -- g_np_count = g_np_sent; -+ //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) -+ // hcd->fiq_state->kick_np_queues = 1; - } else { - deschedule_periodic(hcd, qh); - hcd->periodic_qh_count--; -- if( !hcd->periodic_qh_count ) { -+ if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { - intr_mask.b.sofintr = 1; - DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, - intr_mask.d32, 0); -@@ -771,28 +768,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - int sched_next_periodic_split) - { - if (dwc_qh_is_non_per(qh)) { -- -- dwc_otg_qh_t *qh_tmp; -- dwc_list_link_t *qh_list; -- DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) -- { -- qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry); -- if(qh_tmp == qh) -- { -- /* -- * FIQ is being disabled because this one nevers gets a np_count increment -- * This is still not absolutely correct, but it should fix itself with -- * just an unnecessary extra interrupt -- */ -- g_np_sent = g_np_count; -- } -- } -- -- - dwc_otg_hcd_qh_remove(hcd, qh); - if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { - /* Add back to inactive non-periodic schedule. */ - dwc_otg_hcd_qh_add(hcd, qh); -+ //hcd->fiq_state->kick_np_queues = 1; - } - } else { - uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); -@@ -851,9 +831,9 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); - } else { -- if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame)) -+ if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame)) - { -- g_next_sched_frame = qh->sched_frame; -+ hcd->fiq_state->next_sched_frame = qh->sched_frame; - } - - DWC_LIST_MOVE_HEAD -@@ -944,6 +924,9 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - if (*qh == NULL) { - retval = -DWC_E_NO_MEMORY; - goto done; -+ } else { -+ if (fiq_enable) -+ hcd->fiq_state->kick_np_queues = 1; - } - } - retval = dwc_otg_hcd_qh_add(hcd, *qh); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -deleted file mode 100755 -index 50b94a8..0000000 ---- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -+++ /dev/null -@@ -1,113 +0,0 @@ --#include "dwc_otg_regs.h" --#include "dwc_otg_dbg.h" -- --void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n" -- "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n" -- "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n" -- "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n" -- "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n" -- "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n" -- "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n" -- "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n" -- "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -- function_name, -- gintsts.b.curmode, -- gintsts.b.modemismatch, -- gintsts.b.otgintr, -- gintsts.b.sofintr, -- gintsts.b.rxstsqlvl, -- gintsts.b.nptxfempty, -- gintsts.b.ginnakeff, -- gintsts.b.goutnakeff, -- gintsts.b.ulpickint, -- gintsts.b.i2cintr, -- gintsts.b.erlysuspend, -- gintsts.b.usbsuspend, -- gintsts.b.usbreset, -- gintsts.b.enumdone, -- gintsts.b.isooutdrop, -- gintsts.b.eopframe, -- gintsts.b.restoredone, -- gintsts.b.epmismatch, -- gintsts.b.inepint, -- gintsts.b.outepintr, -- gintsts.b.incomplisoin, -- gintsts.b.incomplisoout, -- gintsts.b.fetsusp, -- gintsts.b.resetdet, -- gintsts.b.portintr, -- gintsts.b.hcintr, -- gintsts.b.ptxfempty, -- gintsts.b.lpmtranrcvd, -- gintsts.b.conidstschng, -- gintsts.b.disconnect, -- gintsts.b.sessreqintr, -- gintsts.b.wkupintr); -- return; --} -- --void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n" -- "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n" -- "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n" -- "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n" -- "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n" -- "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n" -- "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n" -- "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n" -- "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -- function_name, -- gintmsk.b.modemismatch, -- gintmsk.b.otgintr, -- gintmsk.b.sofintr, -- gintmsk.b.rxstsqlvl, -- gintmsk.b.nptxfempty, -- gintmsk.b.ginnakeff, -- gintmsk.b.goutnakeff, -- gintmsk.b.ulpickint, -- gintmsk.b.i2cintr, -- gintmsk.b.erlysuspend, -- gintmsk.b.usbsuspend, -- gintmsk.b.usbreset, -- gintmsk.b.enumdone, -- gintmsk.b.isooutdrop, -- gintmsk.b.eopframe, -- gintmsk.b.restoredone, -- gintmsk.b.epmismatch, -- gintmsk.b.inepintr, -- gintmsk.b.outepintr, -- gintmsk.b.incomplisoin, -- gintmsk.b.incomplisoout, -- gintmsk.b.fetsusp, -- gintmsk.b.resetdet, -- gintmsk.b.portintr, -- gintmsk.b.hcintr, -- gintmsk.b.ptxfempty, -- gintmsk.b.lpmtranrcvd, -- gintmsk.b.conidstschng, -- gintmsk.b.disconnect, -- gintmsk.b.sessreqintr, -- gintmsk.b.wkupintr); -- return; --} -- --void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n" -- "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n" -- "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n" -- "mvic: %1i\n", -- function_name, -- gotgint.b.sesenddet, -- gotgint.b.sesreqsucstschng, -- gotgint.b.hstnegsucstschng, -- gotgint.b.hstnegdet, -- gotgint.b.adevtoutchng, -- gotgint.b.debdone, -- gotgint.b.mvic); -- -- return; --} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -deleted file mode 100755 -index ca17379..0000000 ---- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -+++ /dev/null -@@ -1,48 +0,0 @@ --#ifndef __DWC_OTG_MPHI_FIX_H__ --#define __DWC_OTG_MPHI_FIX_H__ --#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_)) --#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_)) -- --typedef struct { -- volatile void* base; -- volatile void* ctrl; -- volatile void* outdda; -- volatile void* outddb; -- volatile void* intstat; --} mphi_regs_t; -- --void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name); --void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name); --void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name); -- --extern gintsts_data_t gintsts_saved; -- --#ifdef DEBUG --#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__) --#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__) --#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__) -- --#else --#define DWC_DBG_PRINT_CORE_INT(_arg_) --#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) --#define DWC_DBG_PRINT_OTG_INT(_arg_) -- --#endif -- --typedef enum { -- FIQDBG_SCHED = (1 << 0), -- FIQDBG_INT = (1 << 1), -- FIQDBG_ERR = (1 << 2), -- FIQDBG_PORTHUB = (1 << 3), --} FIQDBG_T; -- --void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...); --#ifdef FIQ_DEBUG --#define fiq_print _fiq_print --#else --#define fiq_print(x, y, ...) --#endif -- --extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable; -- --#endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -index 5d310df..4b32941 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -@@ -59,6 +59,8 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_dbg.h" - -+extern bool fiq_enable; -+ - static struct gadget_wrapper { - dwc_otg_pcd_t *pcd; - -@@ -1222,13 +1224,13 @@ int pcd_init(dwc_bus_dev_t *_dev) - */ - #ifdef PLATFORM_INTERFACE - DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", -- platform_get_irq(_dev, 0)); -- retval = request_irq(platform_get_irq(_dev, 0), dwc_otg_pcd_irq, -+ platform_get_irq(_dev, fiq_enable ? 0 : 1)); -+ retval = request_irq(platform_get_irq(_dev, fiq_enable ? 0 : 1), dwc_otg_pcd_irq, - IRQF_SHARED, gadget_wrapper->gadget.name, - otg_dev->pcd); - if (retval != 0) { - DWC_ERROR("request of irq%d failed\n", -- platform_get_irq(_dev, 0)); -+ platform_get_irq(_dev, fiq_enable ? 0 : 1)); - free_wrapper(gadget_wrapper); - return -EBUSY; - } - -From 264fb63a197f8df7a31f27109409699c43a5c82e Mon Sep 17 00:00:00 2001 -From: Vincent Sanders -Date: Wed, 30 Jan 2013 12:45:18 +0000 -Subject: [PATCH 028/172] bcm2835: add v4l2 camera device - -- Supports raw YUV capture, preview, JPEG and H264. -- Uses videobuf2 for data transfer, using dma_buf. -- Uses 3.6.10 timestamping -- Camera power based on use -- Uses immutable input mode on video encoder - -Signed-off-by: Daniel Stone -Signed-off-by: Luke Diamand ---- - Documentation/video4linux/bcm2835-v4l2.txt | 60 + - drivers/media/platform/Kconfig | 1 + - drivers/media/platform/Makefile | 2 + - drivers/media/platform/bcm2835/Kconfig | 25 + - drivers/media/platform/bcm2835/Makefile | 5 + - drivers/media/platform/bcm2835/bcm2835-camera.c | 1476 +++++++++++++++++ - drivers/media/platform/bcm2835/bcm2835-camera.h | 113 ++ - drivers/media/platform/bcm2835/controls.c | 725 ++++++++ - drivers/media/platform/bcm2835/mmal-common.h | 51 + - drivers/media/platform/bcm2835/mmal-encodings.h | 93 ++ - drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + - drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + - drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ - drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ - drivers/media/platform/bcm2835/mmal-parameters.h | 539 ++++++ - drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ - drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ - 17 files changed, 5826 insertions(+) - create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt - create mode 100644 drivers/media/platform/bcm2835/Kconfig - create mode 100644 drivers/media/platform/bcm2835/Makefile - create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c - create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h - create mode 100644 drivers/media/platform/bcm2835/controls.c - create mode 100644 drivers/media/platform/bcm2835/mmal-common.h - create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h - create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h - create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c - create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h - -diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt -new file mode 100644 -index 0000000..c585a8f ---- /dev/null -+++ b/Documentation/video4linux/bcm2835-v4l2.txt -@@ -0,0 +1,60 @@ -+ -+BCM2835 (aka Raspberry Pi) V4L2 driver -+====================================== -+ -+1. Copyright -+============ -+ -+Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ -+2. License -+========== -+ -+This program is free software; you can redistribute it and/or modify -+it under the terms of the GNU General Public License as published by -+the Free Software Foundation; either version 2 of the License, or -+(at your option) any later version. -+ -+This program is distributed in the hope that it will be useful, -+but WITHOUT 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. -+ -+3. Quick Start -+============== -+ -+You need a version 1.0 or later of v4l2-ctl, available from: -+ git://git.linuxtv.org/v4l-utils.git -+ -+$ sudo modprobe bcm2835-v4l2 -+ -+Turn on the overlay: -+ -+$ v4l2-ctl --overlay=1 -+ -+Turn off the overlay: -+ -+$ v4l2-ctl --overlay=0 -+ -+Set the capture format for video: -+ -+$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 -+ -+(Note: 1088 not 1080). -+ -+Capture: -+ -+$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 -+ -+Stills capture: -+ -+$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 -+$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg -+ -+List of available formats: -+ -+$ v4l2-ctl --list-formats -diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig -index 6a1334b..8fdd7a0 100644 ---- a/drivers/media/platform/Kconfig -+++ b/drivers/media/platform/Kconfig -@@ -116,6 +116,7 @@ config VIDEO_S3C_CAMIF - source "drivers/media/platform/soc_camera/Kconfig" - source "drivers/media/platform/exynos4-is/Kconfig" - source "drivers/media/platform/s5p-tv/Kconfig" -+source "drivers/media/platform/bcm2835/Kconfig" - - endif # V4L_PLATFORM_DRIVERS - -diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile -index a49936b..414b28c 100644 ---- a/drivers/media/platform/Makefile -+++ b/drivers/media/platform/Makefile -@@ -46,4 +46,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ - - obj-y += omap/ - -+obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ -+ - ccflags-y += -I$(srctree)/drivers/media/i2c -diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig -new file mode 100644 -index 0000000..a8fd172 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/Kconfig -@@ -0,0 +1,25 @@ -+# Broadcom VideoCore IV v4l2 camera support -+ -+config VIDEO_BCM2835 -+ bool "Broadcom BCM2835 camera interface driver" -+ depends on VIDEO_V4L2 && ARCH_BCM2708 -+ ---help--- -+ Say Y here to enable camera host interface devices for -+ Broadcom BCM2835 SoC. This operates over the VCHIQ interface -+ to a service running on VideoCore. -+ -+ -+if VIDEO_BCM2835 -+ -+config VIDEO_BCM2835_MMAL -+ tristate "Broadcom BM2835 MMAL camera interface driver" -+ depends on BCM2708_VCHIQ -+ select VIDEOBUF2_VMALLOC -+ ---help--- -+ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface -+ -+ To compile this driver as a module, choose M here: the -+ module will be called bcm2835-v4l2.o -+ -+ -+endif # VIDEO_BM2835 -diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile -new file mode 100644 -index 0000000..f17c79c ---- /dev/null -+++ b/drivers/media/platform/bcm2835/Makefile -@@ -0,0 +1,5 @@ -+bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o -+ -+obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o -+ -+ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -new file mode 100644 -index 0000000..1c9d9d5 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -0,0 +1,1476 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "mmal-common.h" -+#include "mmal-encodings.h" -+#include "mmal-vchiq.h" -+#include "mmal-msg.h" -+#include "mmal-parameters.h" -+#include "bcm2835-camera.h" -+ -+#define BM2835_MMAL_VERSION "0.0.2" -+#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" -+ -+#define MAX_WIDTH 2592 -+#define MAX_HEIGHT 1944 -+#define MIN_BUFFER_SIZE (80*1024) -+ -+#define MAX_VIDEO_MODE_WIDTH 1280 -+#define MAX_VIDEO_MODE_HEIGHT 720 -+ -+MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); -+MODULE_AUTHOR("Vincent Sanders"); ++module_init(vchiq_init); ++module_exit(vchiq_exit); +MODULE_LICENSE("GPL"); -+MODULE_VERSION(BM2835_MMAL_VERSION); -+ -+int bcm2835_v4l2_debug; -+module_param_named(debug, bcm2835_v4l2_debug, int, 0644); -+MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); -+ -+static struct bm2835_mmal_dev *gdev; /* global device data */ -+ -+/* video formats */ -+static struct mmal_fmt formats[] = { -+ { -+ .name = "4:2:0, packed YUV", -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .mmal = MMAL_ENCODING_I420, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, YUYV", -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .mmal = MMAL_ENCODING_YUYV, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB24 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .mmal = MMAL_ENCODING_BGR24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "JPEG", -+ .fourcc = V4L2_PIX_FMT_JPEG, -+ .mmal = MMAL_ENCODING_JPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, -+ }, -+ { -+ .name = "H264", -+ .fourcc = V4L2_PIX_FMT_H264, -+ .mmal = MMAL_ENCODING_H264, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ } -+}; -+ -+static struct mmal_fmt *get_format(struct v4l2_format *f) -+{ -+ struct mmal_fmt *fmt; -+ unsigned int k; -+ -+ for (k = 0; k < ARRAY_SIZE(formats); k++) { -+ fmt = &formats[k]; -+ if (fmt->fourcc == f->fmt.pix.pixelformat) -+ break; -+ } -+ -+ if (k == ARRAY_SIZE(formats)) -+ return NULL; -+ -+ return &formats[k]; -+} -+ -+/* ------------------------------------------------------------------ -+ Videobuf queue operations -+ ------------------------------------------------------------------*/ -+ -+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, -+ unsigned int *nbuffers, unsigned int *nplanes, -+ unsigned int sizes[], void *alloc_ctxs[]) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ unsigned long size; -+ -+ /* refuse queue setup if port is not configured */ -+ if (dev->capture.port == NULL) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: capture port not configured\n", __func__); -+ return -EINVAL; -+ } -+ -+ size = dev->capture.port->current_buffer.size; -+ if (size == 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: capture port buffer size is zero\n", __func__); -+ return -EINVAL; -+ } -+ -+ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) -+ *nbuffers = (dev->capture.port->current_buffer.num + 2); -+ -+ *nplanes = 1; -+ -+ sizes[0] = size; -+ -+ /* -+ * videobuf2-vmalloc allocator is context-less so no need to set -+ * alloc_ctxs array. -+ */ -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ return 0; -+} -+ -+static int buffer_prepare(struct vb2_buffer *vb) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); -+ unsigned long size; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ BUG_ON(dev->capture.port == NULL); -+ BUG_ON(dev->capture.fmt == NULL); -+ -+ size = dev->capture.stride * dev->capture.height; -+ if (vb2_plane_size(vb, 0) < size) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s data will not fit into plane (%lu < %lu)\n", -+ __func__, vb2_plane_size(vb, 0), size); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static inline bool is_capturing(struct bm2835_mmal_dev *dev) -+{ -+ return dev->capture.camera_port == -+ &dev-> -+ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; -+} -+ -+static void buffer_cb(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ int status, -+ struct mmal_buffer *buf, -+ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) -+{ -+ struct bm2835_mmal_dev *dev = port->cb_ctx; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", -+ __func__, status, buf, length, mmal_flags, pts); -+ -+ if (status != 0) { -+ /* error in transfer */ -+ if (buf != NULL) { -+ /* there was a buffer with the error so return it */ -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); -+ } -+ return; -+ } else if (length == 0) { -+ /* stream ended */ -+ if (buf != NULL) { -+ /* this should only ever happen if the port is -+ * disabled and there are buffers still queued -+ */ -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); -+ pr_debug("Empty buffer"); -+ } else if (dev->capture.frame_count) { -+ /* grab another frame */ -+ if (is_capturing(dev)) { -+ pr_debug("Grab another frame"); -+ vchiq_mmal_port_parameter_set( -+ instance, -+ dev->capture. -+ camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture. -+ frame_count, -+ sizeof(dev->capture.frame_count)); -+ } -+ } else { -+ /* signal frame completion */ -+ complete(&dev->capture.frame_cmplt); -+ } -+ } else { -+ if (dev->capture.frame_count) { -+ if (dev->capture.vc_start_timestamp != -1) { -+ s64 runtime_us = pts - -+ dev->capture.vc_start_timestamp; -+ u32 div = 0; -+ u32 rem = 0; -+ -+ div = -+ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); -+ buf->vb.v4l2_buf.timestamp.tv_sec = -+ dev->capture.kernel_start_ts.tv_sec - 1 + -+ div; -+ buf->vb.v4l2_buf.timestamp.tv_usec = -+ dev->capture.kernel_start_ts.tv_usec + rem; -+ -+ if (buf->vb.v4l2_buf.timestamp.tv_usec >= -+ USEC_PER_SEC) { -+ buf->vb.v4l2_buf.timestamp.tv_sec++; -+ buf->vb.v4l2_buf.timestamp.tv_usec -= -+ USEC_PER_SEC; -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Convert start time %d.%06d and %llu" -+ "with offset %llu to %d.%06d\n", -+ (int)dev->capture.kernel_start_ts. -+ tv_sec, -+ (int)dev->capture.kernel_start_ts. -+ tv_usec, -+ dev->capture.vc_start_timestamp, pts, -+ (int)buf->vb.v4l2_buf.timestamp.tv_sec, -+ (int)buf->vb.v4l2_buf.timestamp. -+ tv_usec); -+ } else { -+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); -+ } -+ -+ vb2_set_plane_payload(&buf->vb, 0, length); -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); -+ -+ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && -+ is_capturing(dev)) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Grab another frame as buffer has EOS"); -+ vchiq_mmal_port_parameter_set( -+ instance, -+ dev->capture. -+ camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture. -+ frame_count, -+ sizeof(dev->capture.frame_count)); -+ } -+ } else { -+ /* signal frame completion */ -+ complete(&dev->capture.frame_cmplt); -+ } -+ } -+} -+ -+static int enable_camera(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ if (!dev->camera_use_count) { -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed enabling camera, ret %d\n", ret); -+ return -EINVAL; -+ } -+ } -+ dev->camera_use_count++; -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, "enabled camera (refcount %d)\n", -+ dev->camera_use_count); -+ return 0; -+} -+ -+static int disable_camera(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ if (!dev->camera_use_count) { -+ v4l2_err(&dev->v4l2_dev, -+ "Disabled the camera when already disabled\n"); -+ return -EINVAL; -+ } -+ dev->camera_use_count--; -+ if (!dev->camera_use_count) { -+ unsigned int i = 0xFFFFFFFF; -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Disabling camera\n"); -+ ret = -+ vchiq_mmal_component_disable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed disabling camera, ret %d\n", ret); -+ return -EINVAL; -+ } -+ vchiq_mmal_port_parameter_set( -+ dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]->control, -+ MMAL_PARAMETER_CAMERA_NUM, &i, -+ sizeof(i)); -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Camera refcount now %d\n", dev->camera_use_count); -+ return 0; -+} -+ -+static void buffer_queue(struct vb2_buffer *vb) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); -+ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); -+ int ret; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: dev:%p buf:%p\n", __func__, dev, buf); -+ -+ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); -+ buf->buffer_size = vb2_plane_size(&buf->vb, 0); -+ -+ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); -+ if (ret < 0) -+ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", -+ __func__); -+} -+ -+static int start_streaming(struct vb2_queue *vq, unsigned int count) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ int ret; -+ int parameter_size; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ /* ensure a format has actually been set */ -+ if (dev->capture.port == NULL) -+ return -EINVAL; -+ -+ if (enable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); -+ return -EINVAL; -+ } -+ -+ /*init_completion(&dev->capture.frame_cmplt); */ -+ -+ /* enable frame capture */ -+ dev->capture.frame_count = 1; -+ -+ /* if the preview is not already running, wait for a few frames for AGC -+ * to settle down. -+ */ -+ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) -+ msleep(300); -+ -+ /* enable the connection from camera to encoder (if applicable) */ -+ if (dev->capture.camera_port != dev->capture.port -+ && dev->capture.camera_port) { -+ ret = vchiq_mmal_port_enable(dev->instance, -+ dev->capture.camera_port, NULL); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to enable encode tunnel - error %d\n", -+ ret); -+ return -1; -+ } -+ } -+ -+ /* Get VC timestamp at this point in time */ -+ parameter_size = sizeof(dev->capture.vc_start_timestamp); -+ if (vchiq_mmal_port_parameter_get(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_SYSTEM_TIME, -+ &dev->capture.vc_start_timestamp, -+ ¶meter_size)) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to get VC start time - update your VC f/w\n"); -+ -+ /* Flag to indicate just to rely on kernel timestamps */ -+ dev->capture.vc_start_timestamp = -1; -+ } else -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Start time %lld size %d\n", -+ dev->capture.vc_start_timestamp, parameter_size); -+ -+ v4l2_get_timestamp(&dev->capture.kernel_start_ts); -+ -+ /* enable the camera port */ -+ dev->capture.port->cb_ctx = dev; -+ ret = -+ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to enable capture port - error %d\n", ret); -+ return -1; -+ } -+ -+ /* capture the first frame */ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture.frame_count, -+ sizeof(dev->capture.frame_count)); -+ return 0; -+} -+ -+/* abort streaming and wait for last buffer */ -+static int stop_streaming(struct vb2_queue *vq) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ init_completion(&dev->capture.frame_cmplt); -+ dev->capture.frame_count = 0; -+ -+ /* ensure a format has actually been set */ -+ if (dev->capture.port == NULL) -+ return -EINVAL; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); -+ -+ /* stop capturing frames */ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture.frame_count, -+ sizeof(dev->capture.frame_count)); -+ -+ /* wait for last frame to complete */ -+ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); -+ if (ret <= 0) -+ v4l2_err(&dev->v4l2_dev, -+ "error %d waiting for frame completion\n", ret); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "disabling connection\n"); -+ -+ /* disable the connection from camera to encoder */ -+ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); -+ if (!ret && dev->capture.camera_port != dev->capture.port) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "disabling port\n"); -+ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); -+ } else if (dev->capture.camera_port != dev->capture.port) { -+ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", -+ ret); -+ } -+ -+ if (disable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); -+ return -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static void bm2835_mmal_lock(struct vb2_queue *vq) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ mutex_lock(&dev->mutex); -+} -+ -+static void bm2835_mmal_unlock(struct vb2_queue *vq) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ mutex_unlock(&dev->mutex); -+} -+ -+static struct vb2_ops bm2835_mmal_video_qops = { -+ .queue_setup = queue_setup, -+ .buf_prepare = buffer_prepare, -+ .buf_queue = buffer_queue, -+ .start_streaming = start_streaming, -+ .stop_streaming = stop_streaming, -+ .wait_prepare = bm2835_mmal_unlock, -+ .wait_finish = bm2835_mmal_lock, -+}; -+ -+/* ------------------------------------------------------------------ -+ IOCTL operations -+ ------------------------------------------------------------------*/ -+ -+/* overlay ioctl */ -+static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_fmtdesc *f) -+{ -+ struct mmal_fmt *fmt; -+ -+ if (f->index >= ARRAY_SIZE(formats)) -+ return -EINVAL; -+ -+ fmt = &formats[f->index]; -+ -+ strlcpy(f->description, fmt->name, sizeof(f->description)); -+ f->pixelformat = fmt->fourcc; -+ -+ return 0; -+} -+ -+static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ f->fmt.win = dev->overlay; -+ -+ return 0; -+} -+ -+static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ /* Only support one format so get the current one. */ -+ vidioc_g_fmt_vid_overlay(file, priv, f); -+ -+ /* todo: allow the size and/or offset to be changed. */ -+ return 0; -+} -+ -+static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ vidioc_try_fmt_vid_overlay(file, priv, f); -+ -+ dev->overlay = f->fmt.win; -+ -+ /* todo: program the preview port parameters */ -+ return 0; -+} -+ -+static int vidioc_overlay(struct file *file, void *f, unsigned int on) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct vchiq_mmal_port *src; -+ struct vchiq_mmal_port *dst; -+ struct mmal_parameter_displayregion prev_config = { -+ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | -+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, -+ .layer = PREVIEW_LAYER, -+ .alpha = 255, -+ .fullscreen = 0, -+ .dest_rect = { -+ .x = dev->overlay.w.left, -+ .y = dev->overlay.w.top, -+ .width = dev->overlay.w.width, -+ .height = dev->overlay.w.height, -+ }, -+ }; -+ -+ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || -+ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) -+ return 0; /* already in requested state */ -+ -+ src = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; -+ -+ if (!on) { -+ /* disconnect preview ports and disable component */ -+ ret = vchiq_mmal_port_disable(dev->instance, src); -+ if (!ret) -+ ret = -+ vchiq_mmal_port_connect_tunnel(dev->instance, src, -+ NULL); -+ if (ret >= 0) -+ ret = vchiq_mmal_component_disable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ -+ disable_camera(dev); -+ return ret; -+ } -+ -+ /* set preview port format and connect it to output */ -+ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; -+ -+ ret = vchiq_mmal_port_set_format(dev->instance, src); -+ if (ret < 0) -+ goto error; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, -+ MMAL_PARAMETER_DISPLAYREGION, -+ &prev_config, sizeof(prev_config)); -+ if (ret < 0) -+ goto error; -+ -+ if (enable_camera(dev) < 0) -+ goto error; -+ -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ if (ret < 0) -+ goto error; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", -+ src, dst); -+ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); -+ if (!ret) -+ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); -+error: -+ return ret; -+} -+ -+static int vidioc_g_fbuf(struct file *file, void *fh, -+ struct v4l2_framebuffer *a) -+{ -+ /* The video overlay must stay within the framebuffer and can't be -+ positioned independently. */ -+ a->flags = V4L2_FBUF_FLAG_OVERLAY; -+ -+ /* todo: v4l2_framebuffer still needs more info filling in -+ * in order to pass the v4l2-compliance test. */ -+ -+ return 0; -+} -+ -+/* input ioctls */ -+static int vidioc_enum_input(struct file *file, void *priv, -+ struct v4l2_input *inp) -+{ -+ /* only a single camera input */ -+ if (inp->index != 0) -+ return -EINVAL; -+ -+ inp->type = V4L2_INPUT_TYPE_CAMERA; -+ sprintf(inp->name, "Camera %u", inp->index); -+ return 0; -+} -+ -+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -+{ -+ *i = 0; -+ return 0; -+} -+ -+static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -+{ -+ if (i != 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+/* capture ioctls */ -+static int vidioc_querycap(struct file *file, void *priv, -+ struct v4l2_capability *cap) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ u32 major; -+ u32 minor; -+ -+ vchiq_mmal_version(dev->instance, &major, &minor); -+ -+ strcpy(cap->driver, "bm2835 mmal"); -+ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", -+ major, minor); -+ -+ snprintf(cap->bus_info, sizeof(cap->bus_info), -+ "platform:%s", dev->v4l2_dev.name); -+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | -+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; -+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -+ -+ return 0; -+} -+ -+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_fmtdesc *f) -+{ -+ struct mmal_fmt *fmt; -+ -+ if (f->index >= ARRAY_SIZE(formats)) -+ return -EINVAL; -+ -+ fmt = &formats[f->index]; -+ -+ strlcpy(f->description, fmt->name, sizeof(f->description)); -+ f->pixelformat = fmt->fourcc; -+ return 0; -+} -+ -+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ f->fmt.pix.width = dev->capture.width; -+ f->fmt.pix.height = dev->capture.height; -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; -+ f->fmt.pix.bytesperline = -+ (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; -+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG -+ && f->fmt.pix.sizeimage < (100 << 10)) { -+ /* Need a minimum size for JPEG to account for EXIF. */ -+ f->fmt.pix.sizeimage = (100 << 10); -+ } -+ -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || -+ dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ f->fmt.pix.priv = 0; -+ -+ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -+ __func__); -+ return 0; -+} -+ -+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct mmal_fmt *mfmt; -+ -+ mfmt = get_format(f); -+ if (!mfmt) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Fourcc format (0x%08x) unknown.\n", -+ f->fmt.pix.pixelformat); -+ f->fmt.pix.pixelformat = formats[0].fourcc; -+ mfmt = get_format(f); -+ } -+ -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ /* image must be a multiple of 32 pixels wide and 16 lines high */ -+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, -+ &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); -+ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; -+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -+ if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) -+ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; -+ -+ if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || -+ mfmt->fourcc == V4L2_PIX_FMT_UYVY) -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ f->fmt.pix.priv = 0; -+ -+ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -+ __func__); -+ return 0; -+} -+ -+static int mmal_setup_components(struct bm2835_mmal_dev *dev, -+ struct v4l2_format *f) -+{ -+ int ret; -+ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; -+ struct vchiq_mmal_component *encode_component = NULL; -+ struct mmal_fmt *mfmt = get_format(f); -+ -+ BUG_ON(!mfmt); -+ -+ if (dev->capture.encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "vid_cap - disconnect previous tunnel\n"); -+ -+ /* Disconnect any previous connection */ -+ vchiq_mmal_port_connect_tunnel(dev->instance, -+ dev->capture.camera_port, NULL); -+ dev->capture.camera_port = NULL; -+ ret = vchiq_mmal_component_disable(dev->instance, -+ dev->capture. -+ encode_component); -+ if (ret) -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to disable encode component %d\n", -+ ret); -+ -+ dev->capture.encode_component = NULL; -+ } -+ /* format dependant port setup */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_CAMERA: -+ /* Make a further decision on port based on resolution */ -+ if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH -+ && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) -+ camera_port = port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]; -+ else -+ camera_port = port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE]; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; -+ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; -+ camera_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE]; -+ break; -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; -+ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ camera_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]; -+ break; -+ default: -+ break; -+ } -+ -+ if (!port) -+ return -EINVAL; -+ -+ if (encode_component) -+ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; -+ else -+ camera_port->format.encoding = mfmt->mmal; -+ -+ camera_port->format.encoding_variant = 0; -+ camera_port->es.video.width = f->fmt.pix.width; -+ camera_port->es.video.height = f->fmt.pix.height; -+ camera_port->es.video.crop.x = 0; -+ camera_port->es.video.crop.y = 0; -+ camera_port->es.video.crop.width = f->fmt.pix.width; -+ camera_port->es.video.crop.height = f->fmt.pix.height; -+ camera_port->es.video.frame_rate.num = 30; -+ camera_port->es.video.frame_rate.den = 1; -+ -+ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); -+ -+ if (!ret -+ && camera_port == -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]) { -+ bool overlay_enabled = -+ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; -+ struct vchiq_mmal_port *preview_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; -+ /* Preview and encode ports need to match on resolution */ -+ if (overlay_enabled) { -+ /* Need to disable the overlay before we can update -+ * the resolution -+ */ -+ ret = -+ vchiq_mmal_port_disable(dev->instance, -+ preview_port); -+ if (!ret) -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ preview_port, -+ NULL); -+ } -+ preview_port->es.video.width = f->fmt.pix.width; -+ preview_port->es.video.height = f->fmt.pix.height; -+ preview_port->es.video.crop.x = 0; -+ preview_port->es.video.crop.y = 0; -+ preview_port->es.video.crop.width = f->fmt.pix.width; -+ preview_port->es.video.crop.height = f->fmt.pix.height; -+ preview_port->es.video.frame_rate.num = 30; -+ preview_port->es.video.frame_rate.den = 1; -+ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); -+ if (overlay_enabled) { -+ ret = vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ preview_port, -+ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); -+ if (!ret) -+ ret = vchiq_mmal_port_enable(dev->instance, -+ preview_port, -+ NULL); -+ } -+ } -+ -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s failed to set format\n", __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; -+ } else { -+ if (encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "vid_cap - set up encode comp\n"); -+ -+ /* configure buffering */ -+ camera_port->current_buffer.size = -+ camera_port->recommended_buffer.size; -+ camera_port->current_buffer.num = -+ camera_port->recommended_buffer.num; -+ -+ port->format.encoding = mfmt->mmal; -+ port->format.encoding_variant = 0; -+ /* Set any encoding specific parameters */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ port->format.bitrate = -+ dev->capture.encode_bitrate; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ /* Could set EXIF parameters here */ -+ break; -+ default: -+ break; -+ } -+ ret = vchiq_mmal_port_set_format(dev->instance, port); -+ -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s failed to set format\n", __func__); -+ } else { -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ encode_component); -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s Failed to enable encode components\n", -+ __func__); -+ } else { -+ /* configure buffering */ -+ port->current_buffer.num = 1; -+ port->current_buffer.size = -+ f->fmt.pix.sizeimage; -+ if (port->format.encoding == -+ MMAL_ENCODING_JPEG) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "JPEG - fiddle buffer size\n"); -+ port->current_buffer.size = -+ (f->fmt.pix.sizeimage < -+ (100 << 10)) -+ ? (100 << 10) : f->fmt.pix. -+ sizeimage; -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "vid_cap - current_buffer.size being set to %d\n", -+ f->fmt.pix.sizeimage); -+ port->current_buffer.alignment = 0; -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ camera_port, -+ &encode_component->input[0]); -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to create connection\n", -+ __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; -+ } -+ } -+ } -+ } else { -+ /* configure buffering */ -+ camera_port->current_buffer.num = 1; -+ camera_port->current_buffer.size = f->fmt.pix.sizeimage; -+ camera_port->current_buffer.alignment = 0; -+ } -+ -+ if (!ret) { -+ dev->capture.fmt = mfmt; -+ dev->capture.stride = f->fmt.pix.bytesperline; -+ dev->capture.width = port->es.video.crop.width; -+ dev->capture.height = port->es.video.crop.height; -+ -+ /* select port for capture */ -+ dev->capture.port = port; -+ dev->capture.camera_port = camera_port; -+ dev->capture.encode_component = encode_component; -+ } -+ } -+ -+ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ -+ return ret; -+} -+ -+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct mmal_fmt *mfmt; -+ -+ /* try the format to set valid parameters */ -+ ret = vidioc_try_fmt_vid_cap(file, priv, f); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); -+ return ret; -+ } -+ -+ /* if a capture is running refuse to set format */ -+ if (vb2_is_busy(&dev->capture.vb_vidq)) { -+ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); -+ return -EBUSY; -+ } -+ -+ /* If the format is unsupported v4l2 says we should switch to -+ * a supported one and not return an error. */ -+ mfmt = get_format(f); -+ if (!mfmt) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Fourcc format (0x%08x) unknown.\n", -+ f->fmt.pix.pixelformat); -+ f->fmt.pix.pixelformat = formats[0].fourcc; -+ mfmt = get_format(f); -+ } -+ -+ ret = mmal_setup_components(dev, f); -+ if (ret != 0) -+ v4l2_err(&dev->v4l2_dev, -+ "%s: failed to setup mmal components: %d\n", -+ __func__, ret); -+ -+ return ret; -+} -+ -+static const struct v4l2_ioctl_ops camera0_ioctl_ops = { -+ /* overlay */ -+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, -+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, -+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, -+ .vidioc_overlay = vidioc_overlay, -+ .vidioc_g_fbuf = vidioc_g_fbuf, -+ -+ /* inputs */ -+ .vidioc_enum_input = vidioc_enum_input, -+ .vidioc_g_input = vidioc_g_input, -+ .vidioc_s_input = vidioc_s_input, -+ -+ /* capture */ -+ .vidioc_querycap = vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, -+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -+ -+ /* buffer management */ -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ -+ .vidioc_log_status = v4l2_ctrl_log_status, -+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+/* ------------------------------------------------------------------ -+ Driver init/finalise -+ ------------------------------------------------------------------*/ -+ -+static const struct v4l2_file_operations camera0_fops = { -+ .owner = THIS_MODULE, -+ .open = v4l2_fh_open, -+ .release = vb2_fop_release, -+ .read = vb2_fop_read, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ -+ .mmap = vb2_fop_mmap, -+}; -+ -+static struct video_device vdev_template = { -+ .name = "camera0", -+ .fops = &camera0_fops, -+ .ioctl_ops = &camera0_ioctl_ops, -+ .release = video_device_release_empty, -+}; -+ -+static int set_camera_parameters(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *camera) -+{ -+ int ret; -+ struct mmal_parameter_camera_config cam_config = { -+ .max_stills_w = MAX_WIDTH, -+ .max_stills_h = MAX_HEIGHT, -+ .stills_yuv422 = 1, -+ .one_shot_stills = 1, -+ .max_preview_video_w = 1920, -+ .max_preview_video_h = 1088, -+ .num_preview_video_frames = 3, -+ .stills_capture_circular_buffer_height = 0, -+ .fast_preview_resume = 0, -+ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC -+ }; -+ -+ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, -+ MMAL_PARAMETER_CAMERA_CONFIG, -+ &cam_config, sizeof(cam_config)); -+ return ret; -+} -+ -+/* MMAL instance and component init */ -+static int __init mmal_init(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ struct mmal_es_format *format; -+ -+ ret = vchiq_mmal_init(&dev->instance); -+ if (ret < 0) -+ return ret; -+ -+ /* get the camera component ready */ -+ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", -+ &dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) -+ goto unreg_mmal; -+ -+ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < -+ MMAL_CAMERA_PORT_COUNT) { -+ ret = -EINVAL; -+ goto unreg_camera; -+ } -+ -+ ret = set_camera_parameters(dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) -+ goto unreg_camera; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ format->encoding_variant = MMAL_ENCODING_I420; -+ -+ format->es->video.width = 1024; -+ format->es->video.height = 768; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 1024; -+ format->es->video.crop.height = 768; -+ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -+ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ format->encoding_variant = MMAL_ENCODING_I420; -+ -+ format->es->video.width = 1024; -+ format->es->video.height = 768; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 1024; -+ format->es->video.crop.height = 768; -+ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -+ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ -+ format->es->video.width = 2592; -+ format->es->video.height = 1944; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 2592; -+ format->es->video.crop.height = 1944; -+ format->es->video.frame_rate.num = 30; -+ format->es->video.frame_rate.den = 1; -+ -+ dev->capture.width = format->es->video.width; -+ dev->capture.height = format->es->video.height; -+ dev->capture.fmt = &formats[0]; -+ dev->capture.encode_component = NULL; -+ -+ /* get the preview component ready */ -+ ret = vchiq_mmal_component_init( -+ dev->instance, "ril.video_render", -+ &dev->component[MMAL_COMPONENT_PREVIEW]); -+ if (ret < 0) -+ goto unreg_camera; -+ -+ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { -+ ret = -EINVAL; -+ pr_debug("too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); -+ goto unreg_preview; -+ } -+ -+ /* get the image encoder component ready */ -+ ret = vchiq_mmal_component_init( -+ dev->instance, "ril.image_encode", -+ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ if (ret < 0) -+ goto unreg_preview; -+ -+ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { -+ ret = -EINVAL; -+ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, -+ 1); -+ goto unreg_image_encoder; -+ } -+ -+ /* get the video encoder component ready */ -+ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", -+ &dev-> -+ component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ if (ret < 0) -+ goto unreg_image_encoder; -+ -+ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { -+ ret = -EINVAL; -+ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, -+ 1); -+ goto unreg_vid_encoder; -+ } -+ -+ { -+ unsigned int enable = 1; -+ vchiq_mmal_port_parameter_set( -+ dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, -+ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, -+ &enable, sizeof(enable)); -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, -+ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, -+ &enable, -+ sizeof(enable)); -+ } -+ ret = bm2835_mmal_set_all_camera_controls(dev); -+ if (ret < 0) -+ goto unreg_vid_encoder; -+ -+ return 0; -+ -+unreg_vid_encoder: -+ pr_err("Cleanup: Destroy video encoder\n"); -+ vchiq_mmal_component_finalise( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ -+unreg_image_encoder: -+ pr_err("Cleanup: Destroy image encoder\n"); -+ vchiq_mmal_component_finalise( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ -+unreg_preview: -+ pr_err("Cleanup: Destroy video render\n"); -+ vchiq_mmal_component_finalise(dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ -+unreg_camera: -+ pr_err("Cleanup: Destroy camera\n"); -+ vchiq_mmal_component_finalise(dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ -+unreg_mmal: -+ vchiq_mmal_finalise(dev->instance); -+ return ret; -+} -+ -+static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, -+ struct video_device *vfd) -+{ -+ int ret; -+ -+ *vfd = vdev_template; -+ -+ vfd->v4l2_dev = &dev->v4l2_dev; -+ -+ vfd->lock = &dev->mutex; -+ -+ vfd->queue = &dev->capture.vb_vidq; -+ -+ /* video device needs to be able to access instance data */ -+ video_set_drvdata(vfd, dev); -+ -+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); -+ if (ret < 0) -+ return ret; -+ -+ v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", -+ video_device_node_name(vfd)); -+ -+ return 0; -+} -+ -+static struct v4l2_format default_v4l2_format = { -+ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, -+ .fmt.pix.width = 1024, -+ .fmt.pix.bytesperline = 1024 * 3 / 2, -+ .fmt.pix.height = 768, -+ .fmt.pix.sizeimage = 1<<18, -+}; -+ -+static int __init bm2835_mmal_init(void) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev; -+ struct vb2_queue *q; -+ -+ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); -+ if (!dev) -+ return -ENOMEM; -+ -+ /* setup device defaults */ -+ dev->overlay.w.left = 150; -+ dev->overlay.w.top = 50; -+ dev->overlay.w.width = 1024; -+ dev->overlay.w.height = 768; -+ dev->overlay.clipcount = 0; -+ dev->overlay.field = V4L2_FIELD_NONE; -+ -+ dev->capture.fmt = &formats[3]; /* JPEG */ -+ -+ /* v4l device registration */ -+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), -+ "%s", BM2835_MMAL_MODULE_NAME); -+ ret = v4l2_device_register(NULL, &dev->v4l2_dev); -+ if (ret) -+ goto free_dev; -+ -+ /* setup v4l controls */ -+ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); -+ if (ret < 0) -+ goto unreg_dev; -+ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; -+ -+ /* mmal init */ -+ ret = mmal_init(dev); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ /* initialize queue */ -+ q = &dev->capture.vb_vidq; -+ memset(q, 0, sizeof(*q)); -+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; -+ q->drv_priv = dev; -+ q->buf_struct_size = sizeof(struct mmal_buffer); -+ q->ops = &bm2835_mmal_video_qops; -+ q->mem_ops = &vb2_vmalloc_memops; -+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ ret = vb2_queue_init(q); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ -+ mutex_init(&dev->mutex); -+ -+ /* initialise video devices */ -+ ret = bm2835_mmal_init_device(dev, &dev->vdev); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ ret = mmal_setup_components(dev, &default_v4l2_format); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: could not setup components\n", __func__); -+ goto unreg_dev; -+ } -+ -+ v4l2_info(&dev->v4l2_dev, -+ "Broadcom 2835 MMAL video capture ver %s loaded.\n", -+ BM2835_MMAL_VERSION); -+ -+ gdev = dev; -+ return 0; -+ -+unreg_dev: -+ v4l2_ctrl_handler_free(&dev->ctrl_handler); -+ v4l2_device_unregister(&dev->v4l2_dev); -+ -+free_dev: -+ kfree(dev); -+ -+ v4l2_err(&dev->v4l2_dev, -+ "%s: error %d while loading driver\n", -+ BM2835_MMAL_MODULE_NAME, ret); -+ -+ return ret; -+} -+ -+static void __exit bm2835_mmal_exit(void) -+{ -+ if (!gdev) -+ return; -+ -+ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", -+ video_device_node_name(&gdev->vdev)); -+ -+ video_unregister_device(&gdev->vdev); -+ -+ if (gdev->capture.encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, -+ "mmal_exit - disconnect tunnel\n"); -+ vchiq_mmal_port_connect_tunnel(gdev->instance, -+ gdev->capture.camera_port, NULL); -+ vchiq_mmal_component_disable(gdev->instance, -+ gdev->capture.encode_component); -+ } -+ vchiq_mmal_component_disable(gdev->instance, -+ gdev->component[MMAL_COMPONENT_CAMERA]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev-> -+ component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev-> -+ component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev->component[MMAL_COMPONENT_PREVIEW]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev->component[MMAL_COMPONENT_CAMERA]); -+ -+ vchiq_mmal_finalise(gdev->instance); -+ -+ v4l2_ctrl_handler_free(&gdev->ctrl_handler); -+ -+ v4l2_device_unregister(&gdev->v4l2_dev); -+ -+ kfree(gdev); -+} -+ -+module_init(bm2835_mmal_init); -+module_exit(bm2835_mmal_exit); -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h ++MODULE_AUTHOR("Broadcom Corporation"); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h new file mode 100644 -index 0000000..883eab7 +index 0000000..d1e2741 --- /dev/null -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.h -@@ -0,0 +1,113 @@ -+/* -+ * Broadcom BM2835 V4L2 driver ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h +@@ -0,0 +1,223 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. + * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. + * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * core driver device ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + -+#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ ++#ifndef VCHIQ_ARM_H ++#define VCHIQ_ARM_H + -+enum { -+ MMAL_COMPONENT_CAMERA = 0, -+ MMAL_COMPONENT_PREVIEW, -+ MMAL_COMPONENT_IMAGE_ENCODE, -+ MMAL_COMPONENT_VIDEO_ENCODE, -+ MMAL_COMPONENT_COUNT -+}; -+ -+enum { -+ MMAL_CAMERA_PORT_PREVIEW = 0, -+ MMAL_CAMERA_PORT_VIDEO, -+ MMAL_CAMERA_PORT_CAPTURE, -+ MMAL_CAMERA_PORT_COUNT -+}; -+ -+#define PREVIEW_FRAME_RATE_NUM 30 -+#define PREVIEW_FRAME_RATE_DEN 1 -+ -+#define PREVIEW_LAYER 2 -+ -+extern int bcm2835_v4l2_debug; -+ -+struct bm2835_mmal_dev { -+ /* v4l2 devices */ -+ struct v4l2_device v4l2_dev; -+ struct video_device vdev; -+ struct mutex mutex; -+ -+ /* controls */ -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; -+ struct mmal_colourfx colourfx; -+ int hflip; -+ int vflip; -+ -+ /* allocated mmal instance and components */ -+ struct vchiq_mmal_instance *instance; -+ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; -+ int camera_use_count; -+ -+ struct v4l2_window overlay; -+ -+ struct { -+ unsigned int width; /* width */ -+ unsigned int height; /* height */ -+ unsigned int stride; /* stride */ -+ struct mmal_fmt *fmt; -+ -+ /* H264 encode bitrate */ -+ int encode_bitrate; -+ /* H264 bitrate mode. CBR/VBR */ -+ int encode_bitrate_mode; -+ /* JPEG Q-factor */ -+ int q_factor; -+ -+ struct vb2_queue vb_vidq; -+ -+ /* VC start timestamp for streaming */ -+ s64 vc_start_timestamp; -+ /* Kernel start timestamp for streaming */ -+ struct timeval kernel_start_ts; -+ -+ struct vchiq_mmal_port *port; /* port being used for capture */ -+ /* camera port being used for capture */ -+ struct vchiq_mmal_port *camera_port; -+ /* component being used for encode */ -+ struct vchiq_mmal_component *encode_component; -+ /* number of frames remaining which driver should capture */ -+ unsigned int frame_count; -+ /* last frame completion */ -+ struct completion frame_cmplt; -+ -+ } capture; -+ -+}; -+ -+int bm2835_mmal_init_controls( -+ struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl_handler *hdl); -+ -+int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); -+ -+ -+/* Debug helpers */ -+ -+#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ -+{ \ -+ v4l2_dbg(level, debug, dev, \ -+"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ -+ desc == NULL ? "" : desc, \ -+ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ -+ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ -+ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ -+} -diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c -new file mode 100644 -index 0000000..d1408e5 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/controls.c -@@ -0,0 +1,725 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "mmal-common.h" -+#include "mmal-vchiq.h" -+#include "mmal-parameters.h" -+#include "bcm2835-camera.h" -+ -+/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. -+ * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. -+ */ -+static const s64 ev_bias_qmenu[] = { -+ -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 -+}; -+ -+/* Supported ISO values -+ * ISOO = auto ISO -+ */ -+static const s64 iso_qmenu[] = { -+ 0, 100, 200, 400, 800, -+}; -+ -+/* Supported video encode modes */ -+static const s64 bitrate_mode_qmenu[] = { -+ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, -+ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, -+}; -+ -+ -+enum bm2835_mmal_ctrl_type { -+ MMAL_CONTROL_TYPE_STD, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ MMAL_CONTROL_TYPE_INT_MENU, -+ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ -+}; -+ -+struct bm2835_mmal_v4l2_ctrl; -+ -+typedef int(bm2835_mmal_v4l2_ctrl_cb)( -+ struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); -+ -+struct bm2835_mmal_v4l2_ctrl { -+ u32 id; /* v4l2 control identifier */ -+ enum bm2835_mmal_ctrl_type type; -+ /* control minimum value or -+ * mask for MMAL_CONTROL_TYPE_STD_MENU */ -+ s32 min; -+ s32 max; /* maximum value of control */ -+ s32 def; /* default value of control */ -+ s32 step; /* step size of the control */ -+ const s64 *imenu; /* integer menu array */ -+ u32 mmal_id; /* mmal parameter id */ -+ bm2835_mmal_v4l2_ctrl_cb *setter; -+}; -+ -+struct v4l2_to_mmal_effects_setting { -+ u32 v4l2_effect; -+ u32 mmal_effect; -+ s32 col_fx_enable; -+ s32 col_fx_fixed_cbcr; -+ u32 u; -+ u32 v; -+ u32 num_effect_params; -+ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; -+}; -+ -+static const struct v4l2_to_mmal_effects_setting -+ v4l2_to_mmal_effects_values[] = { -+ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, -+ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, -+ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, -+ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, -+ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } -+}; -+ -+ -+/* control handlers*/ -+ -+static int ctrl_set_rational(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ -+ } rational_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ rational_value.num = ctrl->val; -+ rational_value.den = 100; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &rational_value, -+ sizeof(rational_value)); -+} -+ -+static int ctrl_set_value(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ u32 u32_value; -+ struct vchiq_mmal_component *camera; -+ -+ camera = dev->component[MMAL_COMPONENT_CAMERA]; -+ -+ u32_value = ((ctrl->val % 360) / 90) * 90; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ -+ return ret; -+} -+ -+static int ctrl_set_flip(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ u32 u32_value; -+ struct vchiq_mmal_component *camera; -+ -+ if (ctrl->id == V4L2_CID_HFLIP) -+ dev->hflip = ctrl->val; -+ else -+ dev->vflip = ctrl->val; -+ -+ camera = dev->component[MMAL_COMPONENT_CAMERA]; -+ -+ if (dev->hflip && dev->vflip) -+ u32_value = MMAL_PARAM_MIRROR_BOTH; -+ else if (dev->hflip) -+ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; -+ else if (dev->vflip) -+ u32_value = MMAL_PARAM_MIRROR_VERTICAL; -+ else -+ u32_value = MMAL_PARAM_MIRROR_NONE; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ -+ return ret; -+ -+} -+ -+static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_AUTO: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ break; -+ -+ case V4L2_EXPOSURE_MANUAL: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; -+ break; -+ -+ case V4L2_EXPOSURE_SHUTTER_PRIORITY: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; -+ break; -+ -+ case V4L2_EXPOSURE_APERTURE_PRIORITY: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; -+ break; -+ -+ } -+ -+ /* todo: what about the other ten modes there are MMAL parameters for */ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_METERING_AVERAGE: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; -+ break; -+ -+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; -+ break; -+ -+ case V4L2_EXPOSURE_METERING_SPOT: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; -+ break; -+ -+ /* todo matrix weighting not added to Linux API till 3.9 -+ case V4L2_EXPOSURE_METERING_MATRIX: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; -+ break; -+ */ -+ -+ } -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_WHITE_BALANCE_MANUAL: -+ u32_value = MMAL_PARAM_AWBMODE_OFF; -+ break; -+ -+ case V4L2_WHITE_BALANCE_AUTO: -+ u32_value = MMAL_PARAM_AWBMODE_AUTO; -+ break; -+ -+ case V4L2_WHITE_BALANCE_INCANDESCENT: -+ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLUORESCENT: -+ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLUORESCENT_H: -+ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; -+ break; -+ -+ case V4L2_WHITE_BALANCE_HORIZON: -+ u32_value = MMAL_PARAM_AWBMODE_HORIZON; -+ break; -+ -+ case V4L2_WHITE_BALANCE_DAYLIGHT: -+ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLASH: -+ u32_value = MMAL_PARAM_AWBMODE_FLASH; -+ break; -+ -+ case V4L2_WHITE_BALANCE_CLOUDY: -+ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; -+ break; -+ -+ case V4L2_WHITE_BALANCE_SHADE: -+ u32_value = MMAL_PARAM_AWBMODE_SHADE; -+ break; -+ -+ } -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = -EINVAL; -+ int i, j; -+ struct vchiq_mmal_port *control; -+ struct mmal_parameter_imagefx_parameters imagefx; -+ -+ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { -+ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { -+ -+ imagefx.effect = -+ v4l2_to_mmal_effects_values[i].mmal_effect; -+ imagefx.num_effect_params = -+ v4l2_to_mmal_effects_values[i].num_effect_params; -+ -+ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) -+ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; -+ -+ for (j = 0; j < imagefx.num_effect_params; j++) -+ imagefx.effect_parameter[j] = -+ v4l2_to_mmal_effects_values[i].effect_params[j]; -+ -+ dev->colourfx.enable = -+ v4l2_to_mmal_effects_values[i].col_fx_enable; -+ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { -+ dev->colourfx.u = -+ v4l2_to_mmal_effects_values[i].u; -+ dev->colourfx.v = -+ v4l2_to_mmal_effects_values[i].v; -+ } -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ ret = vchiq_mmal_port_parameter_set( -+ dev->instance, control, -+ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, -+ &imagefx, sizeof(imagefx)); -+ if (ret) -+ goto exit; -+ -+ ret = vchiq_mmal_port_parameter_set( -+ dev->instance, control, -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &dev->colourfx, sizeof(dev->colourfx)); -+ } -+ } -+ -+exit: -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", -+ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, -+ dev->colourfx.enable ? "true" : "false", -+ dev->colourfx.u, dev->colourfx.v, -+ ret, (ret == 0 ? 0 : -EINVAL)); -+ return (ret == 0 ? 0 : EINVAL); -+} -+ -+static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = -EINVAL; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; -+ dev->colourfx.enable = ctrl->val & 0xff; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &dev->colourfx, sizeof(dev->colourfx)); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -+ mmal_ctrl, ctrl->id, ctrl->val, ret, -+ (ret == 0 ? 0 : -EINVAL)); -+ return (ret == 0 ? 0 : EINVAL); -+} -+ -+static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ struct vchiq_mmal_port *encoder_out; -+ -+ dev->capture.encode_bitrate = ctrl->val; -+ -+ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, -+ mmal_ctrl->mmal_id, -+ &ctrl->val, sizeof(ctrl->val)); -+ ret = 0; -+ return ret; -+} -+ -+static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 bitrate_mode; -+ struct vchiq_mmal_port *encoder_out; -+ -+ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ dev->capture.encode_bitrate_mode = ctrl->val; -+ switch (ctrl->val) { -+ default: -+ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: -+ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; -+ break; -+ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: -+ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; -+ break; -+ } -+ -+ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, -+ mmal_ctrl->mmal_id, -+ &bitrate_mode, -+ sizeof(bitrate_mode)); -+ return 0; -+} -+ -+static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *jpeg_out; -+ -+ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct bm2835_mmal_dev *dev = -+ container_of(ctrl->handler, struct bm2835_mmal_dev, -+ ctrl_handler); -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; -+ -+ if ((mmal_ctrl == NULL) || -+ (mmal_ctrl->id != ctrl->id) || -+ (mmal_ctrl->setter == NULL)) { -+ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); -+ return -EINVAL; -+ } -+ -+ return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+} -+ -+static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { -+ .s_ctrl = bm2835_mmal_s_ctrl, -+}; -+ -+ -+ -+static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { -+ { -+ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_SATURATION, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_CONTRAST, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, -+ 0, 100, 50, 1, NULL, -+ MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, -+ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, -+ MMAL_PARAMETER_ISO, &ctrl_set_value -+ }, -+ { -+ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value -+ }, -+/* { -+ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL -+ }, -+*/ { -+ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, -+ MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure -+ }, -+/* todo this needs mixing in with set exposure -+ { -+ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, -+ }, -+ */ -+ { -+ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, -+ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, -+ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, -+ MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value -+ }, -+ { -+ V4L2_CID_EXPOSURE_METERING, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, -+ MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode -+ }, -+ { -+ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -+ MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode -+ }, -+ { -+ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, 15, V4L2_COLORFX_NONE, 0, NULL, -+ MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect -+ }, -+ { -+ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, -+ 0, 0xffff, 0x8080, 1, NULL, -+ MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx -+ }, -+ { -+ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, -+ 0, 360, 0, 90, NULL, -+ MMAL_PARAMETER_ROTATION, &ctrl_set_rotate -+ }, -+ { -+ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ }, -+ { -+ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, -+ 0, 0, bitrate_mode_qmenu, -+ MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, -+ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, -+ MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate -+ }, -+ { -+ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, -+ 0, 100, -+ 30, 1, NULL, -+ MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor -+ }, -+}; -+ -+int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) -+{ -+ int c; -+ int ret; -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) { -+ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], -+ &v4l2_ctrls[c]); -+ if (ret) -+ break; -+ } -+ } -+ return ret; -+} -+ -+int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl_handler *hdl) -+{ -+ int c; -+ const struct bm2835_mmal_v4l2_ctrl *ctrl; -+ -+ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ ctrl = &v4l2_ctrls[c]; -+ -+ switch (ctrl->type) { -+ case MMAL_CONTROL_TYPE_STD: -+ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->min, ctrl->max, ctrl->step, ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_STD_MENU: -+ dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->max, ctrl->min, ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_INT_MENU: -+ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->max, ctrl->def, ctrl->imenu); -+ break; -+ -+ case MMAL_CONTROL_TYPE_CLUSTER: -+ /* skip this entry when constructing controls */ -+ continue; -+ } -+ -+ if (hdl->error) -+ break; -+ -+ dev->ctrls[c]->priv = (void *)ctrl; -+ } -+ -+ if (hdl->error) { -+ pr_err("error adding control %d/%d id 0x%x\n", c, -+ V4L2_CTRL_COUNT, ctrl->id); -+ return hdl->error; -+ } -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ ctrl = &v4l2_ctrls[c]; -+ -+ switch (ctrl->type) { -+ case MMAL_CONTROL_TYPE_CLUSTER: -+ v4l2_ctrl_auto_cluster(ctrl->min, -+ &dev->ctrls[c+1], -+ ctrl->max, -+ ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_STD: -+ case MMAL_CONTROL_TYPE_STD_MENU: -+ case MMAL_CONTROL_TYPE_INT_MENU: -+ break; -+ } -+ -+ } -+ -+ return 0; -+} -diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h -new file mode 100644 -index 0000000..84abbb3 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-common.h -@@ -0,0 +1,51 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * MMAL structures -+ * -+ */ -+ -+#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) -+#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') -+ -+/** Special value signalling that time is not known */ -+#define MMAL_TIME_UNKNOWN (1LL<<63) -+ -+/* mapping between v4l and mmal video modes */ -+struct mmal_fmt { -+ char *name; -+ u32 fourcc; /* v4l2 format id */ -+ u32 mmal; -+ int depth; -+ u32 mmal_component; /* MMAL component index to be used to encode */ -+}; -+ -+/* buffer for one video frame */ -+struct mmal_buffer { -+ /* v4l buffer data -- must be first */ -+ struct vb2_buffer vb; -+ -+ /* list of buffers available */ -+ struct list_head list; -+ -+ void *buffer; /* buffer pointer */ -+ unsigned long buffer_size; /* size of allocated buffer */ -+}; -+ -+/* */ -+struct mmal_colourfx { -+ s32 enable; -+ u32 u; -+ u32 v; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h -new file mode 100644 -index 0000000..856e80e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-encodings.h -@@ -0,0 +1,93 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') -+#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') -+#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') -+#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') -+#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') -+#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') -+#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') -+#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') -+#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') -+#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') -+#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') -+#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') -+#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') -+#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') -+ -+#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') -+#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') -+#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') -+#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') -+#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') -+#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') -+ -+#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') -+#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') -+#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') -+#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') -+#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') -+#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') -+#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') -+#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') -+#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') -+#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') -+#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') -+#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') -+#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') -+#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') -+#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') -+#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') -+#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') -+#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') -+#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') -+#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') -+#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') -+ -+/** SAND Video (YUVUV128) format, native format understood by VideoCore. -+ * This format is *not* opaque - if requested you will receive full frames -+ * of YUV_UV video. -+ */ -+#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') -+ -+/** VideoCore opaque image format, image handles are returned to -+ * the host but not the actual image data. -+ */ -+#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') -+ -+/** An EGL image handle -+ */ -+#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') -+ -+/* }@ */ -+ -+/** \name Pre-defined audio encodings */ -+/* @{ */ -+#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') -+#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') -+#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') -+#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') -+#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') -+#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') -+ -+/* Pre-defined H264 encoding variants */ -+ -+/** ISO 14496-10 Annex B byte stream format */ -+#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 -+/** ISO 14496-15 AVC stream format */ -+#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') -+/** Implicitly delineated NAL units without emulation prevention */ -+#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') -diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h -new file mode 100644 -index 0000000..66e8a6e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-common.h -@@ -0,0 +1,50 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#ifndef MMAL_MSG_COMMON_H -+#define MMAL_MSG_COMMON_H -+ -+enum mmal_msg_status { -+ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ -+ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ -+ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ -+ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ -+ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ -+ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ -+ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ -+ MMAL_MSG_STATUS_EIO, /**< I/O error */ -+ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ -+ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ -+ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ -+ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ -+ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ -+ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ -+ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ -+ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ -+}; -+ -+struct mmal_rect { -+ s32 x; /**< x coordinate (from left) */ -+ s32 y; /**< y coordinate (from top) */ -+ s32 width; /**< width */ -+ s32 height; /**< height */ -+}; -+ -+struct mmal_rational { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ -+}; -+ -+#endif /* MMAL_MSG_COMMON_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h -new file mode 100644 -index 0000000..123d86e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-format.h -@@ -0,0 +1,81 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#ifndef MMAL_MSG_FORMAT_H -+#define MMAL_MSG_FORMAT_H -+ -+#include "mmal-msg-common.h" -+ -+/* MMAL_ES_FORMAT_T */ -+ -+ -+struct mmal_audio_format { -+ u32 channels; /**< Number of audio channels */ -+ u32 sample_rate; /**< Sample rate */ -+ -+ u32 bits_per_sample; /**< Bits per sample */ -+ u32 block_align; /**< Size of a block of data */ -+}; -+ -+struct mmal_video_format { -+ u32 width; /**< Width of frame in pixels */ -+ u32 height; /**< Height of frame in rows of pixels */ -+ struct mmal_rect crop; /**< Visible region of the frame */ -+ struct mmal_rational frame_rate; /**< Frame rate */ -+ struct mmal_rational par; /**< Pixel aspect ratio */ -+ -+ /* FourCC specifying the color space of the video stream. See the -+ * \ref MmalColorSpace "pre-defined color spaces" for some examples. -+ */ -+ u32 color_space; -+}; -+ -+struct mmal_subpicture_format { -+ u32 x_offset; -+ u32 y_offset; -+}; -+ -+union mmal_es_specific_format { -+ struct mmal_audio_format audio; -+ struct mmal_video_format video; -+ struct mmal_subpicture_format subpicture; -+}; -+ -+/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ -+struct mmal_es_format { -+ u32 type; /* enum mmal_es_type */ -+ -+ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ -+ u32 encoding_variant; /* FourCC specifying the specific -+ * encoding variant of the elementary -+ * stream. -+ */ -+ -+ union mmal_es_specific_format *es; /* TODO: pointers in -+ * message serialisation?!? -+ */ -+ /* Type specific -+ * information for the -+ * elementary stream -+ */ -+ -+ u32 bitrate; /**< Bitrate in bits per second */ -+ u32 flags; /**< Flags describing properties of the elementary stream. */ -+ -+ u32 extradata_size; /**< Size of the codec specific data */ -+ u8 *extradata; /**< Codec specific data */ -+}; -+ -+#endif /* MMAL_MSG_FORMAT_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h -new file mode 100644 -index 0000000..a55c1ea ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-port.h -@@ -0,0 +1,107 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* MMAL_PORT_TYPE_T */ -+enum mmal_port_type { -+ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ -+ MMAL_PORT_TYPE_CONTROL, /**< Control port */ -+ MMAL_PORT_TYPE_INPUT, /**< Input port */ -+ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ -+ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ -+}; -+ -+/** The port is pass-through and doesn't need buffer headers allocated */ -+#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 -+/** The port wants to allocate the buffer payloads. -+ * This signals a preference that payload allocation should be done -+ * on this port for efficiency reasons. */ -+#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 -+/** The port supports format change events. -+ * This applies to input ports and is used to let the client know -+ * whether the port supports being reconfigured via a format -+ * change event (i.e. without having to disable the port). */ -+#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 -+ -+/* mmal port structure (MMAL_PORT_T) -+ * -+ * most elements are informational only, the pointer values for -+ * interogation messages are generally provided as additional -+ * strucures within the message. When used to set values only teh -+ * buffer_num, buffer_size and userdata parameters are writable. -+ */ -+struct mmal_port { -+ void *priv; /* Private member used by the framework */ -+ const char *name; /* Port name. Used for debugging purposes (RO) */ -+ -+ u32 type; /* Type of the port (RO) enum mmal_port_type */ -+ u16 index; /* Index of the port in its type list (RO) */ -+ u16 index_all; /* Index of the port in the list of all ports (RO) */ -+ -+ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ -+ struct mmal_es_format *format; /* Format of the elementary stream */ -+ -+ u32 buffer_num_min; /* Minimum number of buffers the port -+ * requires (RO). This is set by the -+ * component. -+ */ -+ -+ u32 buffer_size_min; /* Minimum size of buffers the port -+ * requires (RO). This is set by the -+ * component. -+ */ -+ -+ u32 buffer_alignment_min; /* Minimum alignment requirement for -+ * the buffers (RO). A value of -+ * zero means no special alignment -+ * requirements. This is set by the -+ * component. -+ */ -+ -+ u32 buffer_num_recommended; /* Number of buffers the port -+ * recommends for optimal -+ * performance (RO). A value of -+ * zero means no special -+ * recommendation. This is set -+ * by the component. -+ */ -+ -+ u32 buffer_size_recommended; /* Size of buffers the port -+ * recommends for optimal -+ * performance (RO). A value of -+ * zero means no special -+ * recommendation. This is set -+ * by the component. -+ */ -+ -+ u32 buffer_num; /* Actual number of buffers the port will use. -+ * This is set by the client. -+ */ -+ -+ u32 buffer_size; /* Actual maximum size of the buffers that -+ * will be sent to the port. This is set by -+ * the client. -+ */ -+ -+ void *component; /* Component this port belongs to (Read Only) */ -+ -+ void *userdata; /* Field reserved for use by the client */ -+ -+ u32 capabilities; /* Flags describing the capabilities of a -+ * port (RO). Bitwise combination of \ref -+ * portcapabilities "Port capabilities" -+ * values. -+ */ -+ -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h -new file mode 100644 -index 0000000..67b1076 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg.h -@@ -0,0 +1,404 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* all the data structures which serialise the MMAL protocol. note -+ * these are directly mapped onto the recived message data. -+ * -+ * BEWARE: They seem to *assume* pointers are u32 and that there is no -+ * structure padding! -+ * -+ * NOTE: this implementation uses kernel types to ensure sizes. Rather -+ * than assigning values to enums to force their size the -+ * implementation uses fixed size types and not the enums (though the -+ * comments have the actual enum type -+ */ -+ -+#define VC_MMAL_VER 15 -+#define VC_MMAL_MIN_VER 10 -+#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") -+ -+/* max total message size is 512 bytes */ -+#define MMAL_MSG_MAX_SIZE 512 -+/* with six 32bit header elements max payload is therefore 488 bytes */ -+#define MMAL_MSG_MAX_PAYLOAD 488 -+ -+#include "mmal-msg-common.h" -+#include "mmal-msg-format.h" -+#include "mmal-msg-port.h" -+ -+enum mmal_msg_type { -+ MMAL_MSG_TYPE_QUIT = 1, -+ MMAL_MSG_TYPE_SERVICE_CLOSED, -+ MMAL_MSG_TYPE_GET_VERSION, -+ MMAL_MSG_TYPE_COMPONENT_CREATE, -+ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ -+ MMAL_MSG_TYPE_COMPONENT_ENABLE, -+ MMAL_MSG_TYPE_COMPONENT_DISABLE, -+ MMAL_MSG_TYPE_PORT_INFO_GET, -+ MMAL_MSG_TYPE_PORT_INFO_SET, -+ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ -+ MMAL_MSG_TYPE_BUFFER_FROM_HOST, -+ MMAL_MSG_TYPE_BUFFER_TO_HOST, -+ MMAL_MSG_TYPE_GET_STATS, -+ MMAL_MSG_TYPE_PORT_PARAMETER_SET, -+ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ -+ MMAL_MSG_TYPE_EVENT_TO_HOST, -+ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, -+ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, -+ MMAL_MSG_TYPE_CONSUME_MEM, -+ MMAL_MSG_TYPE_LMK, /* 20 */ -+ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, -+ MMAL_MSG_TYPE_DRM_GET_LHS32, -+ MMAL_MSG_TYPE_DRM_GET_TIME, -+ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, -+ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ -+ MMAL_MSG_TYPE_HOST_LOG, -+ MMAL_MSG_TYPE_MSG_LAST -+}; -+ -+/* port action request messages differ depending on the action type */ -+enum mmal_msg_port_action_type { -+ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ -+ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ -+ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ -+ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ -+}; -+ -+struct mmal_msg_header { -+ u32 magic; -+ u32 type; /** enum mmal_msg_type */ -+ -+ /* Opaque handle to the control service */ -+ struct mmal_control_service *control_service; -+ -+ struct mmal_msg_context *context; /** a u32 per message context */ -+ u32 status; /** The status of the vchiq operation */ -+ u32 padding; -+}; -+ -+/* Send from VC to host to report version */ -+struct mmal_msg_version { -+ u32 flags; -+ u32 major; -+ u32 minor; -+ u32 minimum; -+}; -+ -+/* request to VC to create component */ -+struct mmal_msg_component_create { -+ void *client_component; /* component context */ -+ char name[128]; -+ u32 pid; /* For debug */ -+}; -+ -+/* reply from VC to component creation request */ -+struct mmal_msg_component_create_reply { -+ u32 status; /** enum mmal_msg_status - how does this differ to -+ * the one in the header? -+ */ -+ u32 component_handle; /* VideoCore handle for component */ -+ u32 input_num; /* Number of input ports */ -+ u32 output_num; /* Number of output ports */ -+ u32 clock_num; /* Number of clock ports */ -+}; -+ -+/* request to VC to destroy a component */ -+struct mmal_msg_component_destroy { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_destroy_reply { -+ u32 status; /** The component destruction status */ -+}; -+ -+ -+/* request and reply to VC to enable a component */ -+struct mmal_msg_component_enable { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_enable_reply { -+ u32 status; /** The component enable status */ -+}; -+ -+ -+/* request and reply to VC to disable a component */ -+struct mmal_msg_component_disable { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_disable_reply { -+ u32 status; /** The component disable status */ -+}; -+ -+/* request to VC to get port information */ -+struct mmal_msg_port_info_get { -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 index; /* port index to query */ -+}; -+ -+/* reply from VC to get port info request */ -+struct mmal_msg_port_info_get_reply { -+ u32 status; /** enum mmal_msg_status */ -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 port_index; /* port indexed in query */ -+ s32 found; /* unused */ -+ u32 port_handle; /**< Handle to use for this port */ -+ struct mmal_port port; -+ struct mmal_es_format format; /* elementry stream format */ -+ union mmal_es_specific_format es; /* es type specific data */ -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ -+}; -+ -+/* request to VC to set port information */ -+struct mmal_msg_port_info_set { -+ u32 component_handle; -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 port_index; /* port indexed in query */ -+ struct mmal_port port; -+ struct mmal_es_format format; -+ union mmal_es_specific_format es; -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; -+}; -+ -+/* reply from VC to port info set request */ -+struct mmal_msg_port_info_set_reply { -+ u32 status; -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 index; /* port indexed in query */ -+ s32 found; /* unused */ -+ u32 port_handle; /**< Handle to use for this port */ -+ struct mmal_port port; -+ struct mmal_es_format format; -+ union mmal_es_specific_format es; -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; -+}; -+ -+ -+/* port action requests that take a mmal_port as a parameter */ -+struct mmal_msg_port_action_port { -+ u32 component_handle; -+ u32 port_handle; -+ u32 action; /* enum mmal_msg_port_action_type */ -+ struct mmal_port port; -+}; -+ -+/* port action requests that take handles as a parameter */ -+struct mmal_msg_port_action_handle { -+ u32 component_handle; -+ u32 port_handle; -+ u32 action; /* enum mmal_msg_port_action_type */ -+ u32 connect_component_handle; -+ u32 connect_port_handle; -+}; -+ -+struct mmal_msg_port_action_reply { -+ u32 status; /** The port action operation status */ -+}; -+ -+ -+ -+ -+/* MMAL buffer transfer */ -+ -+/** Size of space reserved in a buffer message for short messages. */ -+#define MMAL_VC_SHORT_DATA 128 -+ -+/** Signals that the current payload is the end of the stream of data */ -+#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) -+/** Signals that the start of the current payload starts a frame */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) -+/** Signals that the end of the current payload ends a frame */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) -+/** Signals that the current payload contains only complete frames (>1) */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME \ -+ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) -+/** Signals that the current payload is a keyframe (i.e. self decodable) */ -+#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) -+/** Signals a discontinuity in the stream of data (e.g. after a seek). -+ * Can be used for instance by a decoder to reset its state */ -+#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) -+/** Signals a buffer containing some kind of config data for the component -+ * (e.g. codec config data) */ -+#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) -+/** Signals an encrypted payload */ -+#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) -+/** Signals a buffer containing side information */ -+#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) -+/** Signals a buffer which is the snapshot/postview image from a stills -+ * capture -+ */ -+#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) -+/** Signals a buffer which contains data known to be corrupted */ -+#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) -+/** Signals that a buffer failed to be transmitted */ -+#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) -+ -+struct mmal_driver_buffer { -+ u32 magic; -+ u32 component_handle; -+ u32 port_handle; -+ void *client_context; -+}; -+ -+/* buffer header */ -+struct mmal_buffer_header { -+ struct mmal_buffer_header *next; /* next header */ -+ void *priv; /* framework private data */ -+ u32 cmd; -+ void *data; -+ u32 alloc_size; -+ u32 length; -+ u32 offset; -+ u32 flags; -+ s64 pts; -+ s64 dts; -+ void *type; -+ void *user_data; -+}; -+ -+struct mmal_buffer_header_type_specific { -+ union { -+ struct { -+ u32 planes; -+ u32 offset[4]; -+ u32 pitch[4]; -+ u32 flags; -+ } video; -+ } u; -+}; -+ -+struct mmal_msg_buffer_from_host { -+ /* The front 32 bytes of the buffer header are copied -+ * back to us in the reply to allow for context. This -+ * area is used to store two mmal_driver_buffer structures to -+ * allow for multiple concurrent service users. -+ */ -+ /* control data */ -+ struct mmal_driver_buffer drvbuf; -+ -+ /* referenced control data for passthrough buffer management */ -+ struct mmal_driver_buffer drvbuf_ref; -+ struct mmal_buffer_header buffer_header; /* buffer header itself */ -+ struct mmal_buffer_header_type_specific buffer_header_type_specific; -+ s32 is_zero_copy; -+ s32 has_reference; -+ -+ /** allows short data to be xfered in control message */ -+ u32 payload_in_message; -+ u8 short_data[MMAL_VC_SHORT_DATA]; -+}; -+ -+ -+/* port parameter setting */ -+ -+#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 -+ -+struct mmal_msg_port_parameter_set { -+ u32 component_handle; /* component */ -+ u32 port_handle; /* port */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; -+}; -+ -+struct mmal_msg_port_parameter_set_reply { -+ u32 status; /** enum mmal_msg_status todo: how does this -+ * differ to the one in the header? -+ */ -+}; -+ -+/* port parameter getting */ -+ -+struct mmal_msg_port_parameter_get { -+ u32 component_handle; /* component */ -+ u32 port_handle; /* port */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+}; -+ -+struct mmal_msg_port_parameter_get_reply { -+ u32 status; /* Status of mmal_port_parameter_get call */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; -+}; -+ -+/* event messages */ -+#define MMAL_WORKER_EVENT_SPACE 256 -+ -+struct mmal_msg_event_to_host { -+ void *client_component; /* component context */ -+ -+ u32 port_type; -+ u32 port_num; -+ -+ u32 cmd; -+ u32 length; -+ u8 data[MMAL_WORKER_EVENT_SPACE]; -+ struct mmal_buffer_header *delayed_buffer; -+}; -+ -+/* all mmal messages are serialised through this structure */ -+struct mmal_msg { -+ /* header */ -+ struct mmal_msg_header h; -+ /* payload */ -+ union { -+ struct mmal_msg_version version; -+ -+ struct mmal_msg_component_create component_create; -+ struct mmal_msg_component_create_reply component_create_reply; -+ -+ struct mmal_msg_component_destroy component_destroy; -+ struct mmal_msg_component_destroy_reply component_destroy_reply; -+ -+ struct mmal_msg_component_enable component_enable; -+ struct mmal_msg_component_enable_reply component_enable_reply; -+ -+ struct mmal_msg_component_disable component_disable; -+ struct mmal_msg_component_disable_reply component_disable_reply; -+ -+ struct mmal_msg_port_info_get port_info_get; -+ struct mmal_msg_port_info_get_reply port_info_get_reply; -+ -+ struct mmal_msg_port_info_set port_info_set; -+ struct mmal_msg_port_info_set_reply port_info_set_reply; -+ -+ struct mmal_msg_port_action_port port_action_port; -+ struct mmal_msg_port_action_handle port_action_handle; -+ struct mmal_msg_port_action_reply port_action_reply; -+ -+ struct mmal_msg_buffer_from_host buffer_from_host; -+ -+ struct mmal_msg_port_parameter_set port_parameter_set; -+ struct mmal_msg_port_parameter_set_reply -+ port_parameter_set_reply; -+ struct mmal_msg_port_parameter_get -+ port_parameter_get; -+ struct mmal_msg_port_parameter_get_reply -+ port_parameter_get_reply; -+ -+ struct mmal_msg_event_to_host event_to_host; -+ -+ u8 payload[MMAL_MSG_MAX_PAYLOAD]; -+ } u; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h -new file mode 100644 -index 0000000..c611b58 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-parameters.h -@@ -0,0 +1,539 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* common parameters */ -+ -+/** @name Parameter groups -+ * Parameters are divided into groups, and then allocated sequentially within -+ * a group using an enum. -+ * @{ -+ */ -+ -+/** Common parameter ID group, used with many types of component. */ -+#define MMAL_PARAMETER_GROUP_COMMON (0<<16) -+/** Camera-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) -+/** Video-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) -+/** Audio-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) -+/** Clock-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) -+/** Miracast-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) -+ -+/* Common parameters */ -+enum mmal_parameter_common_type { -+ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ -+ = MMAL_PARAMETER_GROUP_COMMON, -+ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ -+ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ -+ -+ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ -+ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, -+ -+ /** MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ZERO_COPY, -+ -+ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ -+ MMAL_PARAMETER_BUFFER_REQUIREMENTS, -+ -+ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ -+ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ -+ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ -+ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ -+ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ -+ MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ -+}; -+ -+/* camera parameters */ -+ -+enum mmal_parameter_camera_type { -+ /* 0 */ -+ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ -+ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION -+ = MMAL_PARAMETER_GROUP_CAMERA, -+ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ -+ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ -+ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ -+ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ -+ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ -+ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ -+ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ -+ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ -+ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ -+ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ -+ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ -+ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ -+ -+ /* 0x10 */ -+ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ -+ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ -+ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ -+ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ -+ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ -+ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ -+ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ -+ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ -+ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ -+ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ -+ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ -+ /* 0x20 */ -+ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ -+ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ -+ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ -+ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ -+ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ -+ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ -+ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ -+ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ -+ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ -+ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ -+ /* 0x30 */ -+ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ -+ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ -+ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAMERA_MIN_ISO, -+ -+ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ -+ MMAL_PARAMETER_CAMERA_USE_CASE, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAPTURE_STATS_PASS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ENABLE_REGISTER_FILE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, -+ -+ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ -+ MMAL_PARAMETER_CONFIGFILE_REGISTERS, -+ -+ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ -+ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, -+ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ -+ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ -+ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ -+ -+ /* 0x40 */ -+ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+}; -+ -+enum mmal_parameter_camera_config_timestamp_mode { -+ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ -+ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value -+ * for the frame timestamp -+ */ -+ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp -+ * but subtract the -+ * timestamp of the first -+ * frame sent to give a -+ * zero based timestamp. -+ */ -+}; -+ -+/* camera configuration parameter */ -+struct mmal_parameter_camera_config { -+ /* Parameters for setting up the image pools */ -+ u32 max_stills_w; /* Max size of stills capture */ -+ u32 max_stills_h; -+ u32 stills_yuv422; /* Allow YUV422 stills capture */ -+ u32 one_shot_stills; /* Continuous or one shot stills captures. */ -+ -+ u32 max_preview_video_w; /* Max size of the preview or video -+ * capture frames -+ */ -+ u32 max_preview_video_h; -+ u32 num_preview_video_frames; -+ -+ /** Sets the height of the circular buffer for stills capture. */ -+ u32 stills_capture_circular_buffer_height; -+ -+ /** Allows preview/encode to resume as fast as possible after the stills -+ * input frame has been received, and then processes the still frame in -+ * the background whilst preview/encode has resumed. -+ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. -+ */ -+ u32 fast_preview_resume; -+ -+ /** Selects algorithm for timestamping frames if -+ * there is no clock component connected. -+ * enum mmal_parameter_camera_config_timestamp_mode -+ */ -+ s32 use_stc_timestamp; -+}; -+ -+ -+enum mmal_parameter_exposuremode { -+ MMAL_PARAM_EXPOSUREMODE_OFF, -+ MMAL_PARAM_EXPOSUREMODE_AUTO, -+ MMAL_PARAM_EXPOSUREMODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, -+ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, -+ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, -+ MMAL_PARAM_EXPOSUREMODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMODE_SNOW, -+ MMAL_PARAM_EXPOSUREMODE_BEACH, -+ MMAL_PARAM_EXPOSUREMODE_VERYLONG, -+ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, -+ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, -+ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, -+}; -+ -+enum mmal_parameter_exposuremeteringmode { -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, -+}; -+ -+enum mmal_parameter_awbmode { -+ MMAL_PARAM_AWBMODE_OFF, -+ MMAL_PARAM_AWBMODE_AUTO, -+ MMAL_PARAM_AWBMODE_SUNLIGHT, -+ MMAL_PARAM_AWBMODE_CLOUDY, -+ MMAL_PARAM_AWBMODE_SHADE, -+ MMAL_PARAM_AWBMODE_TUNGSTEN, -+ MMAL_PARAM_AWBMODE_FLUORESCENT, -+ MMAL_PARAM_AWBMODE_INCANDESCENT, -+ MMAL_PARAM_AWBMODE_FLASH, -+ MMAL_PARAM_AWBMODE_HORIZON, -+}; -+ -+enum mmal_parameter_imagefx { -+ MMAL_PARAM_IMAGEFX_NONE, -+ MMAL_PARAM_IMAGEFX_NEGATIVE, -+ MMAL_PARAM_IMAGEFX_SOLARIZE, -+ MMAL_PARAM_IMAGEFX_POSTERIZE, -+ MMAL_PARAM_IMAGEFX_WHITEBOARD, -+ MMAL_PARAM_IMAGEFX_BLACKBOARD, -+ MMAL_PARAM_IMAGEFX_SKETCH, -+ MMAL_PARAM_IMAGEFX_DENOISE, -+ MMAL_PARAM_IMAGEFX_EMBOSS, -+ MMAL_PARAM_IMAGEFX_OILPAINT, -+ MMAL_PARAM_IMAGEFX_HATCH, -+ MMAL_PARAM_IMAGEFX_GPEN, -+ MMAL_PARAM_IMAGEFX_PASTEL, -+ MMAL_PARAM_IMAGEFX_WATERCOLOUR, -+ MMAL_PARAM_IMAGEFX_FILM, -+ MMAL_PARAM_IMAGEFX_BLUR, -+ MMAL_PARAM_IMAGEFX_SATURATION, -+ MMAL_PARAM_IMAGEFX_COLOURSWAP, -+ MMAL_PARAM_IMAGEFX_WASHEDOUT, -+ MMAL_PARAM_IMAGEFX_POSTERISE, -+ MMAL_PARAM_IMAGEFX_COLOURPOINT, -+ MMAL_PARAM_IMAGEFX_COLOURBALANCE, -+ MMAL_PARAM_IMAGEFX_CARTOON, -+}; -+ -+/** Manner of video rate control */ -+enum mmal_parameter_rate_control_mode { -+ MMAL_VIDEO_RATECONTROL_DEFAULT, -+ MMAL_VIDEO_RATECONTROL_VARIABLE, -+ MMAL_VIDEO_RATECONTROL_CONSTANT, -+ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, -+ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES -+}; -+ -+/* video parameters */ -+ -+enum mmal_parameter_video_type { -+ /** @ref MMAL_DISPLAYREGION_T */ -+ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ -+ MMAL_PARAMETER_SUPPORTED_PROFILES, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ -+ MMAL_PARAMETER_PROFILE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_INTRAPERIOD, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ -+ MMAL_PARAMETER_RATECONTROL, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ -+ MMAL_PARAMETER_NALUNITFORMAT, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Setting the value to zero resets to the default (one slice per frame). -+ */ -+ MMAL_PARAMETER_MB_ROWS_PER_SLICE, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ -+ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ -+ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ -+ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ -+ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, -+ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ -+ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ -+ MMAL_PARAMETER_VIDEO_BIT_RATE, -+ -+ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ -+ MMAL_PARAMETER_VIDEO_FRAME_RATE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, -+ -+ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Changing this parameter from the default can reduce frame rate -+ * because image buffers need to be re-pitched. -+ */ -+ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Changing this parameter from the default can reduce frame rate -+ * because image buffers need to be re-pitched. -+ */ -+ MMAL_PARAMETER_VIDEO_ALIGN_VERT, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, -+ -+ /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, -+ -+ /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, -+ -+ /* H264 specific parameters */ -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ -+ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ -+ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, -+ -+ /** @ref MMAL_PARAMETER_BYTES_T */ -+ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 -+}; -+ -+/** Valid mirror modes */ -+enum mmal_parameter_mirror { -+ MMAL_PARAM_MIRROR_NONE, -+ MMAL_PARAM_MIRROR_VERTICAL, -+ MMAL_PARAM_MIRROR_HORIZONTAL, -+ MMAL_PARAM_MIRROR_BOTH, -+}; -+ -+enum mmal_parameter_displaytransform { -+ MMAL_DISPLAY_ROT0 = 0, -+ MMAL_DISPLAY_MIRROR_ROT0 = 1, -+ MMAL_DISPLAY_MIRROR_ROT180 = 2, -+ MMAL_DISPLAY_ROT180 = 3, -+ MMAL_DISPLAY_MIRROR_ROT90 = 4, -+ MMAL_DISPLAY_ROT270 = 5, -+ MMAL_DISPLAY_ROT90 = 6, -+ MMAL_DISPLAY_MIRROR_ROT270 = 7, -+}; -+ -+enum mmal_parameter_displaymode { -+ MMAL_DISPLAY_MODE_FILL = 0, -+ MMAL_DISPLAY_MODE_LETTERBOX = 1, -+}; -+ -+enum mmal_parameter_displayset { -+ MMAL_DISPLAY_SET_NONE = 0, -+ MMAL_DISPLAY_SET_NUM = 1, -+ MMAL_DISPLAY_SET_FULLSCREEN = 2, -+ MMAL_DISPLAY_SET_TRANSFORM = 4, -+ MMAL_DISPLAY_SET_DEST_RECT = 8, -+ MMAL_DISPLAY_SET_SRC_RECT = 0x10, -+ MMAL_DISPLAY_SET_MODE = 0x20, -+ MMAL_DISPLAY_SET_PIXEL = 0x40, -+ MMAL_DISPLAY_SET_NOASPECT = 0x80, -+ MMAL_DISPLAY_SET_LAYER = 0x100, -+ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, -+ MMAL_DISPLAY_SET_ALPHA = 0x400, -+}; -+ -+struct mmal_parameter_displayregion { -+ /** Bitfield that indicates which fields are set and should be -+ * used. All other fields will maintain their current value. -+ * \ref MMAL_DISPLAYSET_T defines the bits that can be -+ * combined. -+ */ -+ u32 set; -+ -+ /** Describes the display output device, with 0 typically -+ * being a directly connected LCD display. The actual values -+ * will depend on the hardware. Code using hard-wired numbers -+ * (e.g. 2) is certain to fail. -+ */ -+ -+ u32 display_num; -+ /** Indicates that we are using the full device screen area, -+ * rather than a window of the display. If zero, then -+ * dest_rect is used to specify a region of the display to -+ * use. -+ */ -+ -+ s32 fullscreen; -+ /** Indicates any rotation or flipping used to map frames onto -+ * the natural display orientation. -+ */ -+ u32 transform; /* enum mmal_parameter_displaytransform */ -+ -+ /** Where to display the frame within the screen, if -+ * fullscreen is zero. -+ */ -+ struct vchiq_mmal_rect dest_rect; -+ -+ /** Indicates which area of the frame to display. If all -+ * values are zero, the whole frame will be used. -+ */ -+ struct vchiq_mmal_rect src_rect; -+ -+ /** If set to non-zero, indicates that any display scaling -+ * should disregard the aspect ratio of the frame region being -+ * displayed. -+ */ -+ s32 noaspect; -+ -+ /** Indicates how the image should be scaled to fit the -+ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates -+ * that the image should fill the screen by potentially -+ * cropping the frames. Setting \code mode \endcode to \code -+ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the -+ * source region should be displayed and black bars added if -+ * necessary. -+ */ -+ u32 mode; /* enum mmal_parameter_displaymode */ -+ -+ /** If non-zero, defines the width of a source pixel relative -+ * to \code pixel_y \endcode. If zero, then pixels default to -+ * being square. -+ */ -+ u32 pixel_x; -+ -+ /** If non-zero, defines the height of a source pixel relative -+ * to \code pixel_x \endcode. If zero, then pixels default to -+ * being square. -+ */ -+ u32 pixel_y; -+ -+ /** Sets the relative depth of the images, with greater values -+ * being in front of smaller values. -+ */ -+ u32 layer; -+ -+ /** Set to non-zero to ensure copy protection is used on -+ * output. -+ */ -+ s32 copyprotect_required; -+ -+ /** Level of opacity of the layer, where zero is fully -+ * transparent and 255 is fully opaque. -+ */ -+ u32 alpha; -+}; -+ -+#define MMAL_MAX_IMAGEFX_PARAMETERS 5 -+ -+struct mmal_parameter_imagefx_parameters { -+ enum mmal_parameter_imagefx effect; -+ u32 num_effect_params; -+ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c -new file mode 100644 -index 0000000..a06fb44 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.c -@@ -0,0 +1,1916 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * V4L2 driver MMAL vchiq interface code -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include +#include -+#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include "vchiq_core.h" ++#include "vchiq_debugfs.h" + -+#include "mmal-common.h" -+#include "mmal-vchiq.h" -+#include "mmal-msg.h" + -+#define USE_VCHIQ_ARM -+#include "interface/vchi/vchi.h" ++enum vc_suspend_status { ++ VC_SUSPEND_FORCE_CANCELED = -3, /* Force suspend canceled, too busy */ ++ VC_SUSPEND_REJECTED = -2, /* Videocore rejected suspend request */ ++ VC_SUSPEND_FAILED = -1, /* Videocore suspend failed */ ++ VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ ++ VC_SUSPEND_REQUESTED, /* User has requested suspend */ ++ VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ ++ VC_SUSPEND_SUSPENDED /* Videocore suspend succeeded */ ++}; + -+/* maximum number of components supported */ -+#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++enum vc_resume_status { ++ VC_RESUME_FAILED = -1, /* Videocore resume failed */ ++ VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ ++ VC_RESUME_REQUESTED, /* User has requested resume */ ++ VC_RESUME_IN_PROGRESS, /* Slot handler has received resume request */ ++ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ ++}; + -+/*#define FULL_MSG_DUMP 1*/ + -+#ifdef DEBUG -+static const char *const msg_type_names[] = { -+ "UNKNOWN", -+ "QUIT", ++enum USE_TYPE_E { ++ USE_TYPE_SERVICE, ++ USE_TYPE_SERVICE_NO_RESUME, ++ USE_TYPE_VCHIQ ++}; ++ ++ ++ ++typedef struct vchiq_arm_state_struct { ++ /* Keepalive-related data */ ++ struct task_struct *ka_thread; ++ struct completion ka_evt; ++ atomic_t ka_use_count; ++ atomic_t ka_use_ack_count; ++ atomic_t ka_release_count; ++ ++ struct completion vc_suspend_complete; ++ struct completion vc_resume_complete; ++ ++ rwlock_t susp_res_lock; ++ enum vc_suspend_status vc_suspend_state; ++ enum vc_resume_status vc_resume_state; ++ ++ unsigned int wake_address; ++ ++ struct timer_list suspend_timer; ++ int suspend_timer_timeout; ++ int suspend_timer_running; ++ ++ /* Global use count for videocore. ++ ** This is equal to the sum of the use counts for all services. When ++ ** this hits zero the videocore suspend procedure will be initiated. ++ */ ++ int videocore_use_count; ++ ++ /* Use count to track requests from videocore peer. ++ ** This use count is not associated with a service, so needs to be ++ ** tracked separately with the state. ++ */ ++ int peer_use_count; ++ ++ /* Flag to indicate whether resume is blocked. This happens when the ++ ** ARM is suspending ++ */ ++ struct completion resume_blocker; ++ int resume_blocked; ++ struct completion blocked_blocker; ++ int blocked_count; ++ ++ int autosuspend_override; ++ ++ /* Flag to indicate that the first vchiq connect has made it through. ++ ** This means that both sides should be fully ready, and we should ++ ** be able to suspend after this point. ++ */ ++ int first_connect; ++ ++ unsigned long long suspend_start_time; ++ unsigned long long sleep_start_time; ++ unsigned long long resume_start_time; ++ unsigned long long last_wake_time; ++ ++} VCHIQ_ARM_STATE_T; ++ ++extern int vchiq_arm_log_level; ++extern int vchiq_susp_log_level; ++ ++extern int __init ++vchiq_platform_init(VCHIQ_STATE_T *state); ++ ++extern void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATE_T * ++vchiq_get_state(void); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_force_suspend(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_arm_allow_resume(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_vcresume(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state); ++ ++extern int ++vchiq_check_resume(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_check_suspend(VCHIQ_STATE_T *state); ++ VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_platform_use_suspend_timer(void); ++ ++extern void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_videocore_wanted(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ enum USE_TYPE_E use_type); ++extern VCHIQ_STATUS_T ++vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_DEBUGFS_NODE_T * ++vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance); ++ ++extern void ++vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace); ++ ++extern void ++set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_suspend_status new_state); ++ ++extern void ++set_resume_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_resume_status new_state); ++ ++extern void ++start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state); ++ ++ ++#endif /* VCHIQ_ARM_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h +new file mode 100644 +index 0000000..df64581 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h +@@ -0,0 +1,37 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++const char *vchiq_get_build_hostname(void); ++const char *vchiq_get_build_version(void); ++const char *vchiq_get_build_time(void); ++const char *vchiq_get_build_date(void); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +new file mode 100644 +index 0000000..d2797db +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +@@ -0,0 +1,69 @@ ++/** ++ * Copyright (c) 2010-2014 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CFG_H ++#define VCHIQ_CFG_H ++ ++#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') ++/* The version of VCHIQ - change with any non-trivial change */ ++#define VCHIQ_VERSION 8 ++/* The minimum compatible version - update to match VCHIQ_VERSION with any ++** incompatible change */ ++#define VCHIQ_VERSION_MIN 3 ++ ++/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ ++#define VCHIQ_VERSION_LIB_VERSION 7 ++ ++/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ ++#define VCHIQ_VERSION_CLOSE_DELIVERED 7 ++ ++/* The version that made it safe to use SYNCHRONOUS mode */ ++#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 ++ ++#define VCHIQ_MAX_STATES 1 ++#define VCHIQ_MAX_SERVICES 4096 ++#define VCHIQ_MAX_SLOTS 128 ++#define VCHIQ_MAX_SLOTS_PER_SIDE 64 ++ ++#define VCHIQ_NUM_CURRENT_BULKS 32 ++#define VCHIQ_NUM_SERVICE_BULKS 4 ++ ++#ifndef VCHIQ_ENABLE_DEBUG ++#define VCHIQ_ENABLE_DEBUG 1 ++#endif ++ ++#ifndef VCHIQ_ENABLE_STATS ++#define VCHIQ_ENABLE_STATS 1 ++#endif ++ ++#endif /* VCHIQ_CFG_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c +new file mode 100644 +index 0000000..5efc62f +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c +@@ -0,0 +1,120 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_connected.h" ++#include "vchiq_core.h" ++#include "vchiq_killable.h" ++#include ++#include ++ ++#define MAX_CALLBACKS 10 ++ ++static int g_connected; ++static int g_num_deferred_callbacks; ++static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[MAX_CALLBACKS]; ++static int g_once_init; ++static struct mutex g_connected_mutex; ++ ++/**************************************************************************** ++* ++* Function to initialize our lock. ++* ++***************************************************************************/ ++ ++static void connected_init(void) ++{ ++ if (!g_once_init) { ++ mutex_init(&g_connected_mutex); ++ g_once_init = 1; ++ } ++} ++ ++/**************************************************************************** ++* ++* This function is used to defer initialization until the vchiq stack is ++* initialized. If the stack is already initialized, then the callback will ++* be made immediately, otherwise it will be deferred until ++* vchiq_call_connected_callbacks is called. ++* ++***************************************************************************/ ++ ++void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback) ++{ ++ connected_init(); ++ ++ if (mutex_lock_interruptible(&g_connected_mutex) != 0) ++ return; ++ ++ if (g_connected) ++ /* We're already connected. Call the callback immediately. */ ++ ++ callback(); ++ else { ++ if (g_num_deferred_callbacks >= MAX_CALLBACKS) ++ vchiq_log_error(vchiq_core_log_level, ++ "There already %d callback registered - " ++ "please increase MAX_CALLBACKS", ++ g_num_deferred_callbacks); ++ else { ++ g_deferred_callback[g_num_deferred_callbacks] = ++ callback; ++ g_num_deferred_callbacks++; ++ } ++ } ++ mutex_unlock(&g_connected_mutex); ++} ++ ++/**************************************************************************** ++* ++* This function is called by the vchiq stack once it has been connected to ++* the videocore and clients can start to use the stack. ++* ++***************************************************************************/ ++ ++void vchiq_call_connected_callbacks(void) ++{ ++ int i; ++ ++ connected_init(); ++ ++ if (mutex_lock_interruptible(&g_connected_mutex) != 0) ++ return; ++ ++ for (i = 0; i < g_num_deferred_callbacks; i++) ++ g_deferred_callback[i](); ++ ++ g_num_deferred_callbacks = 0; ++ g_connected = 1; ++ mutex_unlock(&g_connected_mutex); ++} ++EXPORT_SYMBOL(vchiq_add_connected_callback); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h +new file mode 100644 +index 0000000..863b3e3 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h +@@ -0,0 +1,50 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CONNECTED_H ++#define VCHIQ_CONNECTED_H ++ ++/* ---- Include Files ----------------------------------------------------- */ ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++typedef void (*VCHIQ_CONNECTED_CALLBACK_T)(void); ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback); ++void vchiq_call_connected_callbacks(void); ++ ++#endif /* VCHIQ_CONNECTED_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +new file mode 100644 +index 0000000..835688b +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -0,0 +1,3934 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_core.h" ++#include "vchiq_killable.h" ++ ++#define VCHIQ_SLOT_HANDLER_STACK 8192 ++ ++#define HANDLE_STATE_SHIFT 12 ++ ++#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index)) ++#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index)) ++#define SLOT_INDEX_FROM_DATA(state, data) \ ++ (((unsigned int)((char *)data - (char *)state->slot_data)) / \ ++ VCHIQ_SLOT_SIZE) ++#define SLOT_INDEX_FROM_INFO(state, info) \ ++ ((unsigned int)(info - state->slot_info)) ++#define SLOT_QUEUE_INDEX_FROM_POS(pos) \ ++ ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) ++ ++#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) ++ ++#define SRVTRACE_LEVEL(srv) \ ++ (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level) ++#define SRVTRACE_ENABLED(srv, lev) \ ++ (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev))) ++ ++struct vchiq_open_payload { ++ int fourcc; ++ int client_id; ++ short version; ++ short version_min; ++}; ++ ++struct vchiq_openack_payload { ++ short version; ++}; ++ ++enum ++{ ++ QMFLAGS_IS_BLOCKING = (1 << 0), ++ QMFLAGS_NO_MUTEX_LOCK = (1 << 1), ++ QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2) ++}; ++ ++/* we require this for consistency between endpoints */ ++vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); ++vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); ++vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS)); ++vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES)); ++vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN); ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; ++ ++static atomic_t pause_bulks_count = ATOMIC_INIT(0); ++ ++static DEFINE_SPINLOCK(service_spinlock); ++DEFINE_SPINLOCK(bulk_waiter_spinlock); ++DEFINE_SPINLOCK(quota_spinlock); ++ ++VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; ++static unsigned int handle_seq; ++ ++static const char *const srvstate_names[] = { ++ "FREE", ++ "HIDDEN", ++ "LISTENING", ++ "OPENING", ++ "OPEN", ++ "OPENSYNC", ++ "CLOSESENT", ++ "CLOSERECVD", ++ "CLOSEWAIT", ++ "CLOSED" ++}; ++ ++static const char *const reason_names[] = { ++ "SERVICE_OPENED", + "SERVICE_CLOSED", -+ "GET_VERSION", -+ "COMPONENT_CREATE", -+ "COMPONENT_DESTROY", -+ "COMPONENT_ENABLE", -+ "COMPONENT_DISABLE", -+ "PORT_INFO_GET", -+ "PORT_INFO_SET", -+ "PORT_ACTION", -+ "BUFFER_FROM_HOST", -+ "BUFFER_TO_HOST", -+ "GET_STATS", -+ "PORT_PARAMETER_SET", -+ "PORT_PARAMETER_GET", -+ "EVENT_TO_HOST", -+ "GET_CORE_STATS_FOR_PORT", -+ "OPAQUE_ALLOCATOR", -+ "CONSUME_MEM", -+ "LMK", -+ "OPAQUE_ALLOCATOR_DESC", -+ "DRM_GET_LHS32", -+ "DRM_GET_TIME", -+ "BUFFER_FROM_HOST_ZEROLEN", -+ "PORT_FLUSH", -+ "HOST_LOG", -+}; -+#endif -+ -+static const char *const port_action_type_names[] = { -+ "UNKNOWN", -+ "ENABLE", -+ "DISABLE", -+ "FLUSH", -+ "CONNECT", -+ "DISCONNECT", -+ "SET_REQUIREMENTS", ++ "MESSAGE_AVAILABLE", ++ "BULK_TRANSMIT_DONE", ++ "BULK_RECEIVE_DONE", ++ "BULK_TRANSMIT_ABORTED", ++ "BULK_RECEIVE_ABORTED" +}; + -+#if defined(DEBUG) -+#if defined(FULL_MSG_DUMP) -+#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ -+ do { \ -+ pr_debug(TITLE" type:%s(%d) length:%d\n", \ -+ msg_type_names[(MSG)->h.type], \ -+ (MSG)->h.type, (MSG_LEN)); \ -+ print_hex_dump(KERN_DEBUG, "<h.type], \ -+ (MSG)->h.type, (MSG_LEN)); \ -+ } -+#endif -+#else -+#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) -+#endif -+ -+/* normal message context */ -+struct mmal_msg_context { -+ union { -+ struct { -+ /* work struct for defered callback - must come first */ -+ struct work_struct work; -+ /* mmal instance */ -+ struct vchiq_mmal_instance *instance; -+ /* mmal port */ -+ struct vchiq_mmal_port *port; -+ /* actual buffer used to store bulk reply */ -+ struct mmal_buffer *buffer; -+ /* amount of buffer used */ -+ unsigned long buffer_used; -+ /* MMAL buffer flags */ -+ u32 mmal_flags; -+ /* Presentation and Decode timestamps */ -+ s64 pts; -+ s64 dts; -+ -+ int status; /* context status */ -+ -+ } bulk; /* bulk data */ -+ -+ struct { -+ /* message handle to release */ -+ VCHI_HELD_MSG_T msg_handle; -+ /* pointer to received message */ -+ struct mmal_msg *msg; -+ /* received message length */ -+ u32 msg_len; -+ /* completion upon reply */ -+ struct completion cmplt; -+ } sync; /* synchronous response */ -+ } u; -+ ++static const char *const conn_state_names[] = { ++ "DISCONNECTED", ++ "CONNECTING", ++ "CONNECTED", ++ "PAUSING", ++ "PAUSE_SENT", ++ "PAUSED", ++ "RESUMING", ++ "PAUSE_TIMEOUT", ++ "RESUME_TIMEOUT" +}; + -+struct vchiq_mmal_instance { -+ VCHI_SERVICE_HANDLE_T handle; + -+ /* ensure serialised access to service */ -+ struct mutex vchiq_mutex; ++static void ++release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header); + -+ /* ensure serialised access to bulk operations */ -+ struct mutex bulk_mutex; -+ -+ /* vmalloc page to receive scratch bulk xfers into */ -+ void *bulk_scratch; -+ -+ /* component to use next */ -+ int component_idx; -+ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; -+}; -+ -+static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance -+ *instance) ++static const char *msg_type_str(unsigned int msg_type) +{ -+ struct mmal_msg_context *msg_context; -+ -+ /* todo: should this be allocated from a pool to avoid kmalloc */ -+ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); -+ memset(msg_context, 0, sizeof(*msg_context)); -+ -+ return msg_context; ++ switch (msg_type) { ++ case VCHIQ_MSG_PADDING: return "PADDING"; ++ case VCHIQ_MSG_CONNECT: return "CONNECT"; ++ case VCHIQ_MSG_OPEN: return "OPEN"; ++ case VCHIQ_MSG_OPENACK: return "OPENACK"; ++ case VCHIQ_MSG_CLOSE: return "CLOSE"; ++ case VCHIQ_MSG_DATA: return "DATA"; ++ case VCHIQ_MSG_BULK_RX: return "BULK_RX"; ++ case VCHIQ_MSG_BULK_TX: return "BULK_TX"; ++ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE"; ++ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE"; ++ case VCHIQ_MSG_PAUSE: return "PAUSE"; ++ case VCHIQ_MSG_RESUME: return "RESUME"; ++ case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE"; ++ case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE"; ++ case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE"; ++ } ++ return "???"; +} + -+static void release_msg_context(struct mmal_msg_context *msg_context) ++static inline void ++vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate) +{ -+ kfree(msg_context); ++ vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate], ++ srvstate_names[newstate]); ++ service->srvstate = newstate; +} + -+/* deals with receipt of event to host message */ -+static void event_to_host_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, u32 msg_len) ++VCHIQ_SERVICE_T * ++find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle) +{ -+ pr_debug("unhandled event\n"); -+ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", -+ msg->u.event_to_host.client_component, -+ msg->u.event_to_host.port_type, -+ msg->u.event_to_host.port_num, -+ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++ VCHIQ_SERVICE_T *service; ++ ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->handle == handle)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); ++ ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); ++ ++ return service; +} + -+/* workqueue scheduled callback -+ * -+ * we do this because it is important we do not call any other vchiq -+ * sync calls from witin the message delivery thread -+ */ -+static void buffer_work_cb(struct work_struct *work) ++VCHIQ_SERVICE_T * ++find_service_by_port(VCHIQ_STATE_T *state, int localport) +{ -+ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; -+ -+ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port, -+ msg_context->u.bulk.status, -+ msg_context->u.bulk.buffer, -+ msg_context->u.bulk.buffer_used, -+ msg_context->u.bulk.mmal_flags, -+ msg_context->u.bulk.dts, -+ msg_context->u.bulk.pts); -+ -+ /* release message context */ -+ release_msg_context(msg_context); -+} -+ -+/* enqueue a bulk receive for a given message context */ -+static int bulk_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ struct mmal_msg_context *msg_context) -+{ -+ unsigned long rd_len; -+ unsigned long flags = 0; -+ int ret; -+ -+ /* bulk mutex stops other bulk operations while we have a -+ * receive in progress - released in callback -+ */ -+ ret = mutex_lock_interruptible(&instance->bulk_mutex); -+ if (ret != 0) -+ return ret; -+ -+ rd_len = msg->u.buffer_from_host.buffer_header.length; -+ -+ /* take buffer from queue */ -+ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); -+ if (list_empty(&msg_context->u.bulk.port->buffers)) { -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ pr_err("buffer list empty trying to submit bulk receive\n"); -+ -+ /* todo: this is a serious error, we should never have -+ * commited a buffer_to_host operation to the mmal -+ * port without the buffer to back it up (underflow -+ * handling) and there is no obvious way to deal with -+ * this - how is the mmal servie going to react when -+ * we fail to do the xfer and reschedule a buffer when -+ * it arrives? perhaps a starved flag to indicate a -+ * waiting bulk receive? -+ */ -+ -+ mutex_unlock(&instance->bulk_mutex); -+ -+ return -EINVAL; ++ VCHIQ_SERVICE_T *service = NULL; ++ if ((unsigned int)localport <= VCHIQ_PORT_MAX) { ++ spin_lock(&service_spinlock); ++ service = state->services[localport]; ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + } + -+ msg_context->u.bulk.buffer = -+ list_entry(msg_context->u.bulk.port->buffers.next, -+ struct mmal_buffer, list); -+ list_del(&msg_context->u.bulk.buffer->list); ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid port %d", localport); + -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ -+ /* ensure we do not overrun the available buffer */ -+ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { -+ rd_len = msg_context->u.bulk.buffer->buffer_size; -+ pr_warn("short read as not enough receive buffer space\n"); -+ /* todo: is this the correct response, what happens to -+ * the rest of the message data? -+ */ -+ } -+ -+ /* store length */ -+ msg_context->u.bulk.buffer_used = rd_len; -+ msg_context->u.bulk.mmal_flags = -+ msg->u.buffer_from_host.buffer_header.flags; -+ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; -+ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; -+ -+ // only need to flush L1 cache here, as VCHIQ takes care of the L2 -+ // cache. -+ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); -+ -+ /* queue the bulk submission */ -+ vchi_service_use(instance->handle); -+ ret = vchi_bulk_queue_receive(instance->handle, -+ msg_context->u.bulk.buffer->buffer, -+ /* Actual receive needs to be a multiple -+ * of 4 bytes -+ */ -+ (rd_len + 3) & ~3, -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, -+ msg_context); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret != 0) { -+ /* callback will not be clearing the mutex */ -+ mutex_unlock(&instance->bulk_mutex); -+ } -+ -+ return ret; ++ return service; +} + -+/* enque a dummy bulk receive for a given message context */ -+static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ int ret; ++VCHIQ_SERVICE_T * ++find_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle) { ++ VCHIQ_SERVICE_T *service; + -+ /* bulk mutex stops other bulk operations while we have a -+ * receive in progress - released in callback -+ */ -+ ret = mutex_lock_interruptible(&instance->bulk_mutex); -+ if (ret != 0) -+ return ret; ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->handle == handle) && ++ (service->instance == instance)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + -+ /* zero length indicates this was a dummy transfer */ -+ msg_context->u.bulk.buffer_used = 0; ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); + -+ /* queue the bulk submission */ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_bulk_queue_receive(instance->handle, -+ instance->bulk_scratch, -+ 8, -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, -+ msg_context); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret != 0) { -+ /* callback will not be clearing the mutex */ -+ mutex_unlock(&instance->bulk_mutex); -+ } -+ -+ return ret; ++ return service; +} + -+/* data in message, memcpy from packet into output buffer */ -+static int inline_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ struct mmal_msg_context *msg_context) -+{ -+ unsigned long flags = 0; ++VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle) { ++ VCHIQ_SERVICE_T *service; + -+ /* take buffer from queue */ -+ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); -+ if (list_empty(&msg_context->u.bulk.port->buffers)) { -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ pr_err("buffer list empty trying to receive inline\n"); ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && ++ ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && ++ (service->handle == handle) && ++ (service->instance == instance)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + -+ /* todo: this is a serious error, we should never have -+ * commited a buffer_to_host operation to the mmal -+ * port without the buffer to back it up (with -+ * underflow handling) and there is no obvious way to -+ * deal with this. Less bad than the bulk case as we -+ * can just drop this on the floor but...unhelpful -+ */ -+ return -EINVAL; -+ } ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); + -+ msg_context->u.bulk.buffer = -+ list_entry(msg_context->u.bulk.port->buffers.next, -+ struct mmal_buffer, list); -+ list_del(&msg_context->u.bulk.buffer->list); -+ -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ -+ memcpy(msg_context->u.bulk.buffer->buffer, -+ msg->u.buffer_from_host.short_data, -+ msg->u.buffer_from_host.payload_in_message); -+ -+ msg_context->u.bulk.buffer_used = -+ msg->u.buffer_from_host.payload_in_message; -+ -+ return 0; ++ return service; +} + -+/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ -+static int -+buffer_from_host(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++VCHIQ_SERVICE_T * ++next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, ++ int *pidx) +{ -+ struct mmal_msg_context *msg_context; -+ struct mmal_msg m; -+ int ret; ++ VCHIQ_SERVICE_T *service = NULL; ++ int idx = *pidx; + -+ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); -+ -+ /* bulk mutex stops other bulk operations while we -+ * have a receive in progress -+ */ -+ if (mutex_lock_interruptible(&instance->bulk_mutex)) -+ return -EINTR; -+ -+ /* get context */ -+ msg_context = get_msg_context(instance); -+ if (msg_context == NULL) -+ return -ENOMEM; -+ -+ /* store bulk message context for when data arrives */ -+ msg_context->u.bulk.instance = instance; -+ msg_context->u.bulk.port = port; -+ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ -+ msg_context->u.bulk.buffer_used = 0; -+ -+ /* initialise work structure ready to schedule callback */ -+ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); -+ -+ /* prep the buffer from host message */ -+ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ -+ -+ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; -+ m.h.magic = MMAL_MAGIC; -+ m.h.context = msg_context; -+ m.h.status = 0; -+ -+ /* drvbuf is our private data passed back */ -+ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; -+ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; -+ m.u.buffer_from_host.drvbuf.port_handle = port->handle; -+ m.u.buffer_from_host.drvbuf.client_context = msg_context; -+ -+ /* buffer header */ -+ m.u.buffer_from_host.buffer_header.cmd = 0; -+ m.u.buffer_from_host.buffer_header.data = buf->buffer; -+ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; -+ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ -+ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ -+ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ -+ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; -+ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; -+ -+ /* clear buffer type sepecific data */ -+ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, -+ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); -+ -+ /* no payload in message */ -+ m.u.buffer_from_host.payload_in_message = 0; -+ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_msg_queue(instance->handle, &m, -+ sizeof(struct mmal_msg_header) + -+ sizeof(m.u.buffer_from_host), -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (ret != 0) { -+ release_msg_context(msg_context); -+ /* todo: is this correct error value? */ -+ } -+ -+ vchi_service_release(instance->handle); -+ -+ mutex_unlock(&instance->bulk_mutex); -+ -+ return ret; -+} -+ -+/* submit a buffer to the mmal sevice -+ * -+ * the buffer_from_host uses size data from the ports next available -+ * mmal_buffer and deals with there being no buffer available by -+ * incrementing the underflow for later -+ */ -+static int port_buffer_from_host(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_buffer *buf; -+ unsigned long flags = 0; -+ -+ if (!port->enabled) -+ return -EINVAL; -+ -+ /* peek buffer from queue */ -+ spin_lock_irqsave(&port->slock, flags); -+ if (list_empty(&port->buffers)) { -+ port->buffer_underflow++; -+ spin_unlock_irqrestore(&port->slock, flags); -+ return -ENOSPC; -+ } -+ -+ buf = list_entry(port->buffers.next, struct mmal_buffer, list); -+ -+ spin_unlock_irqrestore(&port->slock, flags); -+ -+ /* issue buffer to mmal service */ -+ ret = buffer_from_host(instance, port, buf); -+ if (ret) { -+ pr_err("adding buffer header failed\n"); -+ /* todo: how should this be dealt with */ -+ } -+ -+ return ret; -+} -+ -+/* deals with receipt of buffer to host message */ -+static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, u32 msg_len) -+{ -+ struct mmal_msg_context *msg_context; -+ -+ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", -+ instance, msg, msg_len); -+ -+ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { -+ msg_context = msg->u.buffer_from_host.drvbuf.client_context; -+ } else { -+ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); -+ return; -+ } -+ -+ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { -+ /* message reception had an error */ -+ pr_warn("error %d in reply\n", msg->h.status); -+ -+ msg_context->u.bulk.status = msg->h.status; -+ -+ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { -+ /* empty buffer */ -+ if (msg->u.buffer_from_host.buffer_header.flags & -+ MMAL_BUFFER_HEADER_FLAG_EOS) { -+ msg_context->u.bulk.status = -+ dummy_bulk_receive(instance, msg_context); -+ if (msg_context->u.bulk.status == 0) -+ return; /* successful bulk submission, bulk -+ * completion will trigger callback -+ */ -+ } else { -+ /* do callback with empty buffer - not EOS though */ -+ msg_context->u.bulk.status = 0; -+ msg_context->u.bulk.buffer_used = 0; -+ } -+ } else if (msg->u.buffer_from_host.payload_in_message == 0) { -+ /* data is not in message, queue a bulk receive */ -+ msg_context->u.bulk.status = -+ bulk_receive(instance, msg, msg_context); -+ if (msg_context->u.bulk.status == 0) -+ return; /* successful bulk submission, bulk -+ * completion will trigger callback -+ */ -+ -+ /* failed to submit buffer, this will end badly */ -+ pr_err("error %d on bulk submission\n", -+ msg_context->u.bulk.status); -+ -+ } else if (msg->u.buffer_from_host.payload_in_message <= -+ MMAL_VC_SHORT_DATA) { -+ /* data payload within message */ -+ msg_context->u.bulk.status = inline_receive(instance, msg, -+ msg_context); -+ } else { -+ pr_err("message with invalid short payload\n"); -+ -+ /* signal error */ -+ msg_context->u.bulk.status = -EINVAL; -+ msg_context->u.bulk.buffer_used = -+ msg->u.buffer_from_host.payload_in_message; -+ } -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(instance, msg_context->u.bulk.port); -+ -+ /* schedule the port callback */ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+static void bulk_receive_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ /* bulk receive operation complete */ -+ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port); -+ -+ msg_context->u.bulk.status = 0; -+ -+ /* schedule the port callback */ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+static void bulk_abort_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); -+ -+ /* bulk receive operation complete */ -+ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port); -+ -+ msg_context->u.bulk.status = -EINTR; -+ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+/* incoming event service callback */ -+static void service_callback(void *param, -+ const VCHI_CALLBACK_REASON_T reason, -+ void *bulk_ctx) -+{ -+ struct vchiq_mmal_instance *instance = param; -+ int status; -+ u32 msg_len; -+ struct mmal_msg *msg; -+ VCHI_HELD_MSG_T msg_handle; -+ -+ if (!instance) { -+ pr_err("Message callback passed NULL instance\n"); -+ return; -+ } -+ -+ switch (reason) { -+ case VCHI_CALLBACK_MSG_AVAILABLE: -+ status = vchi_msg_hold(instance->handle, (void **)&msg, -+ &msg_len, VCHI_FLAGS_NONE, &msg_handle); -+ if (status) { -+ pr_err("Unable to dequeue a message (%d)\n", status); ++ spin_lock(&service_spinlock); ++ while (idx < state->unused_service) { ++ VCHIQ_SERVICE_T *srv = state->services[idx++]; ++ if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (srv->instance == instance)) { ++ service = srv; ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; + break; + } ++ } ++ spin_unlock(&service_spinlock); + -+ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ *pidx = idx; + -+ /* handling is different for buffer messages */ -+ switch (msg->h.type) { ++ return service; ++} + -+ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: -+ vchi_held_msg_release(&msg_handle); -+ break; ++void ++lock_service(VCHIQ_SERVICE_T *service) ++{ ++ spin_lock(&service_spinlock); ++ BUG_ON(!service || (service->ref_count == 0)); ++ if (service) ++ service->ref_count++; ++ spin_unlock(&service_spinlock); ++} + -+ case MMAL_MSG_TYPE_EVENT_TO_HOST: -+ event_to_host_cb(instance, msg, msg_len); -+ vchi_held_msg_release(&msg_handle); ++void ++unlock_service(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ spin_lock(&service_spinlock); ++ BUG_ON(!service || (service->ref_count == 0)); ++ if (service && service->ref_count) { ++ service->ref_count--; ++ if (!service->ref_count) { ++ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); ++ state->services[service->localport] = NULL; ++ } else ++ service = NULL; ++ } ++ spin_unlock(&service_spinlock); + -+ break; ++ if (service && service->userdata_term) ++ service->userdata_term(service->base.userdata); + -+ case MMAL_MSG_TYPE_BUFFER_TO_HOST: -+ buffer_to_host_cb(instance, msg, msg_len); -+ vchi_held_msg_release(&msg_handle); -+ break; ++ kfree(service); ++} + -+ default: -+ /* messages dependant on header context to complete */ ++int ++vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ int id; + -+ /* todo: the msg.context really ought to be sanity -+ * checked before we just use it, afaict it comes back -+ * and is used raw from the videocore. Perhaps it -+ * should be verified the address lies in the kernel -+ * address space. -+ */ -+ if (msg->h.context == NULL) { -+ pr_err("received message context was null!\n"); -+ vchi_held_msg_release(&msg_handle); -+ break; ++ id = service ? service->client_id : 0; ++ if (service) ++ unlock_service(service); ++ ++ return id; ++} ++ ++void * ++vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = handle_to_service(handle); ++ ++ return service ? service->base.userdata : NULL; ++} ++ ++int ++vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = handle_to_service(handle); ++ ++ return service ? service->base.fourcc : 0; ++} ++ ++static void ++mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ VCHIQ_SERVICE_QUOTA_T *service_quota; ++ ++ service->closing = 1; ++ ++ /* Synchronise with other threads. */ ++ mutex_lock(&state->recycle_mutex); ++ mutex_unlock(&state->recycle_mutex); ++ if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) { ++ /* If we're pausing then the slot_mutex is held until resume ++ * by the slot handler. Therefore don't try to acquire this ++ * mutex if we're the slot handler and in the pause sent state. ++ * We don't need to in this case anyway. */ ++ mutex_lock(&state->slot_mutex); ++ mutex_unlock(&state->slot_mutex); ++ } ++ ++ /* Unblock any sending thread. */ ++ service_quota = &state->service_quotas[service->localport]; ++ up(&service_quota->quota_event); ++} ++ ++static void ++mark_service_closing(VCHIQ_SERVICE_T *service) ++{ ++ mark_service_closing_internal(service, 0); ++} ++ ++static inline VCHIQ_STATUS_T ++make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, void *bulk_userdata) ++{ ++ VCHIQ_STATUS_T status; ++ vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %x, %x)", ++ service->state->id, service->localport, reason_names[reason], ++ (unsigned int)header, (unsigned int)bulk_userdata); ++ status = service->base.callback(reason, header, service->handle, ++ bulk_userdata); ++ if (status == VCHIQ_ERROR) { ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: ignoring ERROR from callback to service %x", ++ service->state->id, service->handle); ++ status = VCHIQ_SUCCESS; ++ } ++ return status; ++} ++ ++inline void ++vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) ++{ ++ VCHIQ_CONNSTATE_T oldstate = state->conn_state; ++ vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id, ++ conn_state_names[oldstate], ++ conn_state_names[newstate]); ++ state->conn_state = newstate; ++ vchiq_platform_conn_state_changed(state, oldstate, newstate); ++} ++ ++static inline void ++remote_event_create(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ /* Don't clear the 'fired' flag because it may already have been set ++ ** by the other side. */ ++ sema_init(event->event, 0); ++} ++ ++static inline void ++remote_event_destroy(REMOTE_EVENT_T *event) ++{ ++ (void)event; ++} ++ ++static inline int ++remote_event_wait(REMOTE_EVENT_T *event) ++{ ++ if (!event->fired) { ++ event->armed = 1; ++ dsb(); ++ if (!event->fired) { ++ if (down_interruptible(event->event) != 0) { ++ event->armed = 0; ++ return 0; ++ } ++ } ++ event->armed = 0; ++ wmb(); ++ } ++ ++ event->fired = 0; ++ return 1; ++} ++ ++static inline void ++remote_event_signal_local(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ up(event->event); ++} ++ ++static inline void ++remote_event_poll(REMOTE_EVENT_T *event) ++{ ++ if (event->fired && event->armed) ++ remote_event_signal_local(event); ++} ++ ++void ++remote_event_pollall(VCHIQ_STATE_T *state) ++{ ++ remote_event_poll(&state->local->sync_trigger); ++ remote_event_poll(&state->local->sync_release); ++ remote_event_poll(&state->local->trigger); ++ remote_event_poll(&state->local->recycle); ++} ++ ++/* Round up message sizes so that any space at the end of a slot is always big ++** enough for a header. This relies on header size being a power of two, which ++** has been verified earlier by a static assertion. */ ++ ++static inline unsigned int ++calc_stride(unsigned int size) ++{ ++ /* Allow room for the header */ ++ size += sizeof(VCHIQ_HEADER_T); ++ ++ /* Round up */ ++ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) ++ - 1); ++} ++ ++/* Called by the slot handler thread */ ++static VCHIQ_SERVICE_T * ++get_listening_service(VCHIQ_STATE_T *state, int fourcc) ++{ ++ int i; ++ ++ WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && ++ (service->public_fourcc == fourcc) && ++ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && ++ (service->remoteport == VCHIQ_PORT_FREE)))) { ++ lock_service(service); ++ return service; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Called by the slot handler thread */ ++static VCHIQ_SERVICE_T * ++get_connected_service(VCHIQ_STATE_T *state, unsigned int port) ++{ ++ int i; ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) ++ && (service->remoteport == port)) { ++ lock_service(service); ++ return service; ++ } ++ } ++ return NULL; ++} ++ ++inline void ++request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type) ++{ ++ uint32_t value; ++ ++ if (service) { ++ do { ++ value = atomic_read(&service->poll_flags); ++ } while (atomic_cmpxchg(&service->poll_flags, value, ++ value | (1 << poll_type)) != value); ++ ++ do { ++ value = atomic_read(&state->poll_services[ ++ service->localport>>5]); ++ } while (atomic_cmpxchg( ++ &state->poll_services[service->localport>>5], ++ value, value | (1 << (service->localport & 0x1f))) ++ != value); ++ } ++ ++ state->poll_needed = 1; ++ wmb(); ++ ++ /* ... and ensure the slot handler runs. */ ++ remote_event_signal_local(&state->local->trigger); ++} ++ ++/* Called from queue_message, by the slot handler and application threads, ++** with slot_mutex held */ ++static VCHIQ_HEADER_T * ++reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ int tx_pos = state->local_tx_pos; ++ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK); ++ ++ if (space > slot_space) { ++ VCHIQ_HEADER_T *header; ++ /* Fill the remaining space with padding */ ++ WARN_ON(state->tx_data == NULL); ++ header = (VCHIQ_HEADER_T *) ++ (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++ header->msgid = VCHIQ_MSGID_PADDING; ++ header->size = slot_space - sizeof(VCHIQ_HEADER_T); ++ ++ tx_pos += slot_space; ++ } ++ ++ /* If necessary, get the next slot. */ ++ if ((tx_pos & VCHIQ_SLOT_MASK) == 0) { ++ int slot_index; ++ ++ /* If there is no free slot... */ ++ ++ if (down_trylock(&state->slot_available_event) != 0) { ++ /* ...wait for one. */ ++ ++ VCHIQ_STATS_INC(state, slot_stalls); ++ ++ /* But first, flush through the last slot. */ ++ state->local_tx_pos = tx_pos; ++ local->tx_pos = tx_pos; ++ remote_event_signal(&state->remote->trigger); ++ ++ if (!is_blocking || ++ (down_interruptible( ++ &state->slot_available_event) != 0)) ++ return NULL; /* No space available */ ++ } ++ ++ BUG_ON(tx_pos == ++ (state->slot_queue_available * VCHIQ_SLOT_SIZE)); ++ ++ slot_index = local->slot_queue[ ++ SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ state->tx_data = ++ (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ } ++ ++ state->local_tx_pos = tx_pos + space; ++ ++ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++} ++ ++/* Called by the recycle thread. */ ++static void ++process_free_queue(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ int slot_queue_available; ++ ++ /* Use a read memory barrier to ensure that any state that may have ++ ** been modified by another thread is not masked by stale prefetched ++ ** values. */ ++ rmb(); ++ ++ /* Find slots which have been freed by the other side, and return them ++ ** to the available queue. */ ++ slot_queue_available = state->slot_queue_available; ++ ++ while (slot_queue_available != local->slot_queue_recycle) { ++ unsigned int pos; ++ int slot_index = local->slot_queue[slot_queue_available++ & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ int data_found = 0; ++ ++ vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%x %x %x", ++ state->id, slot_index, (unsigned int)data, ++ local->slot_queue_recycle, slot_queue_available); ++ ++ /* Initialise the bitmask for services which have used this ++ ** slot */ ++ BITSET_ZERO(service_found); ++ ++ pos = 0; ++ ++ while (pos < VCHIQ_SLOT_SIZE) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) { ++ int port = VCHIQ_MSG_SRCPORT(msgid); ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[port]; ++ int count; ++ spin_lock("a_spinlock); ++ count = service_quota->message_use_count; ++ if (count > 0) ++ service_quota->message_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ ++ if (count == service_quota->message_quota) ++ /* Signal the service that it ++ ** has dropped below its quota ++ */ ++ up(&service_quota->quota_event); ++ else if (count == 0) { ++ vchiq_log_error(vchiq_core_log_level, ++ "service %d " ++ "message_use_count=%d " ++ "(header %x, msgid %x, " ++ "header->msgid %x, " ++ "header->size %x)", ++ port, ++ service_quota-> ++ message_use_count, ++ (unsigned int)header, msgid, ++ header->msgid, ++ header->size); ++ WARN(1, "invalid message use count\n"); ++ } ++ if (!BITSET_IS_SET(service_found, port)) { ++ /* Set the found bit for this service */ ++ BITSET_SET(service_found, port); ++ ++ spin_lock("a_spinlock); ++ count = service_quota->slot_use_count; ++ if (count > 0) ++ service_quota->slot_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ ++ if (count > 0) { ++ /* Signal the service in case ++ ** it has dropped below its ++ ** quota */ ++ up(&service_quota->quota_event); ++ vchiq_log_trace( ++ vchiq_core_log_level, ++ "%d: pfq:%d %x@%x - " ++ "slot_use->%d", ++ state->id, port, ++ header->size, ++ (unsigned int)header, ++ count - 1); ++ } else { ++ vchiq_log_error( ++ vchiq_core_log_level, ++ "service %d " ++ "slot_use_count" ++ "=%d (header %x" ++ ", msgid %x, " ++ "header->msgid" ++ " %x, header->" ++ "size %x)", ++ port, count, ++ (unsigned int)header, ++ msgid, ++ header->msgid, ++ header->size); ++ WARN(1, "bad slot use count\n"); ++ } ++ } ++ ++ data_found = 1; + } + -+ /* fill in context values */ -+ msg->h.context->u.sync.msg_handle = msg_handle; -+ msg->h.context->u.sync.msg = msg; -+ msg->h.context->u.sync.msg_len = msg_len; -+ -+ /* todo: should this check (completion_done() -+ * == 1) for no one waiting? or do we need a -+ * flag to tell us the completion has been -+ * interrupted so we can free the message and -+ * its context. This probably also solves the -+ * message arriving after interruption todo -+ * below -+ */ -+ -+ /* complete message so caller knows it happened */ -+ complete(&msg->h.context->u.sync.cmplt); -+ break; ++ pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "pfq - pos %x: header %x, msgid %x, " ++ "header->msgid %x, header->size %x", ++ pos, (unsigned int)header, msgid, ++ header->msgid, header->size); ++ WARN(1, "invalid slot position\n"); ++ } + } + -+ break; -+ -+ case VCHI_CALLBACK_BULK_RECEIVED: -+ bulk_receive_cb(instance, bulk_ctx); -+ break; -+ -+ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: -+ bulk_abort_cb(instance, bulk_ctx); -+ break; -+ -+ case VCHI_CALLBACK_SERVICE_CLOSED: -+ /* TODO: consider if this requires action if received when -+ * driver is not explicitly closing the service -+ */ -+ break; -+ -+ default: -+ pr_err("Received unhandled message reason %d\n", reason); -+ break; -+ } -+} -+ -+static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ unsigned int payload_len, -+ struct mmal_msg **msg_out, -+ VCHI_HELD_MSG_T *msg_handle_out) -+{ -+ struct mmal_msg_context msg_context; -+ int ret; -+ -+ /* payload size must not cause message to exceed max size */ -+ if (payload_len > -+ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { -+ pr_err("payload length %d exceeds max:%d\n", payload_len, -+ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); -+ return -EINVAL; -+ } -+ -+ init_completion(&msg_context.u.sync.cmplt); -+ -+ msg->h.magic = MMAL_MAGIC; -+ msg->h.context = &msg_context; -+ msg->h.status = 0; -+ -+ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), -+ ">>> sync message"); -+ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_msg_queue(instance->handle, -+ msg, -+ sizeof(struct mmal_msg_header) + payload_len, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret) { -+ pr_err("error %d queuing message\n", ret); -+ return ret; -+ } -+ -+ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); -+ if (ret <= 0) { -+ pr_err("error %d waiting for sync completion\n", ret); -+ if (ret == 0) -+ ret = -ETIME; -+ /* todo: what happens if the message arrives after aborting */ -+ return ret; -+ } -+ -+ *msg_out = msg_context.u.sync.msg; -+ *msg_handle_out = msg_context.u.sync.msg_handle; -+ -+ return 0; -+} -+ -+static void dump_port_info(struct vchiq_mmal_port *port) -+{ -+ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); -+ -+ pr_debug("buffer minimum num:%d size:%d align:%d\n", -+ port->minimum_buffer.num, -+ port->minimum_buffer.size, port->minimum_buffer.alignment); -+ -+ pr_debug("buffer recommended num:%d size:%d align:%d\n", -+ port->recommended_buffer.num, -+ port->recommended_buffer.size, -+ port->recommended_buffer.alignment); -+ -+ pr_debug("buffer current values num:%d size:%d align:%d\n", -+ port->current_buffer.num, -+ port->current_buffer.size, port->current_buffer.alignment); -+ -+ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", -+ port->format.type, -+ port->format.encoding, port->format.encoding_variant); -+ -+ pr_debug(" bitrate:%d flags:0x%x\n", -+ port->format.bitrate, port->format.flags); -+ -+ if (port->format.type == MMAL_ES_TYPE_VIDEO) { -+ pr_debug -+ ("es video format: width:%d height:%d colourspace:0x%x\n", -+ port->es.video.width, port->es.video.height, -+ port->es.video.color_space); -+ -+ pr_debug(" : crop xywh %d,%d,%d,%d\n", -+ port->es.video.crop.x, -+ port->es.video.crop.y, -+ port->es.video.crop.width, port->es.video.crop.height); -+ pr_debug(" : framerate %d/%d aspect %d/%d\n", -+ port->es.video.frame_rate.num, -+ port->es.video.frame_rate.den, -+ port->es.video.par.num, port->es.video.par.den); -+ } -+} -+ -+static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) -+{ -+ -+ /* todo do readonly fields need setting at all? */ -+ p->type = port->type; -+ p->index = port->index; -+ p->index_all = 0; -+ p->is_enabled = port->enabled; -+ p->buffer_num_min = port->minimum_buffer.num; -+ p->buffer_size_min = port->minimum_buffer.size; -+ p->buffer_alignment_min = port->minimum_buffer.alignment; -+ p->buffer_num_recommended = port->recommended_buffer.num; -+ p->buffer_size_recommended = port->recommended_buffer.size; -+ -+ /* only three writable fields in a port */ -+ p->buffer_num = port->current_buffer.num; -+ p->buffer_size = port->current_buffer.size; -+ p->userdata = port; -+} -+ -+static int port_info_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ pr_debug("setting port info port %p\n", port); -+ if (!port) -+ return -1; -+ dump_port_info(port); -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; -+ -+ m.u.port_info_set.component_handle = port->component->handle; -+ m.u.port_info_set.port_type = port->type; -+ m.u.port_info_set.port_index = port->index; -+ -+ port_to_mmal_msg(port, &m.u.port_info_set.port); -+ -+ /* elementry stream format setup */ -+ m.u.port_info_set.format.type = port->format.type; -+ m.u.port_info_set.format.encoding = port->format.encoding; -+ m.u.port_info_set.format.encoding_variant = -+ port->format.encoding_variant; -+ m.u.port_info_set.format.bitrate = port->format.bitrate; -+ m.u.port_info_set.format.flags = port->format.flags; -+ -+ memcpy(&m.u.port_info_set.es, &port->es, -+ sizeof(union mmal_es_specific_format)); -+ -+ m.u.port_info_set.format.extradata_size = port->format.extradata_size; -+ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, -+ port->format.extradata_size); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_info_set), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ /* return operation status */ -+ ret = -rmsg->u.port_info_get_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, -+ port->component->handle, port->handle); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+ -+} -+ -+/* use port info get message to retrive port information */ -+static int port_info_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ /* port info time */ -+ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; -+ m.u.port_info_get.component_handle = port->component->handle; -+ m.u.port_info_get.port_type = port->type; -+ m.u.port_info_get.index = port->index; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_info_get), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ /* return operation status */ -+ ret = -rmsg->u.port_info_get_reply.status; -+ if (ret != MMAL_MSG_STATUS_SUCCESS) -+ goto release_msg; -+ -+ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) -+ port->enabled = false; -+ else -+ port->enabled = true; -+ -+ /* copy the values out of the message */ -+ port->handle = rmsg->u.port_info_get_reply.port_handle; -+ -+ /* port type and index cached to use on port info set becuase -+ * it does not use a port handle -+ */ -+ port->type = rmsg->u.port_info_get_reply.port_type; -+ port->index = rmsg->u.port_info_get_reply.port_index; -+ -+ port->minimum_buffer.num = -+ rmsg->u.port_info_get_reply.port.buffer_num_min; -+ port->minimum_buffer.size = -+ rmsg->u.port_info_get_reply.port.buffer_size_min; -+ port->minimum_buffer.alignment = -+ rmsg->u.port_info_get_reply.port.buffer_alignment_min; -+ -+ port->recommended_buffer.alignment = -+ rmsg->u.port_info_get_reply.port.buffer_alignment_min; -+ port->recommended_buffer.num = -+ rmsg->u.port_info_get_reply.port.buffer_num_recommended; -+ -+ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; -+ port->current_buffer.size = -+ rmsg->u.port_info_get_reply.port.buffer_size; -+ -+ /* stream format */ -+ port->format.type = rmsg->u.port_info_get_reply.format.type; -+ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; -+ port->format.encoding_variant = -+ rmsg->u.port_info_get_reply.format.encoding_variant; -+ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; -+ port->format.flags = rmsg->u.port_info_get_reply.format.flags; -+ -+ /* elementry stream format */ -+ memcpy(&port->es, -+ &rmsg->u.port_info_get_reply.es, -+ sizeof(union mmal_es_specific_format)); -+ port->format.es = &port->es; -+ -+ port->format.extradata_size = -+ rmsg->u.port_info_get_reply.format.extradata_size; -+ memcpy(port->format.extradata, -+ rmsg->u.port_info_get_reply.extradata, -+ port->format.extradata_size); -+ -+ pr_debug("received port info\n"); -+ dump_port_info(port); -+ -+release_msg: -+ -+ pr_debug("%s:result:%d component:0x%x port:%d\n", -+ __func__, ret, port->component->handle, port->handle); -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* create comonent on vc */ -+static int create_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component, -+ const char *name) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ /* build component create message */ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; -+ m.u.component_create.client_component = component; -+ strncpy(m.u.component_create.name, name, -+ sizeof(m.u.component_create.name)); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_create), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_create_reply.status; -+ if (ret != MMAL_MSG_STATUS_SUCCESS) -+ goto release_msg; -+ -+ /* a valid component response received */ -+ component->handle = rmsg->u.component_create_reply.component_handle; -+ component->inputs = rmsg->u.component_create_reply.input_num; -+ component->outputs = rmsg->u.component_create_reply.output_num; -+ component->clocks = rmsg->u.component_create_reply.clock_num; -+ -+ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", -+ component->handle, -+ component->inputs, component->outputs, component->clocks); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* destroys a component on vc */ -+static int destroy_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; -+ m.u.component_destroy.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_destroy), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_destroy_reply.status; -+ -+release_msg: -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* enable a component on vc */ -+static int enable_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; -+ m.u.component_enable.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_enable), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_enable_reply.status; -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* disable a component on vc */ -+static int disable_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; -+ m.u.component_disable.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_disable), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_disable_reply.status; -+ -+release_msg: -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* get version of mmal implementation */ -+static int get_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, u32 *minor_out) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_GET_VERSION; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.version), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ *major_out = rmsg->u.version.major; -+ *minor_out = rmsg->u.version.minor; -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* do a port action with a port as a parameter */ -+static int port_action_port(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ enum mmal_msg_port_action_type action_type) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; -+ m.u.port_action_port.component_handle = port->component->handle; -+ m.u.port_action_port.port_handle = port->handle; -+ m.u.port_action_port.action = action_type; -+ -+ port_to_mmal_msg(port, &m.u.port_action_port.port); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_action_port), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_action_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", -+ __func__, -+ ret, port->component->handle, port->handle, -+ port_action_type_names[action_type], action_type); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* do a port action with handles as parameters */ -+static int port_action_handle(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ enum mmal_msg_port_action_type action_type, -+ u32 connect_component_handle, -+ u32 connect_port_handle) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; -+ -+ m.u.port_action_handle.component_handle = port->component->handle; -+ m.u.port_action_handle.port_handle = port->handle; -+ m.u.port_action_handle.action = action_type; -+ -+ m.u.port_action_handle.connect_component_handle = -+ connect_component_handle; -+ m.u.port_action_handle.connect_port_handle = connect_port_handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_action_handle), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_action_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ -+ " connect component:0x%x connect port:%d\n", -+ __func__, -+ ret, port->component->handle, port->handle, -+ port_action_type_names[action_type], -+ action_type, connect_component_handle, connect_port_handle); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+static int port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter_id, void *value, u32 value_size) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; -+ -+ m.u.port_parameter_set.component_handle = port->component->handle; -+ m.u.port_parameter_set.port_handle = port->handle; -+ m.u.port_parameter_set.id = parameter_id; -+ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; -+ memcpy(&m.u.port_parameter_set.value, value, value_size); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ (4 * sizeof(u32)) + value_size, -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_parameter_set_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", -+ __func__, -+ ret, port->component->handle, port->handle, parameter_id); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+static int port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter_id, void *value, u32 *value_size) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; -+ -+ m.u.port_parameter_get.component_handle = port->component->handle; -+ m.u.port_parameter_get.port_handle = port->handle; -+ m.u.port_parameter_get.id = parameter_id; -+ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(struct -+ mmal_msg_port_parameter_get), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { -+ /* got an unexpected message type in reply */ -+ pr_err("Incorrect reply type %d\n", rmsg->h.type); -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_parameter_get_reply.status; -+ if (ret) { -+ /* Copy only as much as we have space for -+ * but report true size of parameter -+ */ -+ memcpy(value, &rmsg->u.port_parameter_get_reply.value, -+ *value_size); -+ *value_size = rmsg->u.port_parameter_get_reply.size; -+ } else -+ memcpy(value, &rmsg->u.port_parameter_get_reply.value, -+ rmsg->u.port_parameter_get_reply.size); -+ -+ pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, -+ ret, port->component->handle, port->handle, parameter_id); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* disables a port and drains buffers from it */ -+static int port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct list_head *q, *buf_head; -+ unsigned long flags = 0; -+ -+ if (!port->enabled) -+ return 0; -+ -+ port->enabled = false; -+ -+ ret = port_action_port(instance, port, -+ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); -+ if (ret == 0) { -+ -+ /* drain all queued buffers on port */ -+ spin_lock_irqsave(&port->slock, flags); -+ -+ list_for_each_safe(buf_head, q, &port->buffers) { -+ struct mmal_buffer *mmalbuf; -+ mmalbuf = list_entry(buf_head, struct mmal_buffer, -+ list); -+ list_del(buf_head); -+ if (port->buffer_cb) -+ port->buffer_cb(instance, -+ port, 0, mmalbuf, 0, 0, -+ MMAL_TIME_UNKNOWN, -+ MMAL_TIME_UNKNOWN); ++ if (data_found) { ++ int count; ++ spin_lock("a_spinlock); ++ count = state->data_use_count; ++ if (count > 0) ++ state->data_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ if (count == state->data_quota) ++ up(&state->data_quota_event); + } + -+ spin_unlock_irqrestore(&port->slock, flags); -+ -+ ret = port_info_get(instance, port); ++ state->slot_queue_available = slot_queue_available; ++ up(&state->slot_available_event); + } -+ -+ return ret; +} + -+/* enable a port */ -+static int port_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) ++/* Called by the slot handler and application threads */ ++static VCHIQ_STATUS_T ++queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ int msgid, const VCHIQ_ELEMENT_T *elements, ++ int count, int size, int flags) +{ -+ unsigned int hdr_count; -+ struct list_head *buf_head; -+ int ret; ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; ++ VCHIQ_HEADER_T *header; ++ int type = VCHIQ_MSG_TYPE(msgid); + -+ if (port->enabled) -+ return 0; ++ unsigned int stride; + -+ /* ensure there are enough buffers queued to cover the buffer headers */ -+ if (port->buffer_cb != NULL) { -+ hdr_count = 0; -+ list_for_each(buf_head, &port->buffers) { -+ hdr_count++; ++ local = state->local; ++ ++ stride = calc_stride(size); ++ ++ WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); ++ ++ if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && ++ (mutex_lock_interruptible(&state->slot_mutex) != 0)) ++ return VCHIQ_RETRY; ++ ++ if (type == VCHIQ_MSG_DATA) { ++ int tx_end_index; ++ ++ BUG_ON(!service); ++ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); ++ ++ if (service->closing) { ++ /* The service has been closed */ ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_ERROR; + } -+ if (hdr_count < port->current_buffer.num) -+ return -ENOSPC; -+ } + -+ ret = port_action_port(instance, port, -+ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); -+ if (ret) -+ goto done; ++ service_quota = &state->service_quotas[service->localport]; + -+ port->enabled = true; ++ spin_lock("a_spinlock); + -+ if (port->buffer_cb) { -+ /* send buffer headers to videocore */ -+ hdr_count = 1; -+ list_for_each(buf_head, &port->buffers) { -+ struct mmal_buffer *mmalbuf; -+ mmalbuf = list_entry(buf_head, struct mmal_buffer, -+ list); -+ ret = buffer_from_host(instance, port, mmalbuf); -+ if (ret) -+ goto done; ++ /* Ensure this service doesn't use more than its quota of ++ ** messages or slots */ ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); + -+ hdr_count++; -+ if (hdr_count > port->current_buffer.num) ++ /* Ensure data messages don't use more than their quota of ++ ** slots */ ++ while ((tx_end_index != state->previous_data_index) && ++ (state->data_use_count == state->data_quota)) { ++ VCHIQ_STATS_INC(state, data_stalls); ++ spin_unlock("a_spinlock); ++ mutex_unlock(&state->slot_mutex); ++ ++ if (down_interruptible(&state->data_quota_event) ++ != 0) ++ return VCHIQ_RETRY; ++ ++ mutex_lock(&state->slot_mutex); ++ spin_lock("a_spinlock); ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); ++ if ((tx_end_index == state->previous_data_index) || ++ (state->data_use_count < state->data_quota)) { ++ /* Pass the signal on to other waiters */ ++ up(&state->data_quota_event); + break; -+ } -+ } -+ -+ ret = port_info_get(instance, port); -+ -+done: -+ return ret; -+} -+ -+/* ------------------------------------------------------------------ -+ * Exported API -+ *------------------------------------------------------------------*/ -+ -+int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_info_set(instance, port); -+ if (ret) -+ goto release_unlock; -+ -+ /* read what has actually been set */ -+ ret = port_info_get(instance, port); -+ -+release_unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+ -+} -+ -+int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, void *value, u32 value_size) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_parameter_set(instance, port, parameter, value, value_size); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, void *value, u32 *value_size) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_parameter_get(instance, port, parameter, value, value_size); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* enable a port -+ * -+ * enables a port and queues buffers for satisfying callbacks if we -+ * provide a callback handler -+ */ -+int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ vchiq_mmal_buffer_cb buffer_cb) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ /* already enabled - noop */ -+ if (port->enabled) { -+ ret = 0; -+ goto unlock; -+ } -+ -+ port->buffer_cb = buffer_cb; -+ -+ ret = port_enable(instance, port); -+ -+unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (!port->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; -+ } -+ -+ ret = port_disable(instance, port); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* ports will be connected in a tunneled manner so data buffers -+ * are not handled by client. -+ */ -+int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *src, -+ struct vchiq_mmal_port *dst) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ /* disconnect ports if connected */ -+ if (src->connected != NULL) { -+ ret = port_disable(instance, src); -+ if (ret) { -+ pr_err("failed disabling src port(%d)\n", ret); -+ goto release_unlock; ++ } + } + -+ /* do not need to disable the destination port as they -+ * are connected and it is done automatically -+ */ -+ -+ ret = port_action_handle(instance, src, -+ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, -+ src->connected->component->handle, -+ src->connected->handle); -+ if (ret < 0) { -+ pr_err("failed disconnecting src port\n"); -+ goto release_unlock; ++ while ((service_quota->message_use_count == ++ service_quota->message_quota) || ++ ((tx_end_index != service_quota->previous_tx_index) && ++ (service_quota->slot_use_count == ++ service_quota->slot_quota))) { ++ spin_unlock("a_spinlock); ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: qm:%d %s,%x - quota stall " ++ "(msg %d, slot %d)", ++ state->id, service->localport, ++ msg_type_str(type), size, ++ service_quota->message_use_count, ++ service_quota->slot_use_count); ++ VCHIQ_SERVICE_STATS_INC(service, quota_stalls); ++ mutex_unlock(&state->slot_mutex); ++ if (down_interruptible(&service_quota->quota_event) ++ != 0) ++ return VCHIQ_RETRY; ++ if (service->closing) ++ return VCHIQ_ERROR; ++ if (mutex_lock_interruptible(&state->slot_mutex) != 0) ++ return VCHIQ_RETRY; ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { ++ /* The service has been closed */ ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_ERROR; ++ } ++ spin_lock("a_spinlock); ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); + } -+ src->connected->enabled = false; -+ src->connected = NULL; ++ ++ spin_unlock("a_spinlock); + } + -+ if (dst == NULL) { -+ /* do not make new connection */ -+ ret = 0; -+ pr_debug("not making new connection\n"); -+ goto release_unlock; ++ header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); ++ ++ if (!header) { ++ if (service) ++ VCHIQ_SERVICE_STATS_INC(service, slot_stalls); ++ /* In the event of a failure, return the mutex to the ++ state it was in */ ++ if (!(flags & QMFLAGS_NO_MUTEX_LOCK)) ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_RETRY; + } + -+ /* copy src port format to dst */ -+ dst->format.encoding = src->format.encoding; -+ dst->es.video.width = src->es.video.width; -+ dst->es.video.height = src->es.video.height; -+ dst->es.video.crop.x = src->es.video.crop.x; -+ dst->es.video.crop.y = src->es.video.crop.y; -+ dst->es.video.crop.width = src->es.video.crop.width; -+ dst->es.video.crop.height = src->es.video.crop.height; -+ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; -+ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ if (type == VCHIQ_MSG_DATA) { ++ int i, pos; ++ int tx_end_index; ++ int slot_use_count; + -+ /* set new format */ -+ ret = port_info_set(instance, dst); -+ if (ret) { -+ pr_debug("setting port info failed\n"); -+ goto release_unlock; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: qm %s@%x,%x (%d->%d)", ++ state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ ++ BUG_ON(!service); ++ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); ++ ++ for (i = 0, pos = 0; i < (unsigned int)count; ++ pos += elements[i++].size) ++ if (elements[i].size) { ++ if (vchiq_copy_from_user ++ (header->data + pos, elements[i].data, ++ (size_t) elements[i].size) != ++ VCHIQ_SUCCESS) { ++ mutex_unlock(&state->slot_mutex); ++ VCHIQ_SERVICE_STATS_INC(service, ++ error_count); ++ return VCHIQ_ERROR; ++ } ++ if (i == 0) { ++ if (SRVTRACE_ENABLED(service, ++ VCHIQ_LOG_INFO)) ++ vchiq_log_dump_mem("Sent", 0, ++ header->data + pos, ++ min(64u, ++ elements[0].size)); ++ } ++ } ++ ++ spin_lock("a_spinlock); ++ service_quota->message_use_count++; ++ ++ tx_end_index = ++ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1); ++ ++ /* If this transmission can't fit in the last slot used by any ++ ** service, the data_use_count must be increased. */ ++ if (tx_end_index != state->previous_data_index) { ++ state->previous_data_index = tx_end_index; ++ state->data_use_count++; ++ } ++ ++ /* If this isn't the same slot last used by this service, ++ ** the service's slot_use_count must be increased. */ ++ if (tx_end_index != service_quota->previous_tx_index) { ++ service_quota->previous_tx_index = tx_end_index; ++ slot_use_count = ++service_quota->slot_use_count; ++ } else { ++ slot_use_count = 0; ++ } ++ ++ spin_unlock("a_spinlock); ++ ++ if (slot_use_count) ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: qm:%d %s,%x - slot_use->%d (hdr %p)", ++ state->id, service->localport, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, ++ slot_use_count, header); ++ ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); ++ } else { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: qm %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ if (size != 0) { ++ WARN_ON(!((count == 1) && (size == elements[0].size))); ++ memcpy(header->data, elements[0].data, ++ elements[0].size); ++ } ++ VCHIQ_STATS_INC(state, ctrl_tx_count); + } + -+ /* read what has actually been set */ -+ ret = port_info_get(instance, dst); -+ if (ret) { -+ pr_debug("read back port info failed\n"); -+ goto release_unlock; ++ header->msgid = msgid; ++ header->size = size; ++ ++ { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ VCHIQ_MSG_TYPE(msgid), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid), ++ size); + } + -+ /* connect two ports together */ -+ ret = port_action_handle(instance, src, -+ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, -+ dst->component->handle, dst->handle); -+ if (ret < 0) { -+ pr_debug("connecting port %d:%d to %d:%d failed\n", -+ src->component->handle, src->handle, -+ dst->component->handle, dst->handle); -+ goto release_unlock; -+ } -+ src->connected = dst; ++ /* Make sure the new header is visible to the peer. */ ++ wmb(); + -+release_unlock: ++ /* Make the new tx_pos visible to the peer. */ ++ local->tx_pos = state->local_tx_pos; ++ wmb(); + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (service && (type == VCHIQ_MSG_CLOSE)) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); + -+ return ret; ++ if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) ++ mutex_unlock(&state->slot_mutex); ++ ++ remote_event_signal(&state->remote->trigger); ++ ++ return VCHIQ_SUCCESS; +} + -+int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ struct mmal_buffer *buffer) ++/* Called by the slot handler and application threads */ ++static VCHIQ_STATUS_T ++queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ int msgid, const VCHIQ_ELEMENT_T *elements, ++ int count, int size, int is_blocking) +{ -+ unsigned long flags = 0; ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_HEADER_T *header; + -+ spin_lock_irqsave(&port->slock, flags); -+ list_add_tail(&buffer->list, &port->buffers); -+ spin_unlock_irqrestore(&port->slock, flags); ++ local = state->local; + -+ /* the port previously underflowed because it was missing a -+ * mmal_buffer which has just been added, submit that buffer -+ * to the mmal service. -+ */ -+ if (port->buffer_underflow) { -+ port_buffer_from_host(instance, port); -+ port->buffer_underflow--; ++ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) && ++ (mutex_lock_interruptible(&state->sync_mutex) != 0)) ++ return VCHIQ_RETRY; ++ ++ remote_event_wait(&local->sync_release); ++ ++ rmb(); ++ ++ header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ local->slot_sync); ++ ++ { ++ int oldmsgid = header->msgid; ++ if (oldmsgid != VCHIQ_MSGID_PADDING) ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: qms - msgid %x, not PADDING", ++ state->id, oldmsgid); + } + -+ return 0; ++ if (service) { ++ int i, pos; ++ ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: qms %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ ++ for (i = 0, pos = 0; i < (unsigned int)count; ++ pos += elements[i++].size) ++ if (elements[i].size) { ++ if (vchiq_copy_from_user ++ (header->data + pos, elements[i].data, ++ (size_t) elements[i].size) != ++ VCHIQ_SUCCESS) { ++ mutex_unlock(&state->sync_mutex); ++ VCHIQ_SERVICE_STATS_INC(service, ++ error_count); ++ return VCHIQ_ERROR; ++ } ++ if (i == 0) { ++ if (vchiq_sync_log_level >= ++ VCHIQ_LOG_TRACE) ++ vchiq_log_dump_mem("Sent Sync", ++ 0, header->data + pos, ++ min(64u, ++ elements[0].size)); ++ } ++ } ++ ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); ++ } else { ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: qms %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ if (size != 0) { ++ WARN_ON(!((count == 1) && (size == elements[0].size))); ++ memcpy(header->data, elements[0].data, ++ elements[0].size); ++ } ++ VCHIQ_STATS_INC(state, ctrl_tx_count); ++ } ++ ++ header->size = size; ++ header->msgid = msgid; ++ ++ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ ++ vchiq_log_trace(vchiq_sync_log_level, ++ "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ VCHIQ_MSG_TYPE(msgid), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid), ++ size); ++ } ++ ++ /* Make sure the new header is visible to the peer. */ ++ wmb(); ++ ++ remote_event_signal(&state->remote->sync_trigger); ++ ++ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) ++ mutex_unlock(&state->sync_mutex); ++ ++ return VCHIQ_SUCCESS; +} + -+/* Initialise a mmal component and its ports -+ * -+ */ -+int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, -+ const char *name, -+ struct vchiq_mmal_component **component_out) ++static inline void ++claim_slot(VCHIQ_SLOT_INFO_T *slot) +{ -+ int ret; -+ int idx; /* port index */ -+ struct vchiq_mmal_component *component; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { -+ ret = -EINVAL; /* todo is this correct error? */ -+ goto unlock; -+ } -+ -+ component = &instance->component[instance->component_idx]; -+ -+ ret = create_component(instance, component, name); -+ if (ret < 0) -+ goto unlock; -+ -+ /* ports info needs gathering */ -+ component->control.type = MMAL_PORT_TYPE_CONTROL; -+ component->control.index = 0; -+ component->control.component = component; -+ spin_lock_init(&component->control.slock); -+ INIT_LIST_HEAD(&component->control.buffers); -+ ret = port_info_get(instance, &component->control); -+ if (ret < 0) -+ goto release_component; -+ -+ for (idx = 0; idx < component->inputs; idx++) { -+ component->input[idx].type = MMAL_PORT_TYPE_INPUT; -+ component->input[idx].index = idx; -+ component->input[idx].component = component; -+ spin_lock_init(&component->input[idx].slock); -+ INIT_LIST_HEAD(&component->input[idx].buffers); -+ ret = port_info_get(instance, &component->input[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ for (idx = 0; idx < component->outputs; idx++) { -+ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; -+ component->output[idx].index = idx; -+ component->output[idx].component = component; -+ spin_lock_init(&component->output[idx].slock); -+ INIT_LIST_HEAD(&component->output[idx].buffers); -+ ret = port_info_get(instance, &component->output[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ for (idx = 0; idx < component->clocks; idx++) { -+ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; -+ component->clock[idx].index = idx; -+ component->clock[idx].component = component; -+ spin_lock_init(&component->clock[idx].slock); -+ INIT_LIST_HEAD(&component->clock[idx].buffers); -+ ret = port_info_get(instance, &component->clock[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ instance->component_idx++; -+ -+ *component_out = component; -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return 0; -+ -+release_component: -+ destroy_component(instance, component); -+unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; ++ slot->use_count++; +} + -+/* -+ * cause a mmal component to be destroyed -+ */ -+int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) ++static void ++release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info, ++ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service) +{ -+ int ret; ++ int release_count; + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; ++ mutex_lock(&state->recycle_mutex); + -+ if (component->enabled) -+ ret = disable_component(instance, component); ++ if (header) { ++ int msgid = header->msgid; ++ if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) || ++ (service && service->closing)) { ++ mutex_unlock(&state->recycle_mutex); ++ return; ++ } + -+ ret = destroy_component(instance, component); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* -+ * cause a mmal component to be enabled -+ */ -+int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (component->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; ++ /* Rewrite the message header to prevent a double ++ ** release */ ++ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; + } + -+ ret = enable_component(instance, component); -+ if (ret == 0) -+ component->enabled = true; ++ release_count = slot_info->release_count; ++ slot_info->release_count = ++release_count; + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (release_count == slot_info->use_count) { ++ int slot_queue_recycle; ++ /* Add to the freed queue */ + -+ return ret; -+} ++ /* A read barrier is necessary here to prevent speculative ++ ** fetches of remote->slot_queue_recycle from overtaking the ++ ** mutex. */ ++ rmb(); + -+/* -+ * cause a mmal component to be enabled -+ */ -+int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; ++ slot_queue_recycle = state->remote->slot_queue_recycle; ++ state->remote->slot_queue[slot_queue_recycle & ++ VCHIQ_SLOT_QUEUE_MASK] = ++ SLOT_INDEX_FROM_INFO(state, slot_info); ++ state->remote->slot_queue_recycle = slot_queue_recycle + 1; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: release_slot %d - recycle->%x", ++ state->id, SLOT_INDEX_FROM_INFO(state, slot_info), ++ state->remote->slot_queue_recycle); + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (!component->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; ++ /* A write barrier is necessary, but remote_event_signal ++ ** contains one. */ ++ remote_event_signal(&state->remote->recycle); + } + -+ ret = disable_component(instance, component); -+ if (ret == 0) -+ component->enabled = false; -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; ++ mutex_unlock(&state->recycle_mutex); +} + -+int vchiq_mmal_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, u32 *minor_out) ++/* Called by the slot handler - don't hold the bulk mutex */ ++static VCHIQ_STATUS_T ++notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue, ++ int retry_poll) +{ -+ int ret; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: nb:%d %cx - p=%x rn=%x r=%x", ++ service->state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->process, queue->remote_notify, queue->remove); + -+ ret = get_version(instance, major_out, minor_out); ++ if (service->state->is_master) { ++ while (queue->remote_notify != queue->process) { ++ VCHIQ_BULK_T *bulk = ++ &queue->bulks[BULK_INDEX(queue->remote_notify)]; ++ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE; ++ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, ++ service->remoteport); ++ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 }; ++ /* Only reply to non-dummy bulk requests */ ++ if (bulk->remote_data) { ++ status = queue_message(service->state, NULL, ++ msgid, &element, 1, 4, 0); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ queue->remote_notify++; ++ } ++ } else { ++ queue->remote_notify = queue->process; ++ } + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (status == VCHIQ_SUCCESS) { ++ while (queue->remove != queue->remote_notify) { ++ VCHIQ_BULK_T *bulk = ++ &queue->bulks[BULK_INDEX(queue->remove)]; + -+ return ret; -+} ++ /* Only generate callbacks for non-dummy bulk ++ ** requests, and non-terminated services */ ++ if (bulk->data && service->instance) { ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { ++ if (bulk->dir == VCHIQ_BULK_TRANSMIT) { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ++ bulk_tx_bytes, ++ bulk->actual); ++ } else { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ++ bulk_rx_bytes, ++ bulk->actual); ++ } ++ } else { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_aborted_count); ++ } ++ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { ++ struct bulk_waiter *waiter; ++ spin_lock(&bulk_waiter_spinlock); ++ waiter = bulk->userdata; ++ if (waiter) { ++ waiter->actual = bulk->actual; ++ up(&waiter->event); ++ } ++ spin_unlock(&bulk_waiter_spinlock); ++ } else if (bulk->mode == ++ VCHIQ_BULK_MODE_CALLBACK) { ++ VCHIQ_REASON_T reason = (bulk->dir == ++ VCHIQ_BULK_TRANSMIT) ? ++ ((bulk->actual == ++ VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_TRANSMIT_ABORTED : ++ VCHIQ_BULK_TRANSMIT_DONE) : ++ ((bulk->actual == ++ VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_RECEIVE_ABORTED : ++ VCHIQ_BULK_RECEIVE_DONE); ++ status = make_service_callback(service, ++ reason, NULL, bulk->userdata); ++ if (status == VCHIQ_RETRY) ++ break; ++ } ++ } + -+int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) -+{ -+ int status = 0; ++ queue->remove++; ++ up(&service->bulk_remove_event); ++ } ++ if (!retry_poll) ++ status = VCHIQ_SUCCESS; ++ } + -+ if (instance == NULL) -+ return -EINVAL; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ vchi_service_use(instance->handle); -+ -+ status = vchi_service_close(instance->handle); -+ if (status != 0) -+ pr_err("mmal-vchiq: VCHIQ close failed"); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ vfree(instance->bulk_scratch); -+ -+ kfree(instance); ++ if (status == VCHIQ_RETRY) ++ request_poll(service->state, service, ++ (queue == &service->bulk_tx) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); + + return status; +} + -+int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++/* Called by the slot handler thread */ ++static void ++poll_services(VCHIQ_STATE_T *state) +{ -+ int status; -+ struct vchiq_mmal_instance *instance; -+ static VCHI_CONNECTION_T *vchi_connection; -+ static VCHI_INSTANCE_T vchi_instance; -+ SERVICE_CREATION_T params = { -+ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), -+ VC_MMAL_SERVER_NAME, -+ vchi_connection, -+ 0, /* rx fifo size (unused) */ -+ 0, /* tx fifo size (unused) */ -+ service_callback, -+ NULL, /* service callback parameter */ -+ 1, /* unaligned bulk receives */ -+ 1, /* unaligned bulk transmits */ -+ 0 /* want crc check on bulk transfers */ -+ }; ++ int group, i; + -+ /* compile time checks to ensure structure size as they are -+ * directly (de)serialised from memory. -+ */ ++ for (group = 0; group < BITSET_SIZE(state->unused_service); group++) { ++ uint32_t flags; ++ flags = atomic_xchg(&state->poll_services[group], 0); ++ for (i = 0; flags; i++) { ++ if (flags & (1 << i)) { ++ VCHIQ_SERVICE_T *service = ++ find_service_by_port(state, ++ (group<<5) + i); ++ uint32_t service_flags; ++ flags &= ~(1 << i); ++ if (!service) ++ continue; ++ service_flags = ++ atomic_xchg(&service->poll_flags, 0); ++ if (service_flags & ++ (1 << VCHIQ_POLL_REMOVE)) { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: ps - remove %d<->%d", ++ state->id, service->localport, ++ service->remoteport); + -+ /* ensure the header structure has packed to the correct size */ -+ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ /* Make it look like a client, because ++ it must be removed and not left in ++ the LISTENING state. */ ++ service->public_fourcc = ++ VCHIQ_FOURCC_INVALID; + -+ /* ensure message structure does not exceed maximum length */ -+ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); -+ -+ /* mmal port struct is correct size */ -+ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); -+ -+ /* create a vchi instance */ -+ status = vchi_initialise(&vchi_instance); -+ if (status) { -+ pr_err("Failed to initialise VCHI instance (status=%d)\n", -+ status); -+ return -EIO; -+ } -+ -+ status = vchi_connect(NULL, 0, vchi_instance); -+ if (status) { -+ pr_err("Failed to connect VCHI instance (status=%d)\n", status); -+ return -EIO; -+ } -+ -+ instance = kmalloc(sizeof(*instance), GFP_KERNEL); -+ memset(instance, 0, sizeof(*instance)); -+ -+ mutex_init(&instance->vchiq_mutex); -+ mutex_init(&instance->bulk_mutex); -+ -+ instance->bulk_scratch = vmalloc(PAGE_SIZE); -+ -+ params.callback_param = instance; -+ -+ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); -+ if (status) { -+ pr_err("Failed to open VCHI service connection (status=%d)\n", -+ status); -+ goto err_close_services; -+ } -+ -+ vchi_service_release(instance->handle); -+ -+ *out_instance = instance; -+ -+ return 0; -+ -+err_close_services: -+ -+ vchi_service_close(instance->handle); -+ vfree(instance->bulk_scratch); -+ kfree(instance); -+ return -ENODEV; -+} -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h -new file mode 100644 -index 0000000..9d1d11e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.h -@@ -0,0 +1,178 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * MMAL interface to VCHIQ message passing -+ */ -+ -+#ifndef MMAL_VCHIQ_H -+#define MMAL_VCHIQ_H -+ -+#include "mmal-msg-format.h" -+ -+#define MAX_PORT_COUNT 4 -+ -+/* Maximum size of the format extradata. */ -+#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 -+ -+struct vchiq_mmal_instance; -+ -+enum vchiq_mmal_es_type { -+ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ -+ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ -+ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ -+ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ -+ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ -+}; -+ -+/* rectangle, used lots so it gets its own struct */ -+struct vchiq_mmal_rect { -+ s32 x; -+ s32 y; -+ s32 width; -+ s32 height; -+}; -+ -+struct vchiq_mmal_port_buffer { -+ unsigned int num; /* number of buffers */ -+ u32 size; /* size of buffers */ -+ u32 alignment; /* alignment of buffers */ -+}; -+ -+struct vchiq_mmal_port; -+ -+typedef void (*vchiq_mmal_buffer_cb)( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ int status, struct mmal_buffer *buffer, -+ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); -+ -+struct vchiq_mmal_port { -+ bool enabled; -+ u32 handle; -+ u32 type; /* port type, cached to use on port info set */ -+ u32 index; /* port index, cached to use on port info set */ -+ -+ /* component port belongs to, allows simple deref */ -+ struct vchiq_mmal_component *component; -+ -+ struct vchiq_mmal_port *connected; /* port conencted to */ -+ -+ /* buffer info */ -+ struct vchiq_mmal_port_buffer minimum_buffer; -+ struct vchiq_mmal_port_buffer recommended_buffer; -+ struct vchiq_mmal_port_buffer current_buffer; -+ -+ /* stream format */ -+ struct mmal_es_format format; -+ /* elementry stream format */ -+ union mmal_es_specific_format es; -+ -+ /* data buffers to fill */ -+ struct list_head buffers; -+ /* lock to serialise adding and removing buffers from list */ -+ spinlock_t slock; -+ /* count of how many buffer header refils have failed because -+ * there was no buffer to satisfy them -+ */ -+ int buffer_underflow; -+ /* callback on buffer completion */ -+ vchiq_mmal_buffer_cb buffer_cb; -+ /* callback context */ -+ void *cb_ctx; -+}; -+ -+struct vchiq_mmal_component { -+ bool enabled; -+ u32 handle; /* VideoCore handle for component */ -+ u32 inputs; /* Number of input ports */ -+ u32 outputs; /* Number of output ports */ -+ u32 clocks; /* Number of clock ports */ -+ struct vchiq_mmal_port control; /* control port */ -+ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ -+ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ -+ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ -+}; -+ -+ -+int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); -+int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); -+ -+/* Initialise a mmal component and its ports -+* -+*/ -+int vchiq_mmal_component_init( -+ struct vchiq_mmal_instance *instance, -+ const char *name, -+ struct vchiq_mmal_component **component_out); -+ -+int vchiq_mmal_component_finalise( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+int vchiq_mmal_component_enable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+int vchiq_mmal_component_disable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+ -+ -+/* enable a mmal port -+ * -+ * enables a port and if a buffer callback provided enque buffer -+ * headers as apropriate for the port. -+ */ -+int vchiq_mmal_port_enable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ vchiq_mmal_buffer_cb buffer_cb); -+ -+/* disable a port -+ * -+ * disable a port will dequeue any pending buffers -+ */ -+int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port); -+ -+ -+int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, -+ void *value, -+ u32 value_size); -+ -+int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, -+ void *value, -+ u32 *value_size); -+ -+int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port); -+ -+int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *src, -+ struct vchiq_mmal_port *dst); -+ -+int vchiq_mmal_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, -+ u32 *minor_out); -+ -+int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ struct mmal_buffer *buf); -+ -+#endif /* MMAL_VCHIQ_H */ - -From 28e03357f47e284d1670f6b9aac95557d60ecb75 Mon Sep 17 00:00:00 2001 -From: Dave Stevenson -Date: Mon, 9 Dec 2013 10:58:01 +0000 -Subject: [PATCH 029/172] V4L2: Fixes from 6by9 - -V4L2: Fix EV values. Add manual shutter speed control - -V4L2 EV values should be in units of 1/1000. Corrected. -Add support for V4L2_CID_EXPOSURE_ABSOLUTE which should -give manual shutter control. Requires manual exposure mode -to be selected first. - -Signed-off-by: Dave Stevenson - -V4L2: Correct JPEG Q-factor range - -Should be 1-100, not 0-100 - -Signed-off-by: Dave Stevenson - -V4L2: Fix issue of driver jamming if STREAMON failed. - -Fix issue where the driver was left in a partially enabled -state if STREAMON failed, and would then reject many IOCTLs -as it thought it was streaming. - -Signed-off-by: Dave Stevenson - -V4L2: Fix ISO controls. - -Driver was passing the index to the GPU, and not the desired -ISO value. - -Signed-off-by: Dave Stevenson - -V4L2: Add flicker avoidance controls - -Add support for V4L2_CID_POWER_LINE_FREQUENCY to set flicker -avoidance frequencies. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for frame rate control. - -Add support for frame rate (or time per frame as V4L2 -inverts it) control via s_parm. - -Signed-off-by: Dave Stevenson - -V4L2: Improve G_FBUF handling so we pass conformance - -Return some sane numbers for get framebuffer so that -we pass conformance. - -Signed-off-by: Dave Stevenson - -V4L2: Fix information advertised through g_vidfmt - -Width and height were being stored based on incorrect -values. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for inline H264 headers - -Add support for V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER -to control H264 inline headers. -Requires firmware fix to work correctly, otherwise format -has to be set to H264 before this parameter is set. - -Signed-off-by: Dave Stevenson - -V4L2: Fix JPEG timestamp issue - -JPEG images were coming through from the GPU with timestamp -of 0. Detect this and give current system time instead -of some invalid value. - -Signed-off-by: Dave Stevenson - -V4L2: Fix issue when switching down JPEG resolution. - -JPEG buffer size calculation is based on input resolution. -Input resolution was being configured after output port -format. Caused failures if switching from one JPEG resolution -to a smaller one. - -Signed-off-by: Dave Stevenson - -V4L2: Enable MJPEG encoding - -Requires GPU firmware update to support MJPEG encoder. - -Signed-off-by: Dave Stevenson - -V4L2: Correct flag settings for compressed formats - -Set flags field correctly on enum_fmt_vid_cap for compressed -image formats. - -Signed-off-by: Dave Stevenson - -V4L2: H264 profile & level ctrls, FPS control and auto exp pri - -Several control handling updates. -H264 profile and level controls. -Timeperframe/FPS reworked to add V4L2_CID_EXPOSURE_AUTO_PRIORITY to -select whether AE is allowed to override the framerate specified. - -Signed-off-by: Dave Stevenson - -V4L2: Correct BGR24 to RGB24 in format table - -Signed-off-by: Dave Stevenson - -V4L2: Add additional pixel formats. Correct colourspace - -Adds the other flavours of YUYV, and NV12. -Corrects the overlay advertised colourspace. - -Signed-off-by: Dave Stevenson - -V4L2: Drop logging msg from info to debug - -Signed-off-by: Dave Stevenson - -V4L2: Initial pass at scene modes. - -Only supports exposure mode and metering modes. - -Signed-off-by: Dave Stevenson - -V4L2: Add manual white balance control. - -Adds support for V4L2_CID_RED_BALANCE and -V4L2_CID_BLUE_BALANCE. Only has an effect if -V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE has -V4L2_WHITE_BALANCE_MANUAL selected. - -Signed-off-by: Dave Stevenson - -config: Enable V4L / MMAL driver - -V4L2: Increase the MMAL timeout to 3sec - -MJPEG codec flush is now taking longer and results -in a kernel panic if the driver has stopped waiting for -the result when it finally completes. -Increase the timeout value from 1 to 3secs. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for setting H264_I_PERIOD - -Adds support for the parameter V4L2_CID_MPEG_VIDEO_H264_I_PERIOD -to set the frequency with which I frames are produced. - -Signed-off-by: Dave Stevenson - -V4L2: Enable GPU function for removing padding from images. - -GPU can now support arbitrary strides, although may require -additional processing to achieve it. Enable this feature -so that the images delivered are the size requested. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for V4L2_PIX_FMT_BGR32 - -Signed-off-by: Dave Stevenson - -V4L2: Set the colourspace to avoid odd YUV-RGB conversions - -Removes the amiguity from the conversion routines and stops -them dropping back to the SD vs HD choice of coeffs. - -Signed-off-by: Dave Stevenson - -V4L2: Make video/still threshold a run-time param - -Move the define for at what resolution the driver -switches from a video mode capture to a stills mode -capture to module parameters. - -Signed-off-by: Dave Stevenson - -V4L2: Fix incorrect pool sizing - -Signed-off-by: Dave Stevenson - -V4L2: Add option to disable enum_framesizes. - -Gstreamer's handling of a driver that advertises -V4L2_FRMSIZE_TYPE_STEPWISE to define the supported -resolutions is broken. See bug -https://bugzilla.gnome.org/show_bug.cgi?id=726521 - -Optional parameter of gst_v4l2src_is_broken added. -If non-zero, the driver claims not to support that -ioctl, and gstreamer should be happy again (it -guesses a set of defaults for itself). - -Signed-off-by: Dave Stevenson - -V4L2: Add support for more image formats - -Adds YVU420 (YV12), YVU420SP (NV21), and BGR888. - -Signed-off-by: Dave Stevenson - -V4L2: Extend range for V4L2_CID_MPEG_VIDEO_H264_I_PERIOD - -Request to extend the range from the fairly arbitrary -1000 frames (33 seconds at 30fps). Extend out to the -max range supported (int32 value). -Also allow 0, which is handled by the codec as only -send an I-frame on the first frame and never again. -There may be an exception if it detects a significant -scene change, but there's no easy way around that. - -Signed-off-by: Dave Stevenson - -bcm2835-camera: stop_streaming now has a void return ---- - arch/arm/configs/bcmrpi_defconfig | 3 + - drivers/media/platform/bcm2835/bcm2835-camera.c | 578 ++++++++++++++---- - drivers/media/platform/bcm2835/bcm2835-camera.h | 23 +- - drivers/media/platform/bcm2835/controls.c | 723 +++++++++++++++++++++-- - drivers/media/platform/bcm2835/mmal-common.h | 1 + - drivers/media/platform/bcm2835/mmal-encodings.h | 34 ++ - drivers/media/platform/bcm2835/mmal-parameters.h | 121 +++- - drivers/media/platform/bcm2835/mmal-vchiq.c | 4 +- - 8 files changed, 1300 insertions(+), 187 deletions(-) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index ef20985..63a6546 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -724,6 +724,9 @@ CONFIG_VIDEO_EM28XX=m - CONFIG_VIDEO_EM28XX_V4L2=m - CONFIG_VIDEO_EM28XX_ALSA=m - CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_V4L_PLATFORM_DRIVERS=y -+CONFIG_VIDEO_BCM2835=y -+CONFIG_VIDEO_BCM2835_MMAL=m - CONFIG_RADIO_SI470X=y - CONFIG_USB_SI470X=m - CONFIG_I2C_SI470X=m -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -index 1c9d9d5..e5a0010 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.c -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -36,7 +36,8 @@ - - #define BM2835_MMAL_VERSION "0.0.2" - #define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" -- -+#define MIN_WIDTH 16 -+#define MIN_HEIGHT 16 - #define MAX_WIDTH 2592 - #define MAX_HEIGHT 1944 - #define MIN_BUFFER_SIZE (80*1024) -@@ -53,13 +54,43 @@ int bcm2835_v4l2_debug; - module_param_named(debug, bcm2835_v4l2_debug, int, 0644); - MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); - -+int max_video_width = MAX_VIDEO_MODE_WIDTH; -+int max_video_height = MAX_VIDEO_MODE_HEIGHT; -+module_param(max_video_width, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max_video_width, "Threshold for video mode"); -+module_param(max_video_height, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max_video_height, "Threshold for video mode"); -+ -+/* Gstreamer bug https://bugzilla.gnome.org/show_bug.cgi?id=726521 -+ * v4l2src does bad (and actually wrong) things when the vidioc_enum_framesizes -+ * function says type V4L2_FRMSIZE_TYPE_STEPWISE, which we do by default. -+ * It's happier if we just don't say anything at all, when it then -+ * sets up a load of defaults that it thinks might work. -+ * If gst_v4l2src_is_broken is non-zero, then we remove the function from -+ * our function table list (actually switch to an alternate set, but same -+ * result). -+ */ -+int gst_v4l2src_is_broken = 0; -+module_param(gst_v4l2src_is_broken, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(gst_v4l2src_is_broken, "If non-zero, enable workaround for Gstreamer"); -+ - static struct bm2835_mmal_dev *gdev; /* global device data */ - -+#define FPS_MIN 1 -+#define FPS_MAX 90 -+ -+/* timeperframe: min/max and default */ -+static const struct v4l2_fract -+ tpf_min = {.numerator = 1, .denominator = FPS_MAX}, -+ tpf_max = {.numerator = 1, .denominator = FPS_MIN}, -+ tpf_default = {.numerator = 1000, .denominator = 30000}; -+ - /* video formats */ - static struct mmal_fmt formats[] = { - { - .name = "4:2:0, packed YUV", - .fourcc = V4L2_PIX_FMT_YUV420, -+ .flags = 0, - .mmal = MMAL_ENCODING_I420, - .depth = 12, - .mmal_component = MMAL_COMPONENT_CAMERA, -@@ -67,13 +98,15 @@ static struct mmal_fmt formats[] = { - { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, -+ .flags = 0, - .mmal = MMAL_ENCODING_YUYV, - .depth = 16, - .mmal_component = MMAL_COMPONENT_CAMERA, - }, - { -- .name = "RGB24 (BE)", -- .fourcc = V4L2_PIX_FMT_BGR24, -+ .name = "RGB24 (LE)", -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .flags = 0, - .mmal = MMAL_ENCODING_BGR24, - .depth = 24, - .mmal_component = MMAL_COMPONENT_CAMERA, -@@ -81,6 +114,7 @@ static struct mmal_fmt formats[] = { - { - .name = "JPEG", - .fourcc = V4L2_PIX_FMT_JPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, - .mmal = MMAL_ENCODING_JPEG, - .depth = 8, - .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, -@@ -88,10 +122,83 @@ static struct mmal_fmt formats[] = { - { - .name = "H264", - .fourcc = V4L2_PIX_FMT_H264, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, - .mmal = MMAL_ENCODING_H264, - .depth = 8, - .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -- } -+ }, -+ { -+ .name = "MJPEG", -+ .fourcc = V4L2_PIX_FMT_MJPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, -+ .mmal = MMAL_ENCODING_MJPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ }, -+ { -+ .name = "4:2:2, packed, YVYU", -+ .fourcc = V4L2_PIX_FMT_YVYU, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YVYU, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, VYUY", -+ .fourcc = V4L2_PIX_FMT_VYUY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_VYUY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, UYVY", -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_UYVY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed, NV12", -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB24 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_RGB24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed YVU", -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed, NV21", -+ .fourcc = V4L2_PIX_FMT_NV21, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV21, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB32 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_BGRA, -+ .depth = 32, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, - }; - - static struct mmal_fmt *get_format(struct v4l2_format *f) -@@ -229,7 +336,8 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, - } - } else { - if (dev->capture.frame_count) { -- if (dev->capture.vc_start_timestamp != -1) { -+ if (dev->capture.vc_start_timestamp != -1 && -+ pts != 0) { - s64 runtime_us = pts - - dev->capture.vc_start_timestamp; - u32 div = 0; -@@ -250,7 +358,7 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, - USEC_PER_SEC; - } - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "Convert start time %d.%06d and %llu" -+ "Convert start time %d.%06d and %llu " - "with offset %llu to %d.%06d\n", - (int)dev->capture.kernel_start_ts. - tv_sec, -@@ -425,7 +533,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) - vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); - if (ret) { - v4l2_err(&dev->v4l2_dev, -- "Failed to enable capture port - error %d\n", ret); -+ "Failed to enable capture port - error %d. " -+ "Disabling camera port again\n", ret); -+ -+ vchiq_mmal_port_disable(dev->instance, -+ dev->capture.camera_port); -+ if (disable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); -+ return -EINVAL; ++ if (vchiq_close_service_internal( ++ service, 0/*!close_recvd*/) != ++ VCHIQ_SUCCESS) ++ request_poll(state, service, ++ VCHIQ_POLL_REMOVE); ++ } else if (service_flags & ++ (1 << VCHIQ_POLL_TERMINATE)) { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: ps - terminate %d<->%d", ++ state->id, service->localport, ++ service->remoteport); ++ if (vchiq_close_service_internal( ++ service, 0/*!close_recvd*/) != ++ VCHIQ_SUCCESS) ++ request_poll(state, service, ++ VCHIQ_POLL_TERMINATE); ++ } ++ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY)) ++ notify_bulks(service, ++ &service->bulk_tx, ++ 1/*retry_poll*/); ++ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY)) ++ notify_bulks(service, ++ &service->bulk_rx, ++ 1/*retry_poll*/); ++ unlock_service(service); ++ } + } - return -1; - } - -@@ -439,7 +555,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) - } - - /* abort streaming and wait for last buffer */ --static int stop_streaming(struct vb2_queue *vq) -+static void stop_streaming(struct vb2_queue *vq) - { - int ret; - struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -@@ -451,8 +567,11 @@ static int stop_streaming(struct vb2_queue *vq) - dev->capture.frame_count = 0; - - /* ensure a format has actually been set */ -- if (dev->capture.port == NULL) -- return -EINVAL; -+ if (dev->capture.port == NULL) { -+ v4l2_err(&dev->v4l2_dev, -+ "no capture port - stream not started?\n"); ++ } ++} ++ ++/* Called by the slot handler or application threads, holding the bulk mutex. */ ++static int ++resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int resolved = 0; ++ int rc; ++ ++ while ((queue->process != queue->local_insert) && ++ (queue->process != queue->remote_insert)) { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: rb:%d %cx - li=%x ri=%x p=%x", ++ state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, ++ queue->process); ++ ++ WARN_ON(!((int)(queue->local_insert - queue->process) > 0)); ++ WARN_ON(!((int)(queue->remote_insert - queue->process) > 0)); ++ ++ rc = mutex_lock_interruptible(&state->bulk_transfer_mutex); ++ if (rc != 0) ++ break; ++ ++ vchiq_transfer_bulk(bulk); ++ mutex_unlock(&state->bulk_transfer_mutex); ++ ++ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { ++ const char *header = (queue == &service->bulk_tx) ? ++ "Send Bulk to" : "Recv Bulk from"; ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data); ++ else ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d," ++ " rx len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data); ++ } ++ ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ resolved++; ++ } ++ return resolved; ++} ++ ++/* Called with the bulk_mutex held */ ++static void ++abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ int is_tx = (queue == &service->bulk_tx); ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: aob:%d %cx - li=%x ri=%x p=%x", ++ service->state->id, service->localport, is_tx ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++ WARN_ON(!((int)(queue->local_insert - queue->process) >= 0)); ++ WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0)); ++ ++ while ((queue->process != queue->local_insert) || ++ (queue->process != queue->remote_insert)) { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ if (queue->process == queue->remote_insert) { ++ /* fabricate a matching dummy bulk */ ++ bulk->remote_data = NULL; ++ bulk->remote_size = 0; ++ queue->remote_insert++; ++ } ++ ++ if (queue->process != queue->local_insert) { ++ vchiq_complete_bulk(bulk); ++ ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d, " ++ "rx len:%d", ++ is_tx ? "Send Bulk to" : "Recv Bulk from", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size); ++ } else { ++ /* fabricate a matching dummy bulk */ ++ bulk->data = NULL; ++ bulk->size = 0; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : ++ VCHIQ_BULK_RECEIVE; ++ queue->local_insert++; ++ } ++ ++ queue->process++; ++ } ++} ++ ++/* Called from the slot handler thread */ ++static void ++pause_bulks(VCHIQ_STATE_T *state) ++{ ++ if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) { ++ WARN_ON_ONCE(1); ++ atomic_set(&pause_bulks_count, 1); + return; + } - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); - -@@ -483,12 +602,8 @@ static int stop_streaming(struct vb2_queue *vq) - ret); - } - -- if (disable_camera(dev) < 0) { -- v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); -- return -EINVAL; -- } -- -- return ret; -+ if (disable_camera(dev) < 0) -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); - } - - static void bm2835_mmal_lock(struct vb2_queue *vq) -@@ -530,6 +645,7 @@ static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; -+ f->flags = fmt->flags; - - return 0; - } -@@ -647,10 +763,18 @@ static int vidioc_g_fbuf(struct file *file, void *fh, - { - /* The video overlay must stay within the framebuffer and can't be - positioned independently. */ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct vchiq_mmal_port *preview_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; - a->flags = V4L2_FBUF_FLAG_OVERLAY; -- -- /* todo: v4l2_framebuffer still needs more info filling in -- * in order to pass the v4l2-compliance test. */ -+ a->fmt.width = preview_port->es.video.width; -+ a->fmt.height = preview_port->es.video.height; -+ a->fmt.pixelformat = V4L2_PIX_FMT_YUV420; -+ a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; -+ a->fmt.sizeimage = (preview_port->es.video.width * -+ preview_port->es.video.height * 3)>>1; -+ a->fmt.colorspace = V4L2_COLORSPACE_JPEG; - - return 0; - } -@@ -717,6 +841,8 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; -+ f->flags = fmt->flags; + - return 0; - } - -@@ -729,20 +855,13 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - f->fmt.pix.height = dev->capture.height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; -- f->fmt.pix.bytesperline = -- (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; -- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG -- && f->fmt.pix.sizeimage < (100 << 10)) { -- /* Need a minimum size for JPEG to account for EXIF. */ -- f->fmt.pix.sizeimage = (100 << 10); -- } -+ f->fmt.pix.bytesperline = dev->capture.stride; -+ f->fmt.pix.sizeimage = dev->capture.buffersize; - -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || -- dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) -- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -- else -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -@@ -766,21 +885,35 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - } - - f->fmt.pix.field = V4L2_FIELD_NONE; -- /* image must be a multiple of 32 pixels wide and 16 lines high */ -- v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, -- &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); -- f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; -- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -- if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Clipping/aligning %dx%d format %08X\n", -+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); -+ -+ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 1, -+ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 1, 0); -+ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth)>>3; -+ -+ /* Image buffer has to be padded to allow for alignment, even though -+ * we then remove that padding before delivering the buffer. -+ */ -+ f->fmt.pix.sizeimage = ((f->fmt.pix.height+15)&~15) * -+ (((f->fmt.pix.width+31)&~31) * mfmt->depth) >> 3; -+ -+ if ((mfmt->flags & V4L2_FMT_FLAG_COMPRESSED) && -+ f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) - f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; - -- if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || -- mfmt->fourcc == V4L2_PIX_FMT_UYVY) -- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -- else -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Now %dx%d format %08X\n", -+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); -+ - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, - __func__); - return 0; -@@ -818,8 +951,8 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - switch (mfmt->mmal_component) { - case MMAL_COMPONENT_CAMERA: - /* Make a further decision on port based on resolution */ -- if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH -- && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) -+ if (f->fmt.pix.width <= max_video_width -+ && f->fmt.pix.height <= max_video_height) - camera_port = port = - &dev->component[MMAL_COMPONENT_CAMERA]-> - output[MMAL_CAMERA_PORT_VIDEO]; -@@ -861,8 +994,9 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - camera_port->es.video.crop.y = 0; - camera_port->es.video.crop.width = f->fmt.pix.width; - camera_port->es.video.crop.height = f->fmt.pix.height; -- camera_port->es.video.frame_rate.num = 30; -+ camera_port->es.video.frame_rate.num = 0; - camera_port->es.video.frame_rate.den = 1; -+ camera_port->es.video.color_space = MMAL_COLOR_SPACE_JPEG_JFIF; - - ret = vchiq_mmal_port_set_format(dev->instance, camera_port); - -@@ -896,8 +1030,10 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - preview_port->es.video.crop.y = 0; - preview_port->es.video.crop.width = f->fmt.pix.width; - preview_port->es.video.crop.height = f->fmt.pix.height; -- preview_port->es.video.frame_rate.num = 30; -- preview_port->es.video.frame_rate.den = 1; -+ preview_port->es.video.frame_rate.num = -+ dev->capture.timeperframe.denominator; -+ preview_port->es.video.frame_rate.den = -+ dev->capture.timeperframe.numerator; - ret = vchiq_mmal_port_set_format(dev->instance, preview_port); - if (overlay_enabled) { - ret = vchiq_mmal_port_connect_tunnel( -@@ -913,7 +1049,9 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - - if (ret) { - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "%s failed to set format\n", __func__); -+ "%s failed to set format %dx%d %08X\n", __func__, -+ f->fmt.pix.width, f->fmt.pix.height, -+ f->fmt.pix.pixelformat); - /* ensure capture is not going to be tried */ - dev->capture.port = NULL; - } else { -@@ -927,69 +1065,91 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - camera_port->current_buffer.num = - camera_port->recommended_buffer.num; - -- port->format.encoding = mfmt->mmal; -- port->format.encoding_variant = 0; -- /* Set any encoding specific parameters */ -- switch (mfmt->mmal_component) { -- case MMAL_COMPONENT_VIDEO_ENCODE: -- port->format.bitrate = -- dev->capture.encode_bitrate; -- break; -- case MMAL_COMPONENT_IMAGE_ENCODE: -- /* Could set EXIF parameters here */ -- break; -- default: -- break; -- } -- ret = vchiq_mmal_port_set_format(dev->instance, port); -- -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ camera_port, -+ &encode_component->input[0]); - if (ret) { -- v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "%s failed to set format\n", __func__); -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to create connection\n", -+ __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; - } else { -+ port->es.video.width = f->fmt.pix.width; -+ port->es.video.height = f->fmt.pix.height; -+ port->es.video.crop.x = 0; -+ port->es.video.crop.y = 0; -+ port->es.video.crop.width = f->fmt.pix.width; -+ port->es.video.crop.height = f->fmt.pix.height; -+ port->es.video.frame_rate.num = -+ dev->capture.timeperframe.denominator; -+ port->es.video.frame_rate.den = -+ dev->capture.timeperframe.numerator; -+ -+ port->format.encoding = mfmt->mmal; -+ port->format.encoding_variant = 0; -+ /* Set any encoding specific parameters */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ port->format.bitrate = -+ dev->capture.encode_bitrate; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ /* Could set EXIF parameters here */ -+ break; -+ default: -+ break; -+ } -+ ret = vchiq_mmal_port_set_format(dev->instance, -+ port); -+ if (ret) -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to set format %dx%d fmt %08X\n", -+ __func__, -+ f->fmt.pix.width, -+ f->fmt.pix.height, -+ f->fmt.pix.pixelformat -+ ); -+ } -+ -+ if (!ret) { - ret = vchiq_mmal_component_enable( - dev->instance, - encode_component); - if (ret) { - v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "%s Failed to enable encode components\n", -- __func__); -- } else { -- /* configure buffering */ -- port->current_buffer.num = 1; -- port->current_buffer.size = -- f->fmt.pix.sizeimage; -- if (port->format.encoding == -- MMAL_ENCODING_JPEG) { -- v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "JPEG - fiddle buffer size\n"); -- port->current_buffer.size = -- (f->fmt.pix.sizeimage < -- (100 << 10)) -- ? (100 << 10) : f->fmt.pix. -- sizeimage; -- } -+ &dev->v4l2_dev, -+ "%s Failed to enable encode components\n", -+ __func__); -+ } -+ } -+ if (!ret) { -+ /* configure buffering */ -+ port->current_buffer.num = 1; -+ port->current_buffer.size = -+ f->fmt.pix.sizeimage; -+ if (port->format.encoding == -+ MMAL_ENCODING_JPEG) { - v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "vid_cap - current_buffer.size being set to %d\n", -- f->fmt.pix.sizeimage); -- port->current_buffer.alignment = 0; -- ret = -- vchiq_mmal_port_connect_tunnel( -- dev->instance, -- camera_port, -- &encode_component->input[0]); -- if (ret) { -- v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "%s failed to create connection\n", -- __func__); -- /* ensure capture is not going to be tried */ -- dev->capture.port = NULL; -- } -+ &dev->v4l2_dev, -+ "JPG - buf size now %d was %d\n", -+ f->fmt.pix.sizeimage, -+ port->current_buffer.size); -+ port->current_buffer.size = -+ (f->fmt.pix.sizeimage < -+ (100 << 10)) -+ ? (100 << 10) : f->fmt.pix. -+ sizeimage; - } -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "vid_cap - cur_buf.size set to %d\n", -+ f->fmt.pix.sizeimage); -+ port->current_buffer.alignment = 0; - } - } else { - /* configure buffering */ -@@ -1001,13 +1161,20 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - if (!ret) { - dev->capture.fmt = mfmt; - dev->capture.stride = f->fmt.pix.bytesperline; -- dev->capture.width = port->es.video.crop.width; -- dev->capture.height = port->es.video.crop.height; -+ dev->capture.width = camera_port->es.video.crop.width; -+ dev->capture.height = camera_port->es.video.crop.height; -+ dev->capture.buffersize = port->current_buffer.size; - - /* select port for capture */ - dev->capture.port = port; - dev->capture.camera_port = camera_port; - dev->capture.encode_component = encode_component; -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "Set dev->capture.fmt %08X, %dx%d, stride %d, size %d", -+ port->format.encoding, -+ dev->capture.width, dev->capture.height, -+ dev->capture.stride, dev->capture.buffersize); - } - } - -@@ -1048,14 +1215,115 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - } - - ret = mmal_setup_components(dev, f); -- if (ret != 0) -+ if (ret != 0) { - v4l2_err(&dev->v4l2_dev, - "%s: failed to setup mmal components: %d\n", - __func__, ret); -+ ret = -EINVAL; -+ } - - return ret; - } - -+int vidioc_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ static const struct v4l2_frmsize_stepwise sizes = { -+ MIN_WIDTH, MAX_WIDTH, 2, -+ MIN_HEIGHT, MAX_HEIGHT, 2 -+ }; -+ int i; -+ -+ if (fsize->index) -+ return -EINVAL; -+ for (i = 0; i < ARRAY_SIZE(formats); i++) -+ if (formats[i].fourcc == fsize->pixel_format) -+ break; -+ if (i == ARRAY_SIZE(formats)) -+ return -EINVAL; -+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -+ fsize->stepwise = sizes; -+ return 0; ++ /* Block bulk transfers from all services */ ++ mutex_lock(&state->bulk_transfer_mutex); +} + -+/* timeperframe is arbitrary and continous */ -+static int vidioc_enum_frameintervals(struct file *file, void *priv, -+ struct v4l2_frmivalenum *fival) ++/* Called from the slot handler thread */ ++static void ++resume_bulks(VCHIQ_STATE_T *state) +{ + int i; ++ if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) { ++ WARN_ON_ONCE(1); ++ atomic_set(&pause_bulks_count, 0); ++ return; ++ } + -+ if (fival->index) -+ return -EINVAL; ++ /* Allow bulk transfers from all services */ ++ mutex_unlock(&state->bulk_transfer_mutex); + -+ for (i = 0; i < ARRAY_SIZE(formats); i++) -+ if (formats[i].fourcc == fival->pixel_format) -+ break; -+ if (i == ARRAY_SIZE(formats)) -+ return -EINVAL; ++ if (state->deferred_bulks == 0) ++ return; + -+ /* regarding width & height - we support any within range */ -+ if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH || -+ fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT) -+ return -EINVAL; ++ /* Deal with any bulks which had to be deferred due to being in ++ * paused state. Don't try to match up to number of deferred bulks ++ * in case we've had something come and close the service in the ++ * interim - just process all bulk queues for all services */ ++ vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks", ++ __func__, state->deferred_bulks); + -+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ int resolved_rx = 0; ++ int resolved_tx = 0; ++ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ continue; + -+ /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ -+ fival->stepwise.min = tpf_min; -+ fival->stepwise.max = tpf_max; -+ fival->stepwise.step = (struct v4l2_fract) {1, 1}; ++ mutex_lock(&service->bulk_mutex); ++ resolved_rx = resolve_bulks(service, &service->bulk_rx); ++ resolved_tx = resolve_bulks(service, &service->bulk_tx); ++ mutex_unlock(&service->bulk_mutex); ++ if (resolved_rx) ++ notify_bulks(service, &service->bulk_rx, 1); ++ if (resolved_tx) ++ notify_bulks(service, &service->bulk_tx, 1); ++ } ++ state->deferred_bulks = 0; ++} ++ ++static int ++parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) ++{ ++ VCHIQ_SERVICE_T *service = NULL; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; ++ ++ msgid = header->msgid; ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); ++ if (size >= sizeof(struct vchiq_open_payload)) { ++ const struct vchiq_open_payload *payload = ++ (struct vchiq_open_payload *)header->data; ++ unsigned int fourcc; ++ ++ fourcc = payload->fourcc; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs OPEN@%x (%d->'%c%c%c%c')", ++ state->id, (unsigned int)header, ++ localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc)); ++ ++ service = get_listening_service(state, fourcc); ++ ++ if (service) { ++ /* A matching service exists */ ++ short version = payload->version; ++ short version_min = payload->version_min; ++ if ((service->version < version_min) || ++ (version < service->version_min)) { ++ /* Version mismatch */ ++ vchiq_loud_error_header(); ++ vchiq_loud_error("%d: service %d (%c%c%c%c) " ++ "version mismatch - local (%d, min %d)" ++ " vs. remote (%d, min %d)", ++ state->id, service->localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ service->version, service->version_min, ++ version, version_min); ++ vchiq_loud_error_footer(); ++ unlock_service(service); ++ service = NULL; ++ goto fail_open; ++ } ++ service->peer_version = version; ++ ++ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { ++ struct vchiq_openack_payload ack_payload = { ++ service->version ++ }; ++ VCHIQ_ELEMENT_T body = { ++ &ack_payload, ++ sizeof(ack_payload) ++ }; ++ ++ if (state->version_common < ++ VCHIQ_VERSION_SYNCHRONOUS_MODE) ++ service->sync = 0; ++ ++ /* Acknowledge the OPEN */ ++ if (service->sync && ++ (state->version_common >= ++ VCHIQ_VERSION_SYNCHRONOUS_MODE)) { ++ if (queue_message_sync(state, NULL, ++ VCHIQ_MAKE_MSG( ++ VCHIQ_MSG_OPENACK, ++ service->localport, ++ remoteport), ++ &body, 1, sizeof(ack_payload), ++ 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ } else { ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG( ++ VCHIQ_MSG_OPENACK, ++ service->localport, ++ remoteport), ++ &body, 1, sizeof(ack_payload), ++ 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ } ++ ++ /* The service is now open */ ++ vchiq_set_service_state(service, ++ service->sync ? VCHIQ_SRVSTATE_OPENSYNC ++ : VCHIQ_SRVSTATE_OPEN); ++ } ++ ++ service->remoteport = remoteport; ++ service->client_id = ((int *)header->data)[1]; ++ if (make_service_callback(service, VCHIQ_SERVICE_OPENED, ++ NULL, NULL) == VCHIQ_RETRY) { ++ /* Bail out if not ready */ ++ service->remoteport = VCHIQ_PORT_FREE; ++ goto bail_not_ready; ++ } ++ ++ /* Success - the message has been dealt with */ ++ unlock_service(service); ++ return 1; ++ } ++ } ++ ++fail_open: ++ /* No available service, or an invalid request - send a CLOSE */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)), ++ NULL, 0, 0, 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ ++ return 1; ++ ++bail_not_ready: ++ if (service) ++ unlock_service(service); + + return 0; +} + -+static int vidioc_g_parm(struct file *file, void *priv, -+ struct v4l2_streamparm *parm) ++/* Called by the slot handler thread */ ++static void ++parse_rx_slots(VCHIQ_STATE_T *state) +{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ VCHIQ_SHARED_STATE_T *remote = state->remote; ++ VCHIQ_SERVICE_T *service = NULL; ++ int tx_pos; ++ DEBUG_INITIALISE(state->local) + -+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; ++ tx_pos = remote->tx_pos; + -+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; -+ parm->parm.capture.timeperframe = dev->capture.timeperframe; -+ parm->parm.capture.readbuffers = 1; -+ return 0; -+} ++ while (state->rx_pos != tx_pos) { ++ VCHIQ_HEADER_T *header; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; + -+#define FRACT_CMP(a, OP, b) \ -+ ((u64)(a).numerator * (b).denominator OP \ -+ (u64)(b).numerator * (a).denominator) ++ DEBUG_TRACE(PARSE_LINE); ++ if (!state->rx_data) { ++ int rx_index; ++ WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0)); ++ rx_index = remote->slot_queue[ ++ SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, ++ rx_index); ++ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index); + -+static int vidioc_s_parm(struct file *file, void *priv, -+ struct v4l2_streamparm *parm) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct v4l2_fract tpf; -+ struct mmal_parameter_rational fps_param; -+ -+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; -+ -+ tpf = parm->parm.capture.timeperframe; -+ -+ /* tpf: {*, 0} resets timing; clip to [min, max]*/ -+ tpf = tpf.denominator ? tpf : tpf_default; -+ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; -+ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; -+ -+ dev->capture.timeperframe = tpf; -+ parm->parm.capture.timeperframe = tpf; -+ parm->parm.capture.readbuffers = 1; -+ -+ fps_param.num = 0; /* Select variable fps, and then use -+ * FPS_RANGE to select the actual limits. -+ */ -+ fps_param.den = 1; -+ set_framerate_params(dev); -+ -+ return 0; -+} -+ - static const struct v4l2_ioctl_ops camera0_ioctl_ops = { - /* overlay */ - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -@@ -1084,6 +1352,51 @@ static const struct v4l2_ioctl_ops camera0_ioctl_ops = { - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_enum_framesizes = vidioc_enum_framesizes, -+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -+ .vidioc_g_parm = vidioc_g_parm, -+ .vidioc_s_parm = vidioc_s_parm, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ -+ .vidioc_log_status = v4l2_ctrl_log_status, -+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+static const struct v4l2_ioctl_ops camera0_ioctl_ops_gstreamer = { -+ /* overlay */ -+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, -+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, -+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, -+ .vidioc_overlay = vidioc_overlay, -+ .vidioc_g_fbuf = vidioc_g_fbuf, -+ -+ /* inputs */ -+ .vidioc_enum_input = vidioc_enum_input, -+ .vidioc_g_input = vidioc_g_input, -+ .vidioc_s_input = vidioc_s_input, -+ -+ /* capture */ -+ .vidioc_querycap = vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, -+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -+ -+ /* buffer management */ -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ /* Remove this function ptr to fix gstreamer bug -+ .vidioc_enum_framesizes = vidioc_enum_framesizes, */ -+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -+ .vidioc_g_parm = vidioc_g_parm, -+ .vidioc_s_parm = vidioc_s_parm, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - -@@ -1122,8 +1435,10 @@ static int set_camera_parameters(struct vchiq_mmal_instance *instance, - .max_stills_h = MAX_HEIGHT, - .stills_yuv422 = 1, - .one_shot_stills = 1, -- .max_preview_video_w = 1920, -- .max_preview_video_h = 1088, -+ .max_preview_video_w = (max_video_width > 1920) ? -+ max_video_width : 1920, -+ .max_preview_video_h = (max_video_height > 1088) ? -+ max_video_height : 1088, - .num_preview_video_frames = 3, - .stills_capture_circular_buffer_height = 0, - .fast_preview_resume = 0, -@@ -1141,6 +1456,7 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - { - int ret; - struct mmal_es_format *format; -+ u32 bool_true = 1; - - ret = vchiq_mmal_init(&dev->instance); - if (ret < 0) -@@ -1176,8 +1492,8 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 1024; - format->es->video.crop.height = 768; -- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ -+ format->es->video.frame_rate.den = 1; - - format = - &dev->component[MMAL_COMPONENT_CAMERA]-> -@@ -1192,8 +1508,14 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 1024; - format->es->video.crop.height = 768; -- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ -+ format->es->video.frame_rate.den = 1; -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO], -+ MMAL_PARAMETER_NO_IMAGE_PADDING, -+ &bool_true, sizeof(bool_true)); - - format = - &dev->component[MMAL_COMPONENT_CAMERA]-> -@@ -1207,13 +1529,22 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 2592; - format->es->video.crop.height = 1944; -- format->es->video.frame_rate.num = 30; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ - format->es->video.frame_rate.den = 1; - - dev->capture.width = format->es->video.width; - dev->capture.height = format->es->video.height; - dev->capture.fmt = &formats[0]; - dev->capture.encode_component = NULL; -+ dev->capture.timeperframe = tpf_default; -+ dev->capture.enc_profile = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; -+ dev->capture.enc_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE], -+ MMAL_PARAMETER_NO_IMAGE_PADDING, -+ &bool_true, sizeof(bool_true)); - - /* get the preview component ready */ - ret = vchiq_mmal_component_init( -@@ -1260,6 +1591,14 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - } - - { -+ struct vchiq_mmal_port *encoder_port = -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ encoder_port->format.encoding = MMAL_ENCODING_H264; -+ ret = vchiq_mmal_port_set_format(dev->instance, -+ encoder_port); -+ } -+ -+ { - unsigned int enable = 1; - vchiq_mmal_port_parameter_set( - dev->instance, -@@ -1312,6 +1651,11 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - int ret; - - *vfd = vdev_template; -+ if (gst_v4l2src_is_broken) { -+ v4l2_info(&dev->v4l2_dev, -+ "Work-around for gstreamer issue is active.\n"); -+ vfd->ioctl_ops = &camera0_ioctl_ops_gstreamer; -+ } - - vfd->v4l2_dev = &dev->v4l2_dev; - -@@ -1326,8 +1670,9 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - if (ret < 0) - return ret; - -- v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", -- video_device_node_name(vfd)); -+ v4l2_info(vfd->v4l2_dev, -+ "V4L2 device registered as %s - stills mode > %dx%d\n", -+ video_device_node_name(vfd), max_video_width, max_video_height); - - return 0; - } -@@ -1335,9 +1680,9 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - static struct v4l2_format default_v4l2_format = { - .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, - .fmt.pix.width = 1024, -- .fmt.pix.bytesperline = 1024 * 3 / 2, -+ .fmt.pix.bytesperline = 1024, - .fmt.pix.height = 768, -- .fmt.pix.sizeimage = 1<<18, -+ .fmt.pix.sizeimage = 1024*768, - }; - - static int __init bm2835_mmal_init(void) -@@ -1400,6 +1745,9 @@ static int __init bm2835_mmal_init(void) - if (ret < 0) - goto unreg_dev; - -+ /* Really want to call vidioc_s_fmt_vid_cap with the default -+ * format, but currently the APIs don't join up. -+ */ - ret = mmal_setup_components(dev, &default_v4l2_format); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h -index 883eab7..7fe9f65 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.h -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.h -@@ -15,7 +15,7 @@ - * core driver device - */ - --#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ -+#define V4L2_CTRL_COUNT 28 /* number of v4l controls */ - - enum { - MMAL_COMPONENT_CAMERA = 0, -@@ -32,9 +32,6 @@ enum { - MMAL_CAMERA_PORT_COUNT - }; - --#define PREVIEW_FRAME_RATE_NUM 30 --#define PREVIEW_FRAME_RATE_DEN 1 -- - #define PREVIEW_LAYER 2 - - extern int bcm2835_v4l2_debug; -@@ -48,9 +45,19 @@ struct bm2835_mmal_dev { - /* controls */ - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; -+ enum v4l2_scene_mode scene_mode; - struct mmal_colourfx colourfx; - int hflip; - int vflip; -+ int red_gain; -+ int blue_gain; -+ enum mmal_parameter_exposuremode exposure_mode_user; -+ enum v4l2_exposure_auto_type exposure_mode_v4l2_user; -+ /* active exposure mode may differ if selected via a scene mode */ -+ enum mmal_parameter_exposuremode exposure_mode_active; -+ enum mmal_parameter_exposuremeteringmode metering_mode; -+ unsigned int manual_shutter_speed; -+ bool exp_auto_priority; - - /* allocated mmal instance and components */ - struct vchiq_mmal_instance *instance; -@@ -63,12 +70,18 @@ struct bm2835_mmal_dev { - unsigned int width; /* width */ - unsigned int height; /* height */ - unsigned int stride; /* stride */ -+ unsigned int buffersize; /* buffer size with padding */ - struct mmal_fmt *fmt; -+ struct v4l2_fract timeperframe; - - /* H264 encode bitrate */ - int encode_bitrate; - /* H264 bitrate mode. CBR/VBR */ - int encode_bitrate_mode; -+ /* H264 profile */ -+ enum v4l2_mpeg_video_h264_profile enc_profile; -+ /* H264 level */ -+ enum v4l2_mpeg_video_h264_level enc_level; - /* JPEG Q-factor */ - int q_factor; - -@@ -98,7 +111,7 @@ int bm2835_mmal_init_controls( - struct v4l2_ctrl_handler *hdl); - - int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); -- -+int set_framerate_params(struct bm2835_mmal_dev *dev); - - /* Debug helpers */ - -diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c -index d1408e5..3017b94 100644 ---- a/drivers/media/platform/bcm2835/controls.c -+++ b/drivers/media/platform/bcm2835/controls.c -@@ -30,11 +30,23 @@ - #include "mmal-parameters.h" - #include "bcm2835-camera.h" - --/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. -- * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. -+/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -4.0 to +4.0. -+ * MMAL values are in 1/6th increments so the MMAL range is -24 to +24. -+ * V4L2 docs say value "is expressed in terms of EV, drivers should interpret -+ * the values as 0.001 EV units, where the value 1000 stands for +1 EV." -+ * V4L2 is limited to a max of 32 values in a menu, so count in 1/3rds from -+ * -4 to +4 - */ - static const s64 ev_bias_qmenu[] = { -- -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 -+ -4000, -3667, -3333, -+ -3000, -2667, -2333, -+ -2000, -1667, -1333, -+ -1000, -667, -333, -+ 0, 333, 667, -+ 1000, 1333, 1667, -+ 2000, 2333, 2667, -+ 3000, 3333, 3667, -+ 4000 - }; - - /* Supported ISO values -@@ -44,13 +56,19 @@ static const s64 iso_qmenu[] = { - 0, 100, 200, 400, 800, - }; - -+static const s64 mains_freq_qmenu[] = { -+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, -+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, -+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, -+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO -+}; -+ - /* Supported video encode modes */ - static const s64 bitrate_mode_qmenu[] = { - (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, - }; - -- - enum bm2835_mmal_ctrl_type { - MMAL_CONTROL_TYPE_STD, - MMAL_CONTROL_TYPE_STD_MENU, -@@ -77,6 +95,7 @@ struct bm2835_mmal_v4l2_ctrl { - const s64 *imenu; /* integer menu array */ - u32 mmal_id; /* mmal parameter id */ - bm2835_mmal_v4l2_ctrl_cb *setter; -+ bool ignore_errors; - }; - - struct v4l2_to_mmal_effects_setting { -@@ -126,6 +145,25 @@ static const struct v4l2_to_mmal_effects_setting - 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } - }; - -+struct v4l2_mmal_scene_config { -+ enum v4l2_scene_mode v4l2_scene; -+ enum mmal_parameter_exposuremode exposure_mode; -+ enum mmal_parameter_exposuremeteringmode metering_mode; -+}; -+ -+static const struct v4l2_mmal_scene_config scene_configs[] = { -+ /* V4L2_SCENE_MODE_NONE automatically added */ -+ { -+ V4L2_SCENE_MODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE -+ }, -+ { -+ V4L2_SCENE_MODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE -+ }, -+}; - - /* control handlers*/ - -@@ -133,10 +171,7 @@ static int ctrl_set_rational(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -- struct { -- s32 num; /**< Numerator */ -- s32 den; /**< Denominator */ -- } rational_value; -+ struct mmal_parameter_rational rational_value; - struct vchiq_mmal_port *control; - - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -@@ -166,6 +201,41 @@ static int ctrl_set_value(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_value_menu(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ if (ctrl->val > mmal_ctrl->max || ctrl->val < mmal_ctrl->min) -+ return 1; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ u32_value = mmal_ctrl->imenu[ctrl->val]; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ s32 s32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ s32_value = (ctrl->val-12)*2; /* Convert from index to 1/6ths */ -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &s32_value, sizeof(s32_value)); -+} -+ - static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -@@ -245,37 +315,97 @@ static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -- u32 u32_value; -+ enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode_user; -+ u32 shutter_speed = 0; - struct vchiq_mmal_port *control; -+ int ret = 0; - - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; - -+ if (mmal_ctrl->mmal_id == MMAL_PARAMETER_SHUTTER_SPEED) { -+ /* V4L2 is in 100usec increments. -+ * MMAL is 1usec. -+ */ -+ dev->manual_shutter_speed = ctrl->val * 100; -+ } else if (mmal_ctrl->mmal_id == MMAL_PARAMETER_EXPOSURE_MODE) { -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_AUTO: -+ exp_mode = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ break; -+ -+ case V4L2_EXPOSURE_MANUAL: -+ exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF; -+ break; ++ /* Initialise use_count to one, and increment ++ ** release_count at the end of the slot to avoid ++ ** releasing the slot prematurely. */ ++ state->rx_info->use_count = 1; ++ state->rx_info->release_count = 0; + } -+ dev->exposure_mode_user = exp_mode; -+ dev->exposure_mode_v4l2_user = ctrl->val; -+ } else if (mmal_ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { -+ dev->exp_auto_priority = ctrl->val; -+ } + -+ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { -+ if (exp_mode == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; ++ header = (VCHIQ_HEADER_T *)(state->rx_data + ++ (state->rx_pos & VCHIQ_SLOT_MASK)); ++ DEBUG_VALUE(PARSE_HEADER, (int)header); ++ msgid = header->msgid; ++ DEBUG_VALUE(PARSE_MSGID, msgid); ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); + -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exp_mode, -+ sizeof(u32)); -+ dev->exposure_mode_active = exp_mode; -+ } -+ /* exposure_dynamic_framerate (V4L2_CID_EXPOSURE_AUTO_PRIORITY) should -+ * always apply irrespective of scene mode. -+ */ -+ ret += set_framerate_params(dev); ++ if (type != VCHIQ_MSG_DATA) ++ VCHIQ_STATS_INC(state, ctrl_rx_count); + -+ return ret; -+} ++ switch (type) { ++ case VCHIQ_MSG_OPENACK: ++ case VCHIQ_MSG_CLOSE: ++ case VCHIQ_MSG_DATA: ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ service = find_service_by_port(state, localport); ++ if ((!service || ++ ((service->remoteport != remoteport) && ++ (service->remoteport != VCHIQ_PORT_FREE))) && ++ (localport == 0) && ++ (type == VCHIQ_MSG_CLOSE)) { ++ /* This could be a CLOSE from a client which ++ hadn't yet received the OPENACK - look for ++ the connected service */ ++ if (service) ++ unlock_service(service); ++ service = get_connected_service(state, ++ remoteport); ++ if (service) ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) - " ++ "found connected service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ service->localport); ++ } + -+static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ - switch (ctrl->val) { -- case V4L2_EXPOSURE_AUTO: -- u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ case V4L2_EXPOSURE_METERING_AVERAGE: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; - break; - -- case V4L2_EXPOSURE_MANUAL: -- u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; -+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; - break; - -- case V4L2_EXPOSURE_SHUTTER_PRIORITY: -- u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; -+ case V4L2_EXPOSURE_METERING_SPOT: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; - break; - -- case V4L2_EXPOSURE_APERTURE_PRIORITY: -- u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; -+ /* todo matrix weighting not added to Linux API till 3.9 -+ case V4L2_EXPOSURE_METERING_MATRIX: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; - break; -+ */ - - } - -- /* todo: what about the other ten modes there are MMAL parameters for */ -- return vchiq_mmal_port_parameter_set(dev->instance, control, -+ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { -+ struct vchiq_mmal_port *control; -+ u32 u32_value = dev->metering_mode; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, - mmal_ctrl->mmal_id, - &u32_value, sizeof(u32_value)); -+ } else -+ return 0; - } - --static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -@@ -285,24 +415,18 @@ static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; - - switch (ctrl->val) { -- case V4L2_EXPOSURE_METERING_AVERAGE: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; -+ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: -+ u32_value = MMAL_PARAM_FLICKERAVOID_OFF; - break; -- -- case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; -+ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: -+ u32_value = MMAL_PARAM_FLICKERAVOID_50HZ; - break; -- -- case V4L2_EXPOSURE_METERING_SPOT: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; -+ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: -+ u32_value = MMAL_PARAM_FLICKERAVOID_60HZ; - break; -- -- /* todo matrix weighting not added to Linux API till 3.9 -- case V4L2_EXPOSURE_METERING_MATRIX: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; -+ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: -+ u32_value = MMAL_PARAM_FLICKERAVOID_AUTO; - break; -- */ -- - } - - return vchiq_mmal_port_parameter_set(dev->instance, control, -@@ -367,6 +491,29 @@ static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_awb_gains(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct vchiq_mmal_port *control; -+ struct mmal_parameter_awbgains gains; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ if (ctrl->id == V4L2_CID_RED_BALANCE) -+ dev->red_gain = ctrl->val; -+ else if (ctrl->id == V4L2_CID_BLUE_BALANCE) -+ dev->blue_gain = ctrl->val; -+ -+ gains.r_gain.num = dev->red_gain; -+ gains.b_gain.num = dev->blue_gain; -+ gains.r_gain.den = gains.b_gain.den = 1000; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &gains, sizeof(gains)); -+} -+ - static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -@@ -443,8 +590,8 @@ static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, - &dev->colourfx, sizeof(dev->colourfx)); - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -- mmal_ctrl, ctrl->id, ctrl->val, ret, -+ "%s: After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -+ __func__, mmal_ctrl, ctrl->id, ctrl->val, ret, - (ret == 0 ? 0 : -EINVAL)); - return (ret == 0 ? 0 : EINVAL); - } -@@ -494,7 +641,7 @@ static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, - return 0; - } - --static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, -+static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -@@ -510,12 +657,247 @@ static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_video_encode_param_output(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *vid_enc_ctl; -+ -+ vid_enc_ctl = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, vid_enc_ctl, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_video_encode_profile_level(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct mmal_parameter_video_profile param; -+ int ret = 0; -+ -+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) { -+ switch (ctrl->val) { -+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: -+ dev->capture.enc_profile = ctrl->val; ++ if (!service) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) - " ++ "invalid/closed service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, localport); ++ goto skip_message; ++ } + break; + default: -+ ret = -EINVAL; -+ break; -+ } -+ } else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) { -+ switch (ctrl->val) { -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: -+ dev->capture.enc_level = ctrl->val; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ } -+ -+ if (!ret) { -+ switch (dev->capture.enc_profile) { -+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: -+ param.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: -+ param.profile = -+ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: -+ param.profile = MMAL_VIDEO_PROFILE_H264_MAIN; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: -+ param.profile = MMAL_VIDEO_PROFILE_H264_HIGH; -+ break; -+ default: -+ /* Should never get here */ + break; + } + -+ switch (dev->capture.enc_level) { -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_1; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: -+ param.level = MMAL_VIDEO_LEVEL_H264_1b; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_11; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_12; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: -+ param.level = MMAL_VIDEO_LEVEL_H264_13; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_2; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_21; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_22; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_3; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_31; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_32; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_4; -+ break; -+ default: -+ /* Should never get here */ -+ break; ++ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d " ++ "len:%d", ++ msg_type_str(type), type, ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ remoteport, localport, size); ++ if (size > 0) ++ vchiq_log_dump_mem("Rcvd", 0, header->data, ++ min(64, size)); + } + -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0], -+ mmal_ctrl->mmal_id, -+ ¶m, sizeof(param)); -+ } -+ return ret; -+} ++ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) ++ > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "header %x (msgid %x) - size %x too big for " ++ "slot", ++ (unsigned int)header, (unsigned int)msgid, ++ (unsigned int)size); ++ WARN(1, "oversized for slot\n"); ++ } + -+static int ctrl_set_scene_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = 0; -+ int shutter_speed; -+ struct vchiq_mmal_port *control; ++ switch (type) { ++ case VCHIQ_MSG_OPEN: ++ WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0)); ++ if (!parse_open(state, header)) ++ goto bail_not_ready; ++ break; ++ case VCHIQ_MSG_OPENACK: ++ if (size >= sizeof(struct vchiq_openack_payload)) { ++ const struct vchiq_openack_payload *payload = ++ (struct vchiq_openack_payload *) ++ header->data; ++ service->peer_version = payload->version; ++ } ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs OPENACK@%x,%x (%d->%d) v:%d", ++ state->id, (unsigned int)header, size, ++ remoteport, localport, service->peer_version); ++ if (service->srvstate == ++ VCHIQ_SRVSTATE_OPENING) { ++ service->remoteport = remoteport; ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_OPEN); ++ up(&service->remove_event); ++ } else ++ vchiq_log_error(vchiq_core_log_level, ++ "OPENACK received in state %s", ++ srvstate_names[service->srvstate]); ++ break; ++ case VCHIQ_MSG_CLOSE: ++ WARN_ON(size != 0); /* There should be no data */ + -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "scene mode selected %d, was %d\n", ctrl->val, -+ dev->scene_mode); -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs CLOSE@%x (%d->%d)", ++ state->id, (unsigned int)header, ++ remoteport, localport); + -+ if (ctrl->val == dev->scene_mode) -+ return 0; ++ mark_service_closing_internal(service, 1); + -+ if (ctrl->val == V4L2_SCENE_MODE_NONE) { -+ /* Restore all user selections */ -+ dev->scene_mode = V4L2_SCENE_MODE_NONE; ++ if (vchiq_close_service_internal(service, ++ 1/*close_recvd*/) == VCHIQ_RETRY) ++ goto bail_not_ready; + -+ if (dev->exposure_mode_user == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; -+ else -+ shutter_speed = 0; ++ vchiq_log_info(vchiq_core_log_level, ++ "Close Service %c%c%c%c s:%u d:%d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->localport, ++ service->remoteport); ++ break; ++ case VCHIQ_MSG_DATA: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs DATA@%x,%x (%d->%d)", ++ state->id, (unsigned int)header, size, ++ remoteport, localport); + -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", -+ __func__, shutter_speed, dev->exposure_mode_user, -+ dev->metering_mode); -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &dev->exposure_mode_user, -+ sizeof(u32)); -+ dev->exposure_mode_active = dev->exposure_mode_user; -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &dev->metering_mode, -+ sizeof(u32)); -+ ret += set_framerate_params(dev); -+ } else { -+ /* Set up scene mode */ -+ int i; -+ const struct v4l2_mmal_scene_config *scene = NULL; -+ int shutter_speed; -+ enum mmal_parameter_exposuremode exposure_mode; -+ enum mmal_parameter_exposuremeteringmode metering_mode; ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) { ++ header->msgid = msgid | VCHIQ_MSGID_CLAIMED; ++ claim_slot(state->rx_info); ++ DEBUG_TRACE(PARSE_LINE); ++ if (make_service_callback(service, ++ VCHIQ_MESSAGE_AVAILABLE, header, ++ NULL) == VCHIQ_RETRY) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, ++ size); ++ } else { ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ break; ++ case VCHIQ_MSG_CONNECT: ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs CONNECT@%x", ++ state->id, (unsigned int)header); ++ state->version_common = ((VCHIQ_SLOT_ZERO_T *) ++ state->slot_data)->version; ++ up(&state->connect); ++ break; ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: { ++ VCHIQ_BULK_QUEUE_T *queue; ++ WARN_ON(!state->is_master); ++ queue = (type == VCHIQ_MSG_BULK_RX) ? ++ &service->bulk_tx : &service->bulk_rx; ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) { ++ VCHIQ_BULK_T *bulk; ++ int resolved = 0; + -+ for (i = 0; i < ARRAY_SIZE(scene_configs); i++) { -+ if (scene_configs[i].v4l2_scene == -+ ctrl->val) { -+ scene = &scene_configs[i]; ++ DEBUG_TRACE(PARSE_LINE); ++ if (mutex_lock_interruptible( ++ &service->bulk_mutex) != 0) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ ++ WARN_ON(!(queue->remote_insert < queue->remove + ++ VCHIQ_NUM_SERVICE_BULKS)); ++ bulk = &queue->bulks[ ++ BULK_INDEX(queue->remote_insert)]; ++ bulk->remote_data = ++ (void *)((int *)header->data)[0]; ++ bulk->remote_size = ((int *)header->data)[1]; ++ wmb(); ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->remote_size, ++ (unsigned int)bulk->remote_data); ++ ++ queue->remote_insert++; ++ ++ if (atomic_read(&pause_bulks_count)) { ++ state->deferred_bulks++; ++ vchiq_log_info(vchiq_core_log_level, ++ "%s: deferring bulk (%d)", ++ __func__, ++ state->deferred_bulks); ++ if (state->conn_state != ++ VCHIQ_CONNSTATE_PAUSE_SENT) ++ vchiq_log_error( ++ vchiq_core_log_level, ++ "%s: bulks paused in " ++ "unexpected state %s", ++ __func__, ++ conn_state_names[ ++ state->conn_state]); ++ } else if (state->conn_state == ++ VCHIQ_CONNSTATE_CONNECTED) { ++ DEBUG_TRACE(PARSE_LINE); ++ resolved = resolve_bulks(service, ++ queue); ++ } ++ ++ mutex_unlock(&service->bulk_mutex); ++ if (resolved) ++ notify_bulks(service, queue, ++ 1/*retry_poll*/); ++ } ++ } break; ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ WARN_ON(state->is_master); ++ if ((service->remoteport == remoteport) ++ && (service->srvstate != ++ VCHIQ_SRVSTATE_FREE)) { ++ VCHIQ_BULK_QUEUE_T *queue; ++ VCHIQ_BULK_T *bulk; ++ ++ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ? ++ &service->bulk_rx : &service->bulk_tx; ++ ++ DEBUG_TRACE(PARSE_LINE); ++ if (mutex_lock_interruptible( ++ &service->bulk_mutex) != 0) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ if ((int)(queue->remote_insert - ++ queue->local_insert) >= 0) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) " ++ "unexpected (ri=%d,li=%d)", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ queue->remote_insert, ++ queue->local_insert); ++ mutex_unlock(&service->bulk_mutex); ++ break; ++ } ++ ++ BUG_ON(queue->process == queue->local_insert); ++ BUG_ON(queue->process != queue->remote_insert); ++ ++ bulk = &queue->bulks[ ++ BULK_INDEX(queue->remote_insert)]; ++ bulk->actual = *(int *)header->data; ++ queue->remote_insert++; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->actual, (unsigned int)bulk->data); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs:%d %cx li=%x ri=%x p=%x", ++ state->id, localport, ++ (type == VCHIQ_MSG_BULK_RX_DONE) ? ++ 'r' : 't', ++ queue->local_insert, ++ queue->remote_insert, queue->process); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ WARN_ON(queue->process == queue->local_insert); ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ mutex_unlock(&service->bulk_mutex); ++ DEBUG_TRACE(PARSE_LINE); ++ notify_bulks(service, queue, 1/*retry_poll*/); ++ DEBUG_TRACE(PARSE_LINE); ++ } ++ break; ++ case VCHIQ_MSG_PADDING: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs PADDING@%x,%x", ++ state->id, (unsigned int)header, size); ++ break; ++ case VCHIQ_MSG_PAUSE: ++ /* If initiated, signal the application thread */ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs PAUSE@%x,%x", ++ state->id, (unsigned int)header, size); ++ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: PAUSE received in state PAUSED", ++ state->id); + break; + } -+ } -+ if (i >= ARRAY_SIZE(scene_configs)) -+ return -EINVAL; -+ -+ /* Set all the values */ -+ dev->scene_mode = ctrl->val; -+ -+ if (scene->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; -+ else -+ shutter_speed = 0; -+ exposure_mode = scene->exposure_mode; -+ metering_mode = scene->metering_mode; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", -+ __func__, shutter_speed, exposure_mode, metering_mode); -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exposure_mode, -+ sizeof(u32)); -+ dev->exposure_mode_active = exposure_mode; -+ ret += vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exposure_mode, -+ sizeof(u32)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &metering_mode, -+ sizeof(u32)); -+ ret += set_framerate_params(dev); -+ } -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: Setting scene to %d, ret=%d\n", -+ __func__, ctrl->val, ret); -+ ret = -EINVAL; -+ } -+ return 0; -+} -+ - static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) - { - struct bm2835_mmal_dev *dev = - container_of(ctrl->handler, struct bm2835_mmal_dev, - ctrl_handler); - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; -+ int ret; - - if ((mmal_ctrl == NULL) || - (mmal_ctrl->id != ctrl->id) || -@@ -524,7 +906,13 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) - return -EINVAL; - } - -- return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+ ret = mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+ if (ret) -+ pr_warn("ctrl id:%d/MMAL param %08X- returned ret %d\n", -+ ctrl->id, mmal_ctrl->mmal_id, ret); -+ if (mmal_ctrl->ignore_errors) -+ ret = 0; -+ return ret; - } - - static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { -@@ -537,40 +925,54 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { - { - V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_SATURATION, &ctrl_set_rational -+ MMAL_PARAMETER_SATURATION, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational -+ MMAL_PARAMETER_SHARPNESS, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_CONTRAST, &ctrl_set_rational -+ MMAL_PARAMETER_CONTRAST, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, - 0, 100, 50, 1, NULL, -- MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational -+ MMAL_PARAMETER_BRIGHTNESS, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, - 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, -- MMAL_PARAMETER_ISO, &ctrl_set_value -+ MMAL_PARAMETER_ISO, -+ &ctrl_set_value_menu, -+ false - }, - { - V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value -+ MMAL_PARAMETER_VIDEO_STABILISATION, -+ &ctrl_set_value, -+ false - }, - /* { - 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL -- }, --*/ { -+ }, */ -+ { - V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, - ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, -- MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &ctrl_set_exposure, -+ false - }, - /* todo this needs mixing in with set exposure - { -@@ -578,83 +980,258 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { - }, - */ - { -+ V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, -+ /* Units of 100usecs */ -+ 1, 1*1000*10, 100*10, 1, NULL, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &ctrl_set_exposure, -+ false -+ }, -+ { - V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, - 0, ARRAY_SIZE(ev_bias_qmenu) - 1, - (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, -- MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value -+ MMAL_PARAMETER_EXPOSURE_COMP, -+ &ctrl_set_value_ev, -+ false -+ }, -+ { -+ V4L2_CID_EXPOSURE_AUTO_PRIORITY, MMAL_CONTROL_TYPE_STD, -+ 0, 1, -+ 0, 1, NULL, -+ 0, /* Dummy MMAL ID as it gets mapped into FPS range*/ -+ &ctrl_set_exposure, -+ false - }, - { - V4L2_CID_EXPOSURE_METERING, - MMAL_CONTROL_TYPE_STD_MENU, - ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, -- MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &ctrl_set_metering_mode, -+ false - }, - { - V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, - MMAL_CONTROL_TYPE_STD_MENU, -- ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -- MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode -+ ~0x3ff, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -+ MMAL_PARAMETER_AWB_MODE, -+ &ctrl_set_awb_mode, -+ false -+ }, -+ { -+ V4L2_CID_RED_BALANCE, MMAL_CONTROL_TYPE_STD, -+ 1, 7999, 1000, 1, NULL, -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ &ctrl_set_awb_gains, -+ false -+ }, -+ { -+ V4L2_CID_BLUE_BALANCE, MMAL_CONTROL_TYPE_STD, -+ 1, 7999, 1000, 1, NULL, -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ &ctrl_set_awb_gains, -+ false - }, - { - V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, - 0, 15, V4L2_COLORFX_NONE, 0, NULL, -- MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect -+ MMAL_PARAMETER_IMAGE_EFFECT, -+ &ctrl_set_image_effect, -+ false - }, - { - V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, - 0, 0xffff, 0x8080, 1, NULL, -- MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &ctrl_set_colfx, -+ false - }, - { - V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, - 0, 360, 0, 90, NULL, -- MMAL_PARAMETER_ROTATION, &ctrl_set_rotate -+ MMAL_PARAMETER_ROTATION, -+ &ctrl_set_rotate, -+ false - }, - { - V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ MMAL_PARAMETER_MIRROR, -+ &ctrl_set_flip, -+ false - }, - { - V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ MMAL_PARAMETER_MIRROR, -+ &ctrl_set_flip, -+ false - }, - { - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, - 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, - 0, 0, bitrate_mode_qmenu, -- MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode -+ MMAL_PARAMETER_RATECONTROL, -+ &ctrl_set_bitrate_mode, -+ false - }, - { - V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, - 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, -- MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate -+ MMAL_PARAMETER_VIDEO_BIT_RATE, -+ &ctrl_set_bitrate, -+ false - }, - { - V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, -- 0, 100, -+ 1, 100, - 30, 1, NULL, -- MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor -+ MMAL_PARAMETER_JPEG_Q_FACTOR, -+ &ctrl_set_image_encode_output, -+ false -+ }, -+ { -+ V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, ARRAY_SIZE(mains_freq_qmenu) - 1, -+ 1, 1, NULL, -+ MMAL_PARAMETER_FLICKER_AVOID, -+ &ctrl_set_flicker_avoidance, -+ false -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, MMAL_CONTROL_TYPE_STD, -+ 0, 1, -+ 0, 1, NULL, -+ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, -+ &ctrl_set_video_encode_param_output, -+ true /* Errors ignored as requires latest firmware to work */ -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_H264_PROFILE, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~((1<ctrls[c]) && (v4l2_ctrls[c].setter)) { - ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], - &v4l2_ctrls[c]); -- if (ret) -+ if (!v4l2_ctrls[c].ignore_errors && ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Failed when setting default values for ctrl %d\n", -+ c); - break; ++ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { ++ /* Send a PAUSE in response */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK) ++ == VCHIQ_RETRY) ++ goto bail_not_ready; ++ if (state->is_master) ++ pause_bulks(state); + } - } - } - return ret; - } - -+int set_framerate_params(struct bm2835_mmal_dev *dev) -+{ -+ struct mmal_parameter_fps_range fps_range; -+ int ret; ++ /* At this point slot_mutex is held */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); ++ vchiq_platform_paused(state); ++ break; ++ case VCHIQ_MSG_RESUME: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs RESUME@%x,%x", ++ state->id, (unsigned int)header, size); ++ /* Release the slot mutex */ ++ mutex_unlock(&state->slot_mutex); ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ break; + -+ if ((dev->exposure_mode_active != MMAL_PARAM_EXPOSUREMODE_OFF) && -+ (dev->exp_auto_priority)) { -+ /* Variable FPS. Define min FPS as 1fps. -+ * Max as max defined FPS. -+ */ -+ fps_range.fps_low.num = 1; -+ fps_range.fps_low.den = 1; -+ fps_range.fps_high.num = dev->capture.timeperframe.denominator; -+ fps_range.fps_high.den = dev->capture.timeperframe.numerator; -+ } else { -+ /* Fixed FPS - set min and max to be the same */ -+ fps_range.fps_low.num = fps_range.fps_high.num = -+ dev->capture.timeperframe.denominator; -+ fps_range.fps_low.den = fps_range.fps_high.den = -+ dev->capture.timeperframe.numerator; ++ case VCHIQ_MSG_REMOTE_USE: ++ vchiq_on_remote_use(state); ++ break; ++ case VCHIQ_MSG_REMOTE_RELEASE: ++ vchiq_on_remote_release(state); ++ break; ++ case VCHIQ_MSG_REMOTE_USE_ACTIVE: ++ vchiq_on_remote_use_active(state); ++ break; ++ ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs invalid msgid %x@%x,%x", ++ state->id, msgid, (unsigned int)header, size); ++ WARN(1, "invalid message\n"); ++ break; ++ } ++ ++skip_message: ++ if (service) { ++ unlock_service(service); ++ service = NULL; ++ } ++ ++ state->rx_pos += calc_stride(size); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ /* Perform some housekeeping when the end of the slot is ++ ** reached. */ ++ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) { ++ /* Remove the extra reference count. */ ++ release_slot(state, state->rx_info, NULL, NULL); ++ state->rx_data = NULL; ++ } + } + -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Set fps range to %d/%d to %d/%d\n", -+ fps_range.fps_low.num, -+ fps_range.fps_low.den, -+ fps_range.fps_high.num, -+ fps_range.fps_high.den -+ ); -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ if (ret) -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Failed to set fps ret %d\n", -+ ret); -+ -+ return ret; -+ ++bail_not_ready: ++ if (service) ++ unlock_service(service); +} + - int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl_handler *hdl) - { -@@ -674,10 +1251,30 @@ int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, - break; - - case MMAL_CONTROL_TYPE_STD_MENU: -+ { -+ int mask = ctrl->min; ++/* Called by the slot handler thread */ ++static int ++slot_handler_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ DEBUG_INITIALISE(local) + -+ if (ctrl->id == V4L2_CID_SCENE_MODE) { -+ /* Special handling to work out the mask -+ * value based on the scene_configs array -+ * at runtime. Reduces the chance of -+ * mismatches. -+ */ -+ int i; -+ mask = 1<trigger); ++ ++ rmb(); ++ ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ if (state->poll_needed) { ++ /* Check if we need to suspend - may change our ++ * conn_state */ ++ vchiq_platform_check_suspend(state); ++ ++ state->poll_needed = 0; ++ ++ /* Handle service polling and other rare conditions here ++ ** out of the mainline code */ ++ switch (state->conn_state) { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Poll the services as requested */ ++ poll_services(state); ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSING: ++ if (state->is_master) ++ pause_bulks(state); ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), ++ NULL, 0, 0, ++ QMFLAGS_NO_MUTEX_UNLOCK) ++ != VCHIQ_RETRY) { ++ vchiq_set_conn_state(state, ++ VCHIQ_CONNSTATE_PAUSE_SENT); ++ } else { ++ if (state->is_master) ++ resume_bulks(state); ++ /* Retry later */ ++ state->poll_needed = 1; + } -+ mask = ~mask; ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSED: ++ vchiq_platform_resume(state); ++ break; ++ ++ case VCHIQ_CONNSTATE_RESUMING: ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK) ++ != VCHIQ_RETRY) { ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, ++ VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ } else { ++ /* This should really be impossible, ++ ** since the PAUSE should have flushed ++ ** through outstanding messages. */ ++ vchiq_log_error(vchiq_core_log_level, ++ "Failed to send RESUME " ++ "message"); ++ BUG(); ++ } ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSE_TIMEOUT: ++ case VCHIQ_CONNSTATE_RESUME_TIMEOUT: ++ vchiq_platform_handle_timeout(state); ++ break; ++ default: ++ break; + } + - dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, - &bm2835_mmal_ctrl_ops, ctrl->id, -- ctrl->max, ctrl->min, ctrl->def); -+ ctrl->max, mask, ctrl->def); - break; ++ + } - - case MMAL_CONTROL_TYPE_INT_MENU: - dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, -diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h -index 84abbb3..35698c8 100644 ---- a/drivers/media/platform/bcm2835/mmal-common.h -+++ b/drivers/media/platform/bcm2835/mmal-common.h -@@ -26,6 +26,7 @@ - struct mmal_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ -+ int flags; /* v4l2 flags field */ - u32 mmal; - int depth; - u32 mmal_component; /* MMAL component index to be used to encode */ -diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h -index 856e80e..024d620 100644 ---- a/drivers/media/platform/bcm2835/mmal-encodings.h -+++ b/drivers/media/platform/bcm2835/mmal-encodings.h -@@ -12,6 +12,8 @@ - * Simon Mellor - * Luke Diamand - */ -+#ifndef MMAL_ENCODINGS_H -+#define MMAL_ENCODINGS_H - - #define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') - #define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') -@@ -27,6 +29,7 @@ - #define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') - #define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') - #define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') -+#define MMAL_ENCODING_MJPEG MMAL_FOURCC('M', 'J', 'P', 'G') - - #define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') - #define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') -@@ -91,3 +94,34 @@ - #define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') - /** Implicitly delineated NAL units without emulation prevention */ - #define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') + -+ -+/** \defgroup MmalColorSpace List of pre-defined video color spaces -+ * This defines a list of common color spaces. This list isn't exhaustive and -+ * is only provided as a convenience to avoid clients having to use FourCC -+ * codes directly. However components are allowed to define and use their own -+ * FourCC codes. -+ */ -+/* @{ */ -+ -+/** Unknown color space */ -+#define MMAL_COLOR_SPACE_UNKNOWN 0 -+/** ITU-R BT.601-5 [SDTV] */ -+#define MMAL_COLOR_SPACE_ITUR_BT601 MMAL_FOURCC('Y', '6', '0', '1') -+/** ITU-R BT.709-3 [HDTV] */ -+#define MMAL_COLOR_SPACE_ITUR_BT709 MMAL_FOURCC('Y', '7', '0', '9') -+/** JPEG JFIF */ -+#define MMAL_COLOR_SPACE_JPEG_JFIF MMAL_FOURCC('Y', 'J', 'F', 'I') -+/** Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */ -+#define MMAL_COLOR_SPACE_FCC MMAL_FOURCC('Y', 'F', 'C', 'C') -+/** Society of Motion Picture and Television Engineers 240M (1999) */ -+#define MMAL_COLOR_SPACE_SMPTE240M MMAL_FOURCC('Y', '2', '4', '0') -+/** ITU-R BT.470-2 System M */ -+#define MMAL_COLOR_SPACE_BT470_2_M MMAL_FOURCC('Y', '_', '_', 'M') -+/** ITU-R BT.470-2 System BG */ -+#define MMAL_COLOR_SPACE_BT470_2_BG MMAL_FOURCC('Y', '_', 'B', 'G') -+/** JPEG JFIF, but with 16..255 luma */ -+#define MMAL_COLOR_SPACE_JFIF_Y16_255 MMAL_FOURCC('Y', 'Y', '1', '6') -+/* @} MmalColorSpace List */ -+ -+#endif /* MMAL_ENCODINGS_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h -index c611b58..aa0fd18 100644 ---- a/drivers/media/platform/bcm2835/mmal-parameters.h -+++ b/drivers/media/platform/bcm2835/mmal-parameters.h -@@ -57,7 +57,8 @@ enum mmal_parameter_common_type { - MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ - MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ -- MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ -+ MMAL_PARAMETER_SYSTEM_TIME, /**< MMAL_PARAMETER_UINT64_T */ -+ MMAL_PARAMETER_NO_IMAGE_PADDING /**< MMAL_PARAMETER_BOOLEAN_T */ - }; - - /* camera parameters */ -@@ -161,6 +162,13 @@ enum mmal_parameter_camera_type { - MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_SHUTTER_SPEED, /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ -+}; -+ -+struct mmal_parameter_rational { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ - }; - - enum mmal_parameter_camera_config_timestamp_mode { -@@ -176,6 +184,14 @@ enum mmal_parameter_camera_config_timestamp_mode { - */ - }; - -+struct mmal_parameter_fps_range { -+ /**< Low end of the permitted framerate range */ -+ struct mmal_parameter_rational fps_low; -+ /**< High end of the permitted framerate range */ -+ struct mmal_parameter_rational fps_high; -+}; -+ -+ - /* camera configuration parameter */ - struct mmal_parameter_camera_config { - /* Parameters for setting up the image pools */ -@@ -270,6 +286,19 @@ enum mmal_parameter_imagefx { - MMAL_PARAM_IMAGEFX_CARTOON, - }; - -+enum MMAL_PARAM_FLICKERAVOID_T { -+ MMAL_PARAM_FLICKERAVOID_OFF, -+ MMAL_PARAM_FLICKERAVOID_AUTO, -+ MMAL_PARAM_FLICKERAVOID_50HZ, -+ MMAL_PARAM_FLICKERAVOID_60HZ, -+ MMAL_PARAM_FLICKERAVOID_MAX = 0x7FFFFFFF -+}; -+ -+struct mmal_parameter_awbgains { -+ struct mmal_parameter_rational r_gain; /**< Red gain */ -+ struct mmal_parameter_rational b_gain; /**< Blue gain */ -+}; -+ - /** Manner of video rate control */ - enum mmal_parameter_rate_control_mode { - MMAL_VIDEO_RATECONTROL_DEFAULT, -@@ -279,6 +308,85 @@ enum mmal_parameter_rate_control_mode { - MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES - }; - -+enum mmal_video_profile { -+ MMAL_VIDEO_PROFILE_H263_BASELINE, -+ MMAL_VIDEO_PROFILE_H263_H320CODING, -+ MMAL_VIDEO_PROFILE_H263_BACKWARDCOMPATIBLE, -+ MMAL_VIDEO_PROFILE_H263_ISWV2, -+ MMAL_VIDEO_PROFILE_H263_ISWV3, -+ MMAL_VIDEO_PROFILE_H263_HIGHCOMPRESSION, -+ MMAL_VIDEO_PROFILE_H263_INTERNET, -+ MMAL_VIDEO_PROFILE_H263_INTERLACE, -+ MMAL_VIDEO_PROFILE_H263_HIGHLATENCY, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLESCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_CORE, -+ MMAL_VIDEO_PROFILE_MP4V_MAIN, -+ MMAL_VIDEO_PROFILE_MP4V_NBIT, -+ MMAL_VIDEO_PROFILE_MP4V_SCALABLETEXTURE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFACE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFBA, -+ MMAL_VIDEO_PROFILE_MP4V_BASICANIMATED, -+ MMAL_VIDEO_PROFILE_MP4V_HYBRID, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDREALTIME, -+ MMAL_VIDEO_PROFILE_MP4V_CORESCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCODING, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCORE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSIMPLE, -+ MMAL_VIDEO_PROFILE_H264_BASELINE, -+ MMAL_VIDEO_PROFILE_H264_MAIN, -+ MMAL_VIDEO_PROFILE_H264_EXTENDED, -+ MMAL_VIDEO_PROFILE_H264_HIGH, -+ MMAL_VIDEO_PROFILE_H264_HIGH10, -+ MMAL_VIDEO_PROFILE_H264_HIGH422, -+ MMAL_VIDEO_PROFILE_H264_HIGH444, -+ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, -+ MMAL_VIDEO_PROFILE_DUMMY = 0x7FFFFFFF -+}; -+ -+enum mmal_video_level { -+ MMAL_VIDEO_LEVEL_H263_10, -+ MMAL_VIDEO_LEVEL_H263_20, -+ MMAL_VIDEO_LEVEL_H263_30, -+ MMAL_VIDEO_LEVEL_H263_40, -+ MMAL_VIDEO_LEVEL_H263_45, -+ MMAL_VIDEO_LEVEL_H263_50, -+ MMAL_VIDEO_LEVEL_H263_60, -+ MMAL_VIDEO_LEVEL_H263_70, -+ MMAL_VIDEO_LEVEL_MP4V_0, -+ MMAL_VIDEO_LEVEL_MP4V_0b, -+ MMAL_VIDEO_LEVEL_MP4V_1, -+ MMAL_VIDEO_LEVEL_MP4V_2, -+ MMAL_VIDEO_LEVEL_MP4V_3, -+ MMAL_VIDEO_LEVEL_MP4V_4, -+ MMAL_VIDEO_LEVEL_MP4V_4a, -+ MMAL_VIDEO_LEVEL_MP4V_5, -+ MMAL_VIDEO_LEVEL_MP4V_6, -+ MMAL_VIDEO_LEVEL_H264_1, -+ MMAL_VIDEO_LEVEL_H264_1b, -+ MMAL_VIDEO_LEVEL_H264_11, -+ MMAL_VIDEO_LEVEL_H264_12, -+ MMAL_VIDEO_LEVEL_H264_13, -+ MMAL_VIDEO_LEVEL_H264_2, -+ MMAL_VIDEO_LEVEL_H264_21, -+ MMAL_VIDEO_LEVEL_H264_22, -+ MMAL_VIDEO_LEVEL_H264_3, -+ MMAL_VIDEO_LEVEL_H264_31, -+ MMAL_VIDEO_LEVEL_H264_32, -+ MMAL_VIDEO_LEVEL_H264_4, -+ MMAL_VIDEO_LEVEL_H264_41, -+ MMAL_VIDEO_LEVEL_H264_42, -+ MMAL_VIDEO_LEVEL_H264_5, -+ MMAL_VIDEO_LEVEL_H264_51, -+ MMAL_VIDEO_LEVEL_DUMMY = 0x7FFFFFFF -+}; -+ -+struct mmal_parameter_video_profile { -+ enum mmal_video_profile profile; -+ enum mmal_video_level level; -+}; -+ - /* video parameters */ - - enum mmal_parameter_video_type { -@@ -407,7 +515,16 @@ enum mmal_parameter_video_type { - MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, - - /** @ref MMAL_PARAMETER_BYTES_T */ -- MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 -+ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_VCL_HRD_PARAMETERS, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER - }; - - /** Valid mirror modes */ -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c -index a06fb44..76f249e 100644 ---- a/drivers/media/platform/bcm2835/mmal-vchiq.c -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.c -@@ -742,7 +742,7 @@ static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, - return ret; - } - -- ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); -+ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, 3*HZ); - if (ret <= 0) { - pr_err("error %d waiting for sync completion\n", ret); - if (ret == 0) -@@ -1326,7 +1326,7 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, - memcpy(value, &rmsg->u.port_parameter_get_reply.value, - rmsg->u.port_parameter_get_reply.size); - -- pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, -+ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, - ret, port->component->handle, port->handle, parameter_id); - - release_msg: - -From 62a271e1a6bbd67590b5f7a03cc0f23ec00c3e1c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:22:53 +0100 -Subject: [PATCH 030/172] dmaengine: Add support for BCM2708 - -Add support for DMA controller of BCM2708 as used in the Raspberry Pi. -Currently it only supports cyclic DMA. - -Signed-off-by: Florian Meier - -dmaengine: expand functionality by supporting scatter/gather transfers sdhci-bcm2708 and dma.c: fix for LITE channels - -DMA: fix cyclic LITE length overflow bug ---- - arch/arm/mach-bcm2708/dma.c | 2 + - arch/arm/mach-bcm2708/include/mach/dma.h | 6 +- - drivers/dma/Kconfig | 6 + - drivers/dma/Makefile | 1 + - drivers/dma/bcm2708-dmaengine.c | 1052 ++++++++++++++++++++++++++++++ - 5 files changed, 1066 insertions(+), 1 deletion(-) - create mode 100644 drivers/dma/bcm2708-dmaengine.c - -diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c -index 1da2413..a5e58d1 100644 ---- a/arch/arm/mach-bcm2708/dma.c -+++ b/arch/arm/mach-bcm2708/dma.c -@@ -156,6 +156,8 @@ static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, - dmaman->chan_available = chans_available; - dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ - dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ - } - - static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, -diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h -index a4aac4c..d03e7b5 100644 ---- a/arch/arm/mach-bcm2708/include/mach/dma.h -+++ b/arch/arm/mach-bcm2708/include/mach/dma.h -@@ -77,9 +77,13 @@ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); - those with higher priority smaller ordinal numbers */ - #define BCM_DMA_FEATURE_FAST_ORD 0 - #define BCM_DMA_FEATURE_BULK_ORD 1 -+#define BCM_DMA_FEATURE_NORMAL_ORD 2 -+#define BCM_DMA_FEATURE_LITE_ORD 3 - #define BCM_DMA_FEATURE_FAST (1< -+ * Gellert Weisz -+ * Copyright 2013-2014 -+ * -+ * Based on -+ * OMAP DMAengine support by Russell King -+ * -+ * BCM2708 DMA Driver -+ * Copyright (C) 2010 Broadcom -+ * -+ * Raspberry Pi PCM I2S ALSA Driver -+ * Copyright (c) by Phil Poole 2013 -+ * -+ * MARVELL MMP Peripheral DMA Driver -+ * Copyright 2012 Marvell International Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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 -+#include -+#include -+#include -+#include -+ -+#ifndef CONFIG_OF -+ -+/* dma manager */ -+#include -+ -+//#define DMA_COMPLETE DMA_SUCCESS -+ -+#endif -+ -+#include -+#include -+ -+#include "virt-dma.h" -+ -+ -+struct bcm2835_dmadev { -+ struct dma_device ddev; -+ spinlock_t lock; -+ void __iomem *base; -+ struct device_dma_parameters dma_parms; -+}; -+ -+struct bcm2835_dma_cb { -+ uint32_t info; -+ uint32_t src; -+ uint32_t dst; -+ uint32_t length; -+ uint32_t stride; -+ uint32_t next; -+ uint32_t pad[2]; -+}; -+ -+struct bcm2835_chan { -+ struct virt_dma_chan vc; -+ struct list_head node; -+ -+ struct dma_slave_config cfg; -+ bool cyclic; -+ -+ int ch; -+ struct bcm2835_desc *desc; -+ -+ void __iomem *chan_base; -+ int irq_number; -+ -+ unsigned int dreq; -+}; -+ -+struct bcm2835_desc { -+ struct virt_dma_desc vd; -+ enum dma_transfer_direction dir; -+ -+ unsigned int control_block_size; -+ struct bcm2835_dma_cb *control_block_base; -+ dma_addr_t control_block_base_phys; -+ -+ unsigned int frames; -+ size_t size; -+}; -+ -+#define BCM2835_DMA_CS 0x00 -+#define BCM2835_DMA_ADDR 0x04 -+#define BCM2835_DMA_SOURCE_AD 0x0c -+#define BCM2835_DMA_DEST_AD 0x10 -+#define BCM2835_DMA_NEXTCB 0x1C -+ -+/* DMA CS Control and Status bits */ -+#define BCM2835_DMA_ACTIVE BIT(0) -+#define BCM2835_DMA_INT BIT(2) -+#define BCM2835_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ -+#define BCM2835_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ -+#define BCM2835_DMA_ERR BIT(8) -+#define BCM2835_DMA_ABORT BIT(30) /* Stop current CB, go to next, WO */ -+#define BCM2835_DMA_RESET BIT(31) /* WO, self clearing */ -+ -+#define BCM2835_DMA_INT_EN BIT(0) -+#define BCM2835_DMA_WAIT_RESP BIT(3) -+#define BCM2835_DMA_D_INC BIT(4) -+#define BCM2835_DMA_D_WIDTH BIT(5) -+#define BCM2835_DMA_D_DREQ BIT(6) -+#define BCM2835_DMA_S_INC BIT(8) -+#define BCM2835_DMA_S_WIDTH BIT(9) -+#define BCM2835_DMA_S_DREQ BIT(10) -+ -+#define BCM2835_DMA_PER_MAP(x) ((x) << 16) -+#define BCM2835_DMA_WAITS(x) (((x)&0x1f) << 21) -+ -+#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ -+ -+#define BCM2835_DMA_DATA_TYPE_S8 1 -+#define BCM2835_DMA_DATA_TYPE_S16 2 -+#define BCM2835_DMA_DATA_TYPE_S32 4 -+#define BCM2835_DMA_DATA_TYPE_S128 16 -+ -+#define BCM2835_DMA_BULK_MASK BIT(0) -+#define BCM2835_DMA_FIQ_MASK (BIT(2) | BIT(3)) -+ -+ -+/* Valid only for channels 0 - 14, 15 has its own base address */ -+#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ -+#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) -+ -+#define MAX_LITE_TRANSFER 32768 -+#define MAX_NORMAL_TRANSFER 1073741824 -+ -+static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d) -+{ -+ return container_of(d, struct bcm2835_dmadev, ddev); -+} -+ -+static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c) -+{ -+ return container_of(c, struct bcm2835_chan, vc.chan); -+} -+ -+static inline struct bcm2835_desc *to_bcm2835_dma_desc( -+ struct dma_async_tx_descriptor *t) -+{ -+ return container_of(t, struct bcm2835_desc, vd.tx); -+} -+ -+static void dma_dumpregs(struct bcm2835_chan *c) -+{ -+ pr_debug("-------------DMA DUMPREGS-------------\n"); -+ pr_debug("CS= %u\n", -+ readl(c->chan_base + BCM2835_DMA_CS)); -+ pr_debug("ADDR= %u\n", -+ readl(c->chan_base + BCM2835_DMA_ADDR)); -+ pr_debug("SOURCE_ADDR= %u\n", -+ readl(c->chan_base + BCM2835_DMA_SOURCE_AD)); -+ pr_debug("DEST_AD= %u\n", -+ readl(c->chan_base + BCM2835_DMA_DEST_AD)); -+ pr_debug("NEXTCB= %u\n", -+ readl(c->chan_base + BCM2835_DMA_NEXTCB)); -+ pr_debug("--------------------------------------\n"); -+} -+ -+static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) -+{ -+ struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd); -+ dma_free_coherent(desc->vd.tx.chan->device->dev, -+ desc->control_block_size, -+ desc->control_block_base, -+ desc->control_block_base_phys); -+ kfree(desc); -+} -+ -+static int bcm2835_dma_abort(void __iomem *chan_base) -+{ -+ unsigned long cs; -+ long int timeout = 10000; -+ -+ cs = readl(chan_base + BCM2835_DMA_CS); -+ if (!(cs & BCM2835_DMA_ACTIVE)) -+ return 0; -+ -+ /* Write 0 to the active bit - Pause the DMA */ -+ writel(0, chan_base + BCM2835_DMA_CS); -+ -+ /* Wait for any current AXI transfer to complete */ -+ while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) { -+ cpu_relax(); -+ cs = readl(chan_base + BCM2835_DMA_CS); ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ parse_rx_slots(state); + } ++ return 0; ++} + -+ /* We'll un-pause when we set of our next DMA */ -+ if (!timeout) -+ return -ETIMEDOUT; + -+ if (!(cs & BCM2835_DMA_ACTIVE)) -+ return 0; ++/* Called by the recycle thread */ ++static int ++recycle_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; + -+ /* Terminate the control block chain */ -+ writel(0, chan_base + BCM2835_DMA_NEXTCB); ++ while (1) { ++ remote_event_wait(&local->recycle); + -+ /* Abort the whole DMA */ -+ writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, -+ chan_base + BCM2835_DMA_CS); ++ process_free_queue(state); ++ } ++ return 0; ++} ++ ++ ++/* Called by the sync thread */ ++static int ++sync_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ state->remote->slot_sync); ++ ++ while (1) { ++ VCHIQ_SERVICE_T *service; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; ++ ++ remote_event_wait(&local->sync_trigger); ++ ++ rmb(); ++ ++ msgid = header->msgid; ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); ++ ++ service = find_service_by_port(state, localport); ++ ++ if (!service) { ++ vchiq_log_error(vchiq_sync_log_level, ++ "%d: sf %s@%x (%d->%d) - " ++ "invalid/closed service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, localport); ++ release_message_sync(state, header); ++ continue; ++ } ++ ++ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ vchiq_log_trace(vchiq_sync_log_level, ++ "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d", ++ msg_type_str(type), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ remoteport, localport, size); ++ if (size > 0) ++ vchiq_log_dump_mem("Rcvd", 0, header->data, ++ min(64, size)); ++ } ++ ++ switch (type) { ++ case VCHIQ_MSG_OPENACK: ++ if (size >= sizeof(struct vchiq_openack_payload)) { ++ const struct vchiq_openack_payload *payload = ++ (struct vchiq_openack_payload *) ++ header->data; ++ service->peer_version = payload->version; ++ } ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: sf OPENACK@%x,%x (%d->%d) v:%d", ++ state->id, (unsigned int)header, size, ++ remoteport, localport, service->peer_version); ++ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { ++ service->remoteport = remoteport; ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_OPENSYNC); ++ service->sync = 1; ++ up(&service->remove_event); ++ } ++ release_message_sync(state, header); ++ break; ++ ++ case VCHIQ_MSG_DATA: ++ vchiq_log_trace(vchiq_sync_log_level, ++ "%d: sf DATA@%x,%x (%d->%d)", ++ state->id, (unsigned int)header, size, ++ remoteport, localport); ++ ++ if ((service->remoteport == remoteport) && ++ (service->srvstate == ++ VCHIQ_SRVSTATE_OPENSYNC)) { ++ if (make_service_callback(service, ++ VCHIQ_MESSAGE_AVAILABLE, header, ++ NULL) == VCHIQ_RETRY) ++ vchiq_log_error(vchiq_sync_log_level, ++ "synchronous callback to " ++ "service %d returns " ++ "VCHIQ_RETRY", ++ localport); ++ } ++ break; ++ ++ default: ++ vchiq_log_error(vchiq_sync_log_level, ++ "%d: sf unexpected msgid %x@%x,%x", ++ state->id, msgid, (unsigned int)header, size); ++ release_message_sync(state, header); ++ break; ++ } ++ ++ unlock_service(service); ++ } + + return 0; +} + + -+static void bcm2835_dma_start_desc(struct bcm2835_chan *c) ++static void ++init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) +{ -+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc); -+ struct bcm2835_desc *d; ++ queue->local_insert = 0; ++ queue->remote_insert = 0; ++ queue->process = 0; ++ queue->remote_notify = 0; ++ queue->remove = 0; ++} ++ ++ ++inline const char * ++get_conn_state_name(VCHIQ_CONNSTATE_T conn_state) ++{ ++ return conn_state_names[conn_state]; ++} ++ ++ ++VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size) ++{ ++ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK; ++ VCHIQ_SLOT_ZERO_T *slot_zero = ++ (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align); ++ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE; ++ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS; ++ ++ /* Ensure there is enough memory to run an absolutely minimum system */ ++ num_slots -= first_data_slot; ++ ++ if (num_slots < 4) { ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_init_slots - insufficient memory %x bytes", ++ mem_size); ++ return NULL; ++ } ++ ++ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T)); ++ ++ slot_zero->magic = VCHIQ_MAGIC; ++ slot_zero->version = VCHIQ_VERSION; ++ slot_zero->version_min = VCHIQ_VERSION_MIN; ++ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T); ++ slot_zero->slot_size = VCHIQ_SLOT_SIZE; ++ slot_zero->max_slots = VCHIQ_MAX_SLOTS; ++ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE; ++ ++ slot_zero->master.slot_sync = first_data_slot; ++ slot_zero->master.slot_first = first_data_slot + 1; ++ slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1; ++ slot_zero->slave.slot_sync = first_data_slot + (num_slots/2); ++ slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1; ++ slot_zero->slave.slot_last = first_data_slot + num_slots - 1; ++ ++ return slot_zero; ++} ++ ++VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, ++ int is_master) ++{ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_STATUS_T status; ++ char threadname[10]; ++ static int id; ++ int i; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%s: slot_zero = 0x%08lx, is_master = %d", ++ __func__, (unsigned long)slot_zero, is_master); ++ ++ /* Check the input configuration */ ++ ++ if (slot_zero->magic != VCHIQ_MAGIC) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Invalid VCHIQ magic value found."); ++ vchiq_loud_error("slot_zero=%x: magic=%x (expected %x)", ++ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->version < VCHIQ_VERSION_MIN) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Incompatible VCHIQ versions found."); ++ vchiq_loud_error("slot_zero=%x: VideoCore version=%d " ++ "(minimum %d)", ++ (unsigned int)slot_zero, slot_zero->version, ++ VCHIQ_VERSION_MIN); ++ vchiq_loud_error("Restart with a newer VideoCore image."); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (VCHIQ_VERSION < slot_zero->version_min) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Incompatible VCHIQ versions found."); ++ vchiq_loud_error("slot_zero=%x: version=%d (VideoCore " ++ "minimum %d)", ++ (unsigned int)slot_zero, VCHIQ_VERSION, ++ slot_zero->version_min); ++ vchiq_loud_error("Restart with a newer kernel."); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) || ++ (slot_zero->slot_size != VCHIQ_SLOT_SIZE) || ++ (slot_zero->max_slots != VCHIQ_MAX_SLOTS) || ++ (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) { ++ vchiq_loud_error_header(); ++ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) ++ vchiq_loud_error("slot_zero=%x: slot_zero_size=%x " ++ "(expected %x)", ++ (unsigned int)slot_zero, ++ slot_zero->slot_zero_size, ++ sizeof(VCHIQ_SLOT_ZERO_T)); ++ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE) ++ vchiq_loud_error("slot_zero=%x: slot_size=%d " ++ "(expected %d", ++ (unsigned int)slot_zero, slot_zero->slot_size, ++ VCHIQ_SLOT_SIZE); ++ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS) ++ vchiq_loud_error("slot_zero=%x: max_slots=%d " ++ "(expected %d)", ++ (unsigned int)slot_zero, slot_zero->max_slots, ++ VCHIQ_MAX_SLOTS); ++ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE) ++ vchiq_loud_error("slot_zero=%x: max_slots_per_side=%d " ++ "(expected %d)", ++ (unsigned int)slot_zero, ++ slot_zero->max_slots_per_side, ++ VCHIQ_MAX_SLOTS_PER_SIDE); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (VCHIQ_VERSION < slot_zero->version) ++ slot_zero->version = VCHIQ_VERSION; ++ ++ if (is_master) { ++ local = &slot_zero->master; ++ remote = &slot_zero->slave; ++ } else { ++ local = &slot_zero->slave; ++ remote = &slot_zero->master; ++ } ++ ++ if (local->initialised) { ++ vchiq_loud_error_header(); ++ if (remote->initialised) ++ vchiq_loud_error("local state has already been " ++ "initialised"); ++ else ++ vchiq_loud_error("master/slave mismatch - two %ss", ++ is_master ? "master" : "slave"); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ memset(state, 0, sizeof(VCHIQ_STATE_T)); ++ ++ state->id = id++; ++ state->is_master = is_master; ++ ++ /* ++ initialize shared state pointers ++ */ ++ ++ state->local = local; ++ state->remote = remote; ++ state->slot_data = (VCHIQ_SLOT_T *)slot_zero; ++ ++ /* ++ initialize events and mutexes ++ */ ++ ++ sema_init(&state->connect, 0); ++ mutex_init(&state->mutex); ++ sema_init(&state->trigger_event, 0); ++ sema_init(&state->recycle_event, 0); ++ sema_init(&state->sync_trigger_event, 0); ++ sema_init(&state->sync_release_event, 0); ++ ++ mutex_init(&state->slot_mutex); ++ mutex_init(&state->recycle_mutex); ++ mutex_init(&state->sync_mutex); ++ mutex_init(&state->bulk_transfer_mutex); ++ ++ sema_init(&state->slot_available_event, 0); ++ sema_init(&state->slot_remove_event, 0); ++ sema_init(&state->data_quota_event, 0); ++ ++ state->slot_queue_available = 0; ++ ++ for (i = 0; i < VCHIQ_MAX_SERVICES; i++) { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[i]; ++ sema_init(&service_quota->quota_event, 0); ++ } ++ ++ for (i = local->slot_first; i <= local->slot_last; i++) { ++ local->slot_queue[state->slot_queue_available++] = i; ++ up(&state->slot_available_event); ++ } ++ ++ state->default_slot_quota = state->slot_queue_available/2; ++ state->default_message_quota = ++ min((unsigned short)(state->default_slot_quota * 256), ++ (unsigned short)~0); ++ ++ state->previous_data_index = -1; ++ state->data_use_count = 0; ++ state->data_quota = state->slot_queue_available - 1; ++ ++ local->trigger.event = &state->trigger_event; ++ remote_event_create(&local->trigger); ++ local->tx_pos = 0; ++ ++ local->recycle.event = &state->recycle_event; ++ remote_event_create(&local->recycle); ++ local->slot_queue_recycle = state->slot_queue_available; ++ ++ local->sync_trigger.event = &state->sync_trigger_event; ++ remote_event_create(&local->sync_trigger); ++ ++ local->sync_release.event = &state->sync_release_event; ++ remote_event_create(&local->sync_release); ++ ++ /* At start-of-day, the slot is empty and available */ ++ ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid ++ = VCHIQ_MSGID_PADDING; ++ remote_event_signal_local(&local->sync_release); ++ ++ local->debug[DEBUG_ENTRIES] = DEBUG_MAX; ++ ++ status = vchiq_platform_init_state(state); ++ ++ /* ++ bring up slot handler thread ++ */ ++ snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); ++ state->slot_handler_thread = kthread_create(&slot_handler_func, ++ (void *)state, ++ threadname); ++ ++ if (state->slot_handler_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->slot_handler_thread, -19); ++ wake_up_process(state->slot_handler_thread); ++ ++ snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); ++ state->recycle_thread = kthread_create(&recycle_func, ++ (void *)state, ++ threadname); ++ if (state->recycle_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->recycle_thread, -19); ++ wake_up_process(state->recycle_thread); ++ ++ snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id); ++ state->sync_thread = kthread_create(&sync_func, ++ (void *)state, ++ threadname); ++ if (state->sync_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->sync_thread, -20); ++ wake_up_process(state->sync_thread); ++ ++ BUG_ON(state->id >= VCHIQ_MAX_STATES); ++ vchiq_states[state->id] = state; ++ ++ /* Indicate readiness to the other side */ ++ local->initialised = 1; ++ ++ return status; ++} ++ ++/* Called from application thread when a client or server service is created. */ ++VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term) ++{ ++ VCHIQ_SERVICE_T *service; ++ ++ service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL); ++ if (service) { ++ service->base.fourcc = params->fourcc; ++ service->base.callback = params->callback; ++ service->base.userdata = params->userdata; ++ service->handle = VCHIQ_SERVICE_HANDLE_INVALID; ++ service->ref_count = 1; ++ service->srvstate = VCHIQ_SRVSTATE_FREE; ++ service->userdata_term = userdata_term; ++ service->localport = VCHIQ_PORT_FREE; ++ service->remoteport = VCHIQ_PORT_FREE; ++ ++ service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? ++ VCHIQ_FOURCC_INVALID : params->fourcc; ++ service->client_id = 0; ++ service->auto_close = 1; ++ service->sync = 0; ++ service->closing = 0; ++ service->trace = 0; ++ atomic_set(&service->poll_flags, 0); ++ service->version = params->version; ++ service->version_min = params->version_min; ++ service->state = state; ++ service->instance = instance; ++ service->service_use_count = 0; ++ init_bulk_queue(&service->bulk_tx); ++ init_bulk_queue(&service->bulk_rx); ++ sema_init(&service->remove_event, 0); ++ sema_init(&service->bulk_remove_event, 0); ++ mutex_init(&service->bulk_mutex); ++ memset(&service->stats, 0, sizeof(service->stats)); ++ } else { ++ vchiq_log_error(vchiq_core_log_level, ++ "Out of memory"); ++ } ++ ++ if (service) { ++ VCHIQ_SERVICE_T **pservice = NULL; ++ int i; ++ ++ /* Although it is perfectly possible to use service_spinlock ++ ** to protect the creation of services, it is overkill as it ++ ** disables interrupts while the array is searched. ++ ** The only danger is of another thread trying to create a ++ ** service - service deletion is safe. ++ ** Therefore it is preferable to use state->mutex which, ++ ** although slower to claim, doesn't block interrupts while ++ ** it is held. ++ */ ++ ++ mutex_lock(&state->mutex); ++ ++ /* Prepare to use a previously unused service */ ++ if (state->unused_service < VCHIQ_MAX_SERVICES) ++ pservice = &state->services[state->unused_service]; ++ ++ if (srvstate == VCHIQ_SRVSTATE_OPENING) { ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) { ++ pservice = &state->services[i]; ++ break; ++ } ++ } ++ } else { ++ for (i = (state->unused_service - 1); i >= 0; i--) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) ++ pservice = &state->services[i]; ++ else if ((srv->public_fourcc == params->fourcc) ++ && ((srv->instance != instance) || ++ (srv->base.callback != ++ params->callback))) { ++ /* There is another server using this ++ ** fourcc which doesn't match. */ ++ pservice = NULL; ++ break; ++ } ++ } ++ } ++ ++ if (pservice) { ++ service->localport = (pservice - state->services); ++ if (!handle_seq) ++ handle_seq = VCHIQ_MAX_STATES * ++ VCHIQ_MAX_SERVICES; ++ service->handle = handle_seq | ++ (state->id * VCHIQ_MAX_SERVICES) | ++ service->localport; ++ handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; ++ *pservice = service; ++ if (pservice == &state->services[state->unused_service]) ++ state->unused_service++; ++ } ++ ++ mutex_unlock(&state->mutex); ++ ++ if (!pservice) { ++ kfree(service); ++ service = NULL; ++ } ++ } ++ ++ if (service) { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[service->localport]; ++ service_quota->slot_quota = state->default_slot_quota; ++ service_quota->message_quota = state->default_message_quota; ++ if (service_quota->slot_use_count == 0) ++ service_quota->previous_tx_index = ++ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) ++ - 1; ++ ++ /* Bring this service online */ ++ vchiq_set_service_state(service, srvstate); ++ ++ vchiq_log_info(vchiq_core_msg_log_level, ++ "%s Service %c%c%c%c SrcPort:%d", ++ (srvstate == VCHIQ_SRVSTATE_OPENING) ++ ? "Open" : "Add", ++ VCHIQ_FOURCC_AS_4CHARS(params->fourcc), ++ service->localport); ++ } ++ ++ /* Don't unlock the service - leave it with a ref_count of 1. */ ++ ++ return service; ++} ++ ++VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) ++{ ++ struct vchiq_open_payload payload = { ++ service->base.fourcc, ++ client_id, ++ service->version, ++ service->version_min ++ }; ++ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) }; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ service->client_id = client_id; ++ vchiq_use_service_internal(service); ++ status = queue_message(service->state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), ++ &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING); ++ if (status == VCHIQ_SUCCESS) { ++ /* Wait for the ACK/NAK */ ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ vchiq_release_service_internal(service); ++ } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && ++ (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { ++ if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: osi - srvstate = %s (ref %d)", ++ service->state->id, ++ srvstate_names[service->srvstate], ++ service->ref_count); ++ status = VCHIQ_ERROR; ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ vchiq_release_service_internal(service); ++ } ++ } ++ return status; ++} ++ ++static void ++release_service_messages(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int slot_last = state->remote->slot_last; ++ int i; ++ ++ /* Release any claimed messages aimed at this service */ ++ ++ if (service->sync) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ state->remote->slot_sync); ++ if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport) ++ release_message_sync(state, header); + -+ if (!vd) { -+ c->desc = NULL; + return; + } + -+ list_del(&vd->node); ++ for (i = state->remote->slot_first; i <= slot_last; i++) { ++ VCHIQ_SLOT_INFO_T *slot_info = ++ SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info->release_count != slot_info->use_count) { ++ char *data = ++ (char *)SLOT_DATA_FROM_INDEX(state, i); ++ unsigned int pos, end; + -+ c->desc = d = to_bcm2835_dma_desc(&vd->tx); ++ end = VCHIQ_SLOT_SIZE; ++ if (data == state->rx_data) ++ /* This buffer is still being read from - stop ++ ** at the current read position */ ++ end = state->rx_pos & VCHIQ_SLOT_MASK; + -+ writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR); -+ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); -+ -+} -+ -+static irqreturn_t bcm2835_dma_callback(int irq, void *data) -+{ -+ struct bcm2835_chan *c = data; -+ struct bcm2835_desc *d; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ -+ /* Acknowledge interrupt */ -+ writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); -+ -+ d = c->desc; -+ -+ if (d) { -+ if (c->cyclic) { -+ vchan_cyclic_callback(&d->vd); -+ -+ /* Keep the DMA engine running */ -+ writel(BCM2835_DMA_ACTIVE, -+ c->chan_base + BCM2835_DMA_CS); -+ -+ } else { -+ vchan_cookie_complete(&c->desc->vd); -+ bcm2835_dma_start_desc(c); -+ } -+ } -+ -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ -+ return IRQ_HANDLED; -+} -+ -+static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ int ret; -+ -+ dev_dbg(c->vc.chan.device->dev, -+ "Allocating DMA channel %d\n", c->ch); -+ -+ ret = request_irq(c->irq_number, -+ bcm2835_dma_callback, 0, "DMA IRQ", c); -+ -+ return ret; -+} -+ -+static void bcm2835_dma_free_chan_resources(struct dma_chan *chan) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ -+ vchan_free_chan_resources(&c->vc); -+ free_irq(c->irq_number, c); -+ -+ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); -+} -+ -+static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d) -+{ -+ return d->size; -+} -+ -+static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) -+{ -+ unsigned int i; -+ size_t size; -+ -+ for (size = i = 0; i < d->frames; i++) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[i]; -+ size_t this_size = control_block->length; -+ dma_addr_t dma; -+ -+ if (d->dir == DMA_DEV_TO_MEM) -+ dma = control_block->dst; -+ else -+ dma = control_block->src; -+ -+ if (size) -+ size += this_size; -+ else if (addr >= dma && addr < dma + this_size) -+ size += dma + this_size - addr; -+ } -+ -+ return size; -+} -+ -+static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, -+ dma_cookie_t cookie, struct dma_tx_state *txstate) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ struct bcm2835_desc *d; -+ struct virt_dma_desc *vd; -+ enum dma_status ret; -+ unsigned long flags; -+ dma_addr_t pos; -+ -+ ret = dma_cookie_status(chan, cookie, txstate); -+ if (ret == DMA_COMPLETE || !txstate) -+ return ret; -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ vd = vchan_find_desc(&c->vc, cookie); -+ if (vd) { -+ txstate->residue = -+ bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx)); -+ } else if (c->desc && c->desc->vd.tx.cookie == cookie) { -+ d = c->desc; -+ -+ if (d->dir == DMA_MEM_TO_DEV) -+ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); -+ else if (d->dir == DMA_DEV_TO_MEM) -+ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); -+ else + pos = 0; + -+ txstate->residue = bcm2835_dma_desc_size_pos(d, pos); -+ } else { -+ txstate->residue = 0; ++ while (pos < end) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ int port = VCHIQ_MSG_DSTPORT(msgid); ++ if ((port == service->localport) && ++ (msgid & VCHIQ_MSGID_CLAIMED)) { ++ vchiq_log_info(vchiq_core_log_level, ++ " fsi - hdr %x", ++ (unsigned int)header); ++ release_slot(state, slot_info, header, ++ NULL); ++ } ++ pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "fsi - pos %x: header %x, " ++ "msgid %x, header->msgid %x, " ++ "header->size %x", ++ pos, (unsigned int)header, ++ msgid, header->msgid, ++ header->size); ++ WARN(1, "invalid slot position\n"); ++ } ++ } ++ } + } -+ -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ -+ return ret; +} + -+static void bcm2835_dma_issue_pending(struct dma_chan *chan) ++static int ++do_abort_bulks(VCHIQ_SERVICE_T *service) +{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ unsigned long flags; ++ VCHIQ_STATUS_T status; + -+ spin_lock_irqsave(&c->vc.lock, flags); -+ if (vchan_issue_pending(&c->vc) && !c->desc) -+ bcm2835_dma_start_desc(c); ++ /* Abort any outstanding bulk transfers */ ++ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) ++ return 0; ++ abort_outstanding_bulks(service, &service->bulk_tx); ++ abort_outstanding_bulks(service, &service->bulk_rx); ++ mutex_unlock(&service->bulk_mutex); + -+ spin_unlock_irqrestore(&c->vc.lock, flags); ++ status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/); ++ if (status == VCHIQ_SUCCESS) ++ status = notify_bulks(service, &service->bulk_rx, ++ 0/*!retry_poll*/); ++ return (status == VCHIQ_SUCCESS); +} + -+static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( -+ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, -+ size_t period_len, enum dma_transfer_direction direction, -+ unsigned long flags) ++static VCHIQ_STATUS_T ++close_service_complete(VCHIQ_SERVICE_T *service, int failstate) +{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ enum dma_slave_buswidth dev_width; -+ struct bcm2835_desc *d; -+ dma_addr_t dev_addr; -+ unsigned int es, sync_type; -+ unsigned int frame, max_size; ++ VCHIQ_STATUS_T status; ++ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); ++ int newstate; + -+ /* Grab configuration */ -+ if (!is_slave_direction(direction)) { -+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); -+ return NULL; -+ } -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ dev_addr = c->cfg.src_addr; -+ dev_width = c->cfg.src_addr_width; -+ sync_type = BCM2835_DMA_S_DREQ; -+ } else { -+ dev_addr = c->cfg.dst_addr; -+ dev_width = c->cfg.dst_addr_width; -+ sync_type = BCM2835_DMA_D_DREQ; -+ } -+ -+ /* Bus width translates to the element size (ES) */ -+ switch (dev_width) { -+ case DMA_SLAVE_BUSWIDTH_4_BYTES: -+ es = BCM2835_DMA_DATA_TYPE_S32; ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPEN: ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ case VCHIQ_SRVSTATE_CLOSERECVD: ++ if (is_server) { ++ if (service->auto_close) { ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; ++ newstate = VCHIQ_SRVSTATE_LISTENING; ++ } else ++ newstate = VCHIQ_SRVSTATE_CLOSEWAIT; ++ } else ++ newstate = VCHIQ_SRVSTATE_CLOSED; ++ vchiq_set_service_state(service, newstate); ++ break; ++ case VCHIQ_SRVSTATE_LISTENING: + break; + default: -+ return NULL; ++ vchiq_log_error(vchiq_core_log_level, ++ "close_service_complete(%x) called in state %s", ++ service->handle, srvstate_names[service->srvstate]); ++ WARN(1, "close_service_complete in unexpected state\n"); ++ return VCHIQ_ERROR; + } + -+ /* Now allocate and setup the descriptor. */ -+ d = kzalloc(sizeof(*d), GFP_NOWAIT); -+ if (!d) -+ return NULL; ++ status = make_service_callback(service, ++ VCHIQ_SERVICE_CLOSED, NULL, NULL); + -+ d->dir = direction; ++ if (status != VCHIQ_RETRY) { ++ int uc = service->service_use_count; ++ int i; ++ /* Complete the close process */ ++ for (i = 0; i < uc; i++) ++ /* cater for cases where close is forced and the ++ ** client may not close all it's handles */ ++ vchiq_release_service_internal(service); + -+ if (c->ch >= 8) /* we have a LITE channel */ -+ max_size = MAX_LITE_TRANSFER; -+ else -+ max_size = MAX_NORMAL_TRANSFER; -+ period_len = min(period_len, max_size); ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; + -+ d->frames = (buf_len-1) / period_len + 1; ++ if (service->srvstate == VCHIQ_SRVSTATE_CLOSED) ++ vchiq_free_service_internal(service); ++ else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) { ++ if (is_server) ++ service->closing = 0; + -+ /* Allocate memory for control blocks */ -+ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); -+ d->control_block_base = dma_zalloc_coherent(chan->device->dev, -+ d->control_block_size, &d->control_block_base_phys, -+ GFP_NOWAIT); ++ up(&service->remove_event); ++ } ++ } else ++ vchiq_set_service_state(service, failstate); + -+ if (!d->control_block_base) { -+ kfree(d); -+ return NULL; -+ } ++ return status; ++} + -+ /* -+ * Iterate over all frames, create a control block -+ * for each frame and link them together. -+ */ -+ for (frame = 0; frame < d->frames; frame++) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[frame]; ++/* Called by the slot handler */ ++VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); + -+ /* Setup adresses */ -+ if (d->dir == DMA_DEV_TO_MEM) { -+ control_block->info = BCM2835_DMA_D_INC; -+ control_block->src = dev_addr; -+ control_block->dst = buf_addr + frame * period_len; ++ vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)", ++ service->state->id, service->localport, close_recvd, ++ srvstate_names[service->srvstate]); ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_CLOSED: ++ case VCHIQ_SRVSTATE_HIDDEN: ++ case VCHIQ_SRVSTATE_LISTENING: ++ case VCHIQ_SRVSTATE_CLOSEWAIT: ++ if (close_recvd) ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_close_service_internal(1) called " ++ "in state %s", ++ srvstate_names[service->srvstate]); ++ else if (is_server) { ++ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { ++ status = VCHIQ_ERROR; ++ } else { ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; ++ if (service->srvstate == ++ VCHIQ_SRVSTATE_CLOSEWAIT) ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ } ++ up(&service->remove_event); ++ } else ++ vchiq_free_service_internal(service); ++ break; ++ case VCHIQ_SRVSTATE_OPENING: ++ if (close_recvd) { ++ /* The open was rejected - tell the user */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_CLOSEWAIT); ++ up(&service->remove_event); + } else { -+ control_block->info = BCM2835_DMA_S_INC; -+ control_block->src = buf_addr + frame * period_len; -+ control_block->dst = dev_addr; ++ /* Shutdown mid-open - let the other side know */ ++ status = queue_message(state, service, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, 0); + } -+ -+ /* Enable interrupt */ -+ control_block->info |= BCM2835_DMA_INT_EN; -+ -+ /* Setup synchronization */ -+ if (sync_type != 0) -+ control_block->info |= sync_type; -+ -+ /* Setup DREQ channel */ -+ if (c->cfg.slave_id != 0) -+ control_block->info |= -+ BCM2835_DMA_PER_MAP(c->cfg.slave_id); -+ -+ /* Length of a frame */ -+ if (frame != d->frames-1) -+ control_block->length = period_len; -+ else -+ control_block->length = buf_len - (d->frames - 1) * period_len; -+ -+ d->size += control_block->length; -+ -+ /* -+ * Next block is the next frame. -+ * This function is called on cyclic DMA transfers. -+ * Therefore, wrap around at number of frames. -+ */ -+ control_block->next = d->control_block_base_phys + -+ sizeof(struct bcm2835_dma_cb) -+ * ((frame + 1) % d->frames); -+ } -+ -+ c->cyclic = true; -+ -+ return vchan_tx_prep(&c->vc, &d->vd, flags); -+} -+ -+ -+static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( -+ struct dma_chan *chan, struct scatterlist *sgl, -+ unsigned int sg_len, enum dma_transfer_direction direction, -+ unsigned long flags, void *context) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ enum dma_slave_buswidth dev_width; -+ struct bcm2835_desc *d; -+ dma_addr_t dev_addr; -+ struct scatterlist *sgent; -+ unsigned int es, sync_type; -+ unsigned int i, j, splitct, max_size; -+ -+ if (!is_slave_direction(direction)) { -+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); -+ return NULL; -+ } -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ dev_addr = c->cfg.src_addr; -+ dev_width = c->cfg.src_addr_width; -+ sync_type = BCM2835_DMA_S_DREQ; -+ } else { -+ dev_addr = c->cfg.dst_addr; -+ dev_width = c->cfg.dst_addr_width; -+ sync_type = BCM2835_DMA_D_DREQ; -+ } -+ -+ /* Bus width translates to the element size (ES) */ -+ switch (dev_width) { -+ case DMA_SLAVE_BUSWIDTH_4_BYTES: -+ es = BCM2835_DMA_DATA_TYPE_S32; + break; -+ default: -+ return NULL; -+ } + -+ /* Now allocate and setup the descriptor. */ -+ d = kzalloc(sizeof(*d), GFP_NOWAIT); -+ if (!d) -+ return NULL; ++ case VCHIQ_SRVSTATE_OPENSYNC: ++ mutex_lock(&state->sync_mutex); ++ /* Drop through */ + -+ d->dir = direction; -+ -+ if (c->ch >= 8) /* we have a LITE channel */ -+ max_size = MAX_LITE_TRANSFER; -+ else -+ max_size = MAX_NORMAL_TRANSFER; -+ -+ /* We store the length of the SG list in d->frames -+ taking care to account for splitting up transfers -+ too large for a LITE channel */ -+ -+ d->frames = 0; -+ for_each_sg(sgl, sgent, sg_len, i) { -+ uint32_t len = sg_dma_len(sgent); -+ d->frames += 1 + len / max_size; -+ } -+ -+ /* Allocate memory for control blocks */ -+ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); -+ d->control_block_base = dma_zalloc_coherent(chan->device->dev, -+ d->control_block_size, &d->control_block_base_phys, -+ GFP_NOWAIT); -+ -+ if (!d->control_block_base) { -+ kfree(d); -+ return NULL; -+ } -+ -+ /* -+ * Iterate over all SG entries, create a control block -+ * for each frame and link them together. -+ */ -+ -+ /* we count the number of times an SG entry had to be splitct -+ as a result of using a LITE channel */ -+ splitct = 0; -+ -+ for_each_sg(sgl, sgent, sg_len, i) { -+ dma_addr_t addr = sg_dma_address(sgent); -+ uint32_t len = sg_dma_len(sgent); -+ -+ for (j = 0; j < len; j += max_size) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[i+splitct]; -+ -+ /* Setup adresses */ -+ if (d->dir == DMA_DEV_TO_MEM) { -+ control_block->info = BCM2835_DMA_D_INC | -+ BCM2835_DMA_D_WIDTH | BCM2835_DMA_S_DREQ; -+ control_block->src = dev_addr; -+ control_block->dst = addr + (dma_addr_t)j; -+ } else { -+ control_block->info = BCM2835_DMA_S_INC | -+ BCM2835_DMA_S_WIDTH | BCM2835_DMA_D_DREQ; -+ control_block->src = addr + (dma_addr_t)j; -+ control_block->dst = dev_addr; -+ } -+ -+ /* Common part */ -+ control_block->info |= BCM2835_DMA_WAITS(SDHCI_BCM_DMA_WAITS); -+ control_block->info |= BCM2835_DMA_WAIT_RESP; -+ -+ /* Enable */ -+ if (i == sg_len-1 && len-j <= max_size) -+ control_block->info |= BCM2835_DMA_INT_EN; -+ -+ /* Setup synchronization */ -+ if (sync_type != 0) -+ control_block->info |= sync_type; -+ -+ /* Setup DREQ channel */ -+ c->dreq = c->cfg.slave_id; /* DREQ loaded from config */ -+ -+ if (c->dreq != 0) -+ control_block->info |= -+ BCM2835_DMA_PER_MAP(c->dreq); -+ -+ /* Length of a frame */ -+ control_block->length = min(len-j, max_size); -+ d->size += control_block->length; -+ -+ /* -+ * Next block is the next frame. -+ */ -+ if (i < sg_len-1 || len-j > max_size) { -+ /* next block is the next frame. */ -+ control_block->next = d->control_block_base_phys + -+ sizeof(struct bcm2835_dma_cb) * (i + splitct + 1); -+ } else { -+ /* next block is empty. */ -+ control_block->next = 0; -+ } -+ -+ if (len-j > max_size) -+ splitct++; ++ case VCHIQ_SRVSTATE_OPEN: ++ if (state->is_master || close_recvd) { ++ if (!do_abort_bulks(service)) ++ status = VCHIQ_RETRY; + } -+ } + -+ c->cyclic = false; ++ release_service_messages(service); + -+ return vchan_tx_prep(&c->vc, &d->vd, flags); -+} ++ if (status == VCHIQ_SUCCESS) ++ status = queue_message(state, service, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK); + -+static int bcm2835_dma_slave_config(struct bcm2835_chan *c, -+ struct dma_slave_config *cfg) -+{ -+ if ((cfg->direction == DMA_DEV_TO_MEM && -+ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || -+ (cfg->direction == DMA_MEM_TO_DEV && -+ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || -+ !is_slave_direction(cfg->direction)) { -+ return -EINVAL; -+ } -+ -+ c->cfg = *cfg; -+ -+ return 0; -+} -+ -+static int bcm2835_dma_terminate_all(struct bcm2835_chan *c) -+{ -+ struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); -+ unsigned long flags; -+ int timeout = 10000; -+ LIST_HEAD(head); -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ -+ /* Prevent this channel being scheduled */ -+ spin_lock(&d->lock); -+ list_del_init(&c->node); -+ spin_unlock(&d->lock); -+ -+ /* -+ * Stop DMA activity: we assume the callback will not be called -+ * after bcm_dma_abort() returns (even if it does, it will see -+ * c->desc is NULL and exit.) -+ */ -+ if (c->desc) { -+ c->desc = NULL; -+ bcm2835_dma_abort(c->chan_base); -+ -+ /* Wait for stopping */ -+ while (--timeout) { -+ if (!(readl(c->chan_base + BCM2835_DMA_CS) & -+ BCM2835_DMA_ACTIVE)) ++ if (status == VCHIQ_SUCCESS) { ++ if (!close_recvd) { ++ /* Change the state while the mutex is ++ still held */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_CLOSESENT); ++ mutex_unlock(&state->slot_mutex); ++ if (service->sync) ++ mutex_unlock(&state->sync_mutex); + break; ++ } ++ } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { ++ mutex_unlock(&state->sync_mutex); ++ break; ++ } else ++ break; + -+ cpu_relax(); ++ /* Change the state while the mutex is still held */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD); ++ mutex_unlock(&state->slot_mutex); ++ if (service->sync) ++ mutex_unlock(&state->sync_mutex); ++ ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; ++ ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ if (!close_recvd) ++ /* This happens when a process is killed mid-close */ ++ break; ++ ++ if (!state->is_master) { ++ if (!do_abort_bulks(service)) { ++ status = VCHIQ_RETRY; ++ break; ++ } + } + -+ if (!timeout) -+ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); -+ } ++ if (status == VCHIQ_SUCCESS) ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; + -+ vchan_get_all_descriptors(&c->vc, &head); -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ vchan_dma_desc_free_list(&c->vc, &head); -+ -+ return 0; -+} -+ -+static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, -+ unsigned long arg) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ -+ switch (cmd) { -+ case DMA_SLAVE_CONFIG: -+ return bcm2835_dma_slave_config(c, -+ (struct dma_slave_config *)arg); -+ -+ case DMA_TERMINATE_ALL: -+ return bcm2835_dma_terminate_all(c); ++ case VCHIQ_SRVSTATE_CLOSERECVD: ++ if (!close_recvd && is_server) ++ /* Force into LISTENING mode */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; + + default: -+ return -ENXIO; ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_close_service_internal(%d) called in state %s", ++ close_recvd, srvstate_names[service->srvstate]); ++ break; ++ } ++ ++ return status; ++} ++ ++/* Called from the application process upon process death */ ++void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ ++ vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)", ++ state->id, service->localport, service->remoteport); ++ ++ mark_service_closing(service); ++ ++ /* Mark the service for removal by the slot handler */ ++ request_poll(state, service, VCHIQ_POLL_REMOVE); ++} ++ ++/* Called from the slot handler */ ++void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ ++ vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)", ++ state->id, service->localport); ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPENING: ++ case VCHIQ_SRVSTATE_CLOSED: ++ case VCHIQ_SRVSTATE_HIDDEN: ++ case VCHIQ_SRVSTATE_LISTENING: ++ case VCHIQ_SRVSTATE_CLOSEWAIT: ++ break; ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: fsi - (%d) in state %s", ++ state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ return; ++ } ++ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE); ++ ++ up(&service->remove_event); ++ ++ /* Release the initial lock */ ++ unlock_service(service); ++} ++ ++VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ /* Find all services registered to this client and enable them. */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ unlock_service(service); ++ } ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, ++ 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY) ++ return VCHIQ_RETRY; ++ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); ++ } ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) { ++ if (down_interruptible(&state->connect) != 0) ++ return VCHIQ_RETRY; ++ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ up(&state->connect); ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ /* Find all services registered to this client and enable them. */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ (void)vchiq_remove_service(service->handle); ++ unlock_service(service); ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ switch (state->conn_state) { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Request a pause */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING); ++ request_poll(state, NULL, 0); ++ break; ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_pause_internal in state %s\n", ++ conn_state_names[state->conn_state]); ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ break; ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING); ++ request_poll(state, NULL, 0); ++ } else { ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: close_service:%d", ++ service->state->id, service->localport); ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) { ++ unlock_service(service); ++ return VCHIQ_ERROR; ++ } ++ ++ mark_service_closing(service); ++ ++ if (current == service->state->slot_handler_thread) { ++ status = vchiq_close_service_internal(service, ++ 0/*!close_recvd*/); ++ BUG_ON(status == VCHIQ_RETRY); ++ } else { ++ /* Mark the service for termination by the slot handler */ ++ request_poll(service->state, service, VCHIQ_POLL_TERMINATE); ++ } ++ ++ while (1) { ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ break; ++ } ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) ++ break; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: close_service:%d - waiting in state %s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && ++ (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->srvstate != VCHIQ_SRVSTATE_LISTENING)) ++ status = VCHIQ_ERROR; ++ ++ unlock_service(service); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: remove_service:%d", ++ service->state->id, service->localport); ++ ++ if (service->srvstate == VCHIQ_SRVSTATE_FREE) { ++ unlock_service(service); ++ return VCHIQ_ERROR; ++ } ++ ++ mark_service_closing(service); ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || ++ (current == service->state->slot_handler_thread)) { ++ /* Make it look like a client, because it must be removed and ++ not left in the LISTENING state. */ ++ service->public_fourcc = VCHIQ_FOURCC_INVALID; ++ ++ status = vchiq_close_service_internal(service, ++ 0/*!close_recvd*/); ++ BUG_ON(status == VCHIQ_RETRY); ++ } else { ++ /* Mark the service for removal by the slot handler */ ++ request_poll(service->state, service, VCHIQ_POLL_REMOVE); ++ } ++ while (1) { ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ break; ++ } ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) ++ break; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: remove_service:%d - waiting in state %s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && ++ (service->srvstate != VCHIQ_SRVSTATE_FREE)) ++ status = VCHIQ_ERROR; ++ ++ unlock_service(service); ++ ++ return status; ++} ++ ++ ++/* This function may be called by kernel threads or user threads. ++ * User threads may receive VCHIQ_RETRY to indicate that a signal has been ++ * received and the call should be retried after being returned to user ++ * context. ++ * When called in blocking mode, the userdata field points to a bulk_waiter ++ * structure. ++ */ ++VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_BULK_QUEUE_T *queue; ++ VCHIQ_BULK_T *bulk; ++ VCHIQ_STATE_T *state; ++ struct bulk_waiter *bulk_waiter = NULL; ++ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; ++ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (!service || ++ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || ++ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) ++ goto error_exit; ++ ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ bulk_waiter = (struct bulk_waiter *)userdata; ++ sema_init(&bulk_waiter->event, 0); ++ bulk_waiter->actual = 0; ++ bulk_waiter->bulk = NULL; ++ break; ++ case VCHIQ_BULK_MODE_WAITING: ++ bulk_waiter = (struct bulk_waiter *)userdata; ++ bulk = bulk_waiter->bulk; ++ goto waiting; ++ default: ++ goto error_exit; ++ } ++ ++ state = service->state; ++ ++ queue = (dir == VCHIQ_BULK_TRANSMIT) ? ++ &service->bulk_tx : &service->bulk_rx; ++ ++ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ ++ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); ++ do { ++ mutex_unlock(&service->bulk_mutex); ++ if (down_interruptible(&service->bulk_remove_event) ++ != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ if (mutex_lock_interruptible(&service->bulk_mutex) ++ != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ } while (queue->local_insert == queue->remove + ++ VCHIQ_NUM_SERVICE_BULKS); ++ } ++ ++ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; ++ ++ bulk->mode = mode; ++ bulk->dir = dir; ++ bulk->userdata = userdata; ++ bulk->size = size; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ ++ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != ++ VCHIQ_SUCCESS) ++ goto unlock_error_exit; ++ ++ wmb(); ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: bt (%d->%d) %cx %x@%x %x", ++ state->id, ++ service->localport, service->remoteport, dir_char, ++ size, (unsigned int)bulk->data, (unsigned int)userdata); ++ ++ /* The slot mutex must be held when the service is being closed, so ++ claim it here to ensure that isn't happening */ ++ if (mutex_lock_interruptible(&state->slot_mutex) != 0) { ++ status = VCHIQ_RETRY; ++ goto cancel_bulk_error_exit; ++ } ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) ++ goto unlock_both_error_exit; ++ ++ if (state->is_master) { ++ queue->local_insert++; ++ if (resolve_bulks(service, queue)) ++ request_poll(state, service, ++ (dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); ++ } else { ++ int payload[2] = { (int)bulk->data, bulk->size }; ++ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) }; ++ ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(dir_msgtype, ++ service->localport, service->remoteport), ++ &element, 1, sizeof(payload), ++ QMFLAGS_IS_BLOCKING | ++ QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK); ++ if (status != VCHIQ_SUCCESS) { ++ goto unlock_both_error_exit; ++ } ++ queue->local_insert++; ++ } ++ ++ mutex_unlock(&state->slot_mutex); ++ mutex_unlock(&service->bulk_mutex); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: bt:%d %cx li=%x ri=%x p=%x", ++ state->id, ++ service->localport, dir_char, ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++waiting: ++ unlock_service(service); ++ ++ status = VCHIQ_SUCCESS; ++ ++ if (bulk_waiter) { ++ bulk_waiter->bulk = bulk; ++ if (down_interruptible(&bulk_waiter->event) != 0) ++ status = VCHIQ_RETRY; ++ else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) ++ status = VCHIQ_ERROR; ++ } ++ ++ return status; ++ ++unlock_both_error_exit: ++ mutex_unlock(&state->slot_mutex); ++cancel_bulk_error_exit: ++ vchiq_complete_bulk(bulk); ++unlock_error_exit: ++ mutex_unlock(&service->bulk_mutex); ++ ++error_exit: ++ if (service) ++ unlock_service(service); ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, ++ const VCHIQ_ELEMENT_T *elements, unsigned int count) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ unsigned int size = 0; ++ unsigned int i; ++ ++ if (!service || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) ++ goto error_exit; ++ ++ for (i = 0; i < (unsigned int)count; i++) { ++ if (elements[i].size) { ++ if (elements[i].data == NULL) { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ goto error_exit; ++ } ++ size += elements[i].size; ++ } ++ } ++ ++ if (size > VCHIQ_MAX_MSG_SIZE) { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ goto error_exit; ++ } ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPEN: ++ status = queue_message(service->state, service, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, ++ service->localport, ++ service->remoteport), ++ elements, count, size, 1); ++ break; ++ case VCHIQ_SRVSTATE_OPENSYNC: ++ status = queue_message_sync(service->state, service, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, ++ service->localport, ++ service->remoteport), ++ elements, count, size, 1); ++ break; ++ default: ++ status = VCHIQ_ERROR; ++ break; ++ } ++ ++error_exit: ++ if (service) ++ unlock_service(service); ++ ++ return status; ++} ++ ++void ++vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_STATE_T *state; ++ int slot_index; ++ ++ if (!service) ++ return; ++ ++ state = service->state; ++ remote = state->remote; ++ ++ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header); ++ ++ if ((slot_index >= remote->slot_first) && ++ (slot_index <= remote->slot_last)) { ++ int msgid = header->msgid; ++ if (msgid & VCHIQ_MSGID_CLAIMED) { ++ VCHIQ_SLOT_INFO_T *slot_info = ++ SLOT_INFO_FROM_INDEX(state, slot_index); ++ ++ release_slot(state, slot_info, header, service); ++ } ++ } else if (slot_index == remote->slot_sync) ++ release_message_sync(state, header); ++ ++ unlock_service(service); ++} ++ ++static void ++release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) ++{ ++ header->msgid = VCHIQ_MSGID_PADDING; ++ wmb(); ++ remote_event_signal(&state->remote->sync_release); ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ ++ if (!service || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS) || ++ !peer_version) ++ goto exit; ++ *peer_version = service->peer_version; ++ status = VCHIQ_SUCCESS; ++ ++exit: ++ if (service) ++ unlock_service(service); ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, VCHIQ_CONFIG_T *pconfig) ++{ ++ VCHIQ_CONFIG_T config; ++ ++ (void)instance; ++ ++ config.max_msg_size = VCHIQ_MAX_MSG_SIZE; ++ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE; ++ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS; ++ config.max_services = VCHIQ_MAX_SERVICES; ++ config.version = VCHIQ_VERSION; ++ config.version_min = VCHIQ_VERSION_MIN; ++ ++ if (config_size > sizeof(VCHIQ_CONFIG_T)) ++ return VCHIQ_ERROR; ++ ++ memcpy(pconfig, &config, ++ min(config_size, (int)(sizeof(VCHIQ_CONFIG_T)))); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHIQ_SERVICE_OPTION_T option, int value) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (service) { ++ switch (option) { ++ case VCHIQ_SERVICE_OPTION_AUTOCLOSE: ++ service->auto_close = value; ++ status = VCHIQ_SUCCESS; ++ break; ++ ++ case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[ ++ service->localport]; ++ if (value == 0) ++ value = service->state->default_slot_quota; ++ if ((value >= service_quota->slot_use_count) && ++ (value < (unsigned short)~0)) { ++ service_quota->slot_quota = value; ++ if ((value >= service_quota->slot_use_count) && ++ (service_quota->message_quota >= ++ service_quota->message_use_count)) { ++ /* Signal the service that it may have ++ ** dropped below its quota */ ++ up(&service_quota->quota_event); ++ } ++ status = VCHIQ_SUCCESS; ++ } ++ } break; ++ ++ case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[ ++ service->localport]; ++ if (value == 0) ++ value = service->state->default_message_quota; ++ if ((value >= service_quota->message_use_count) && ++ (value < (unsigned short)~0)) { ++ service_quota->message_quota = value; ++ if ((value >= ++ service_quota->message_use_count) && ++ (service_quota->slot_quota >= ++ service_quota->slot_use_count)) ++ /* Signal the service that it may have ++ ** dropped below its quota */ ++ up(&service_quota->quota_event); ++ status = VCHIQ_SUCCESS; ++ } ++ } break; ++ ++ case VCHIQ_SERVICE_OPTION_SYNCHRONOUS: ++ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || ++ (service->srvstate == ++ VCHIQ_SRVSTATE_LISTENING)) { ++ service->sync = value; ++ status = VCHIQ_SUCCESS; ++ } ++ break; ++ ++ case VCHIQ_SERVICE_OPTION_TRACE: ++ service->trace = value; ++ status = VCHIQ_SUCCESS; ++ break; ++ ++ default: ++ break; ++ } ++ unlock_service(service); ++ } ++ ++ return status; ++} ++ ++void ++vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, ++ VCHIQ_SHARED_STATE_T *shared, const char *label) ++{ ++ static const char *const debug_names[] = { ++ "", ++ "SLOT_HANDLER_COUNT", ++ "SLOT_HANDLER_LINE", ++ "PARSE_LINE", ++ "PARSE_HEADER", ++ "PARSE_MSGID", ++ "AWAIT_COMPLETION_LINE", ++ "DEQUEUE_MESSAGE_LINE", ++ "SERVICE_CALLBACK_LINE", ++ "MSG_QUEUE_FULL_COUNT", ++ "COMPLETION_QUEUE_FULL_COUNT" ++ }; ++ int i; ++ ++ char buf[80]; ++ int len; ++ len = snprintf(buf, sizeof(buf), ++ " %s: slots %d-%d tx_pos=%x recycle=%x", ++ label, shared->slot_first, shared->slot_last, ++ shared->tx_pos, shared->slot_queue_recycle); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Slots claimed:"); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ for (i = shared->slot_first; i <= shared->slot_last; i++) { ++ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info.use_count != slot_info.release_count) { ++ len = snprintf(buf, sizeof(buf), ++ " %d: %d/%d", i, slot_info.use_count, ++ slot_info.release_count); ++ vchiq_dump(dump_context, buf, len + 1); ++ } ++ } ++ ++ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) { ++ len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", ++ debug_names[i], shared->debug[i], shared->debug[i]); ++ vchiq_dump(dump_context, buf, len + 1); + } +} + -+#ifdef CONFIG_OF -+static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) ++void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state) +{ -+ struct bcm2835_chan *c; ++ char buf[80]; ++ int len; ++ int i; + -+ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); -+ if (!c) -+ return -ENOMEM; ++ len = snprintf(buf, sizeof(buf), "State %d: %s", state->id, ++ conn_state_names[state->conn_state]); ++ vchiq_dump(dump_context, buf, len + 1); + -+ c->vc.desc_free = bcm2835_dma_desc_free; -+ vchan_init(&c->vc, &d->ddev); -+ INIT_LIST_HEAD(&c->node); ++ len = snprintf(buf, sizeof(buf), ++ " tx_pos=%x(@%x), rx_pos=%x(@%x)", ++ state->local->tx_pos, ++ (uint32_t)state->tx_data + ++ (state->local_tx_pos & VCHIQ_SLOT_MASK), ++ state->rx_pos, ++ (uint32_t)state->rx_data + ++ (state->rx_pos & VCHIQ_SLOT_MASK)); ++ vchiq_dump(dump_context, buf, len + 1); + -+ d->ddev.chancnt++; ++ len = snprintf(buf, sizeof(buf), ++ " Version: %d (min %d)", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN); ++ vchiq_dump(dump_context, buf, len + 1); + -+ c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); -+ c->ch = chan_id; -+ c->irq_number = irq; ++ if (VCHIQ_ENABLE_STATS) { ++ len = snprintf(buf, sizeof(buf), ++ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, " ++ "error_count=%d", ++ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, ++ state->stats.error_count); ++ vchiq_dump(dump_context, buf, len + 1); ++ } + -+ return 0; ++ len = snprintf(buf, sizeof(buf), ++ " Slots: %d available (%d data), %d recyclable, %d stalls " ++ "(%d data)", ++ ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - ++ state->local_tx_pos) / VCHIQ_SLOT_SIZE, ++ state->data_quota - state->data_use_count, ++ state->local->slot_queue_recycle - state->slot_queue_available, ++ state->stats.slot_stalls, state->stats.data_stalls); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ vchiq_dump_platform_state(dump_context); ++ ++ vchiq_dump_shared_state(dump_context, state, state->local, "Local"); ++ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote"); ++ ++ vchiq_dump_platform_instances(dump_context); ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = find_service_by_port(state, i); ++ ++ if (service) { ++ vchiq_dump_service_state(dump_context, service); ++ unlock_service(service); ++ } ++ } +} ++ ++void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ char buf[80]; ++ int len; ++ ++ len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)", ++ service->localport, srvstate_names[service->srvstate], ++ service->ref_count - 1); /*Don't include the lock just taken*/ ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_FREE) { ++ char remoteport[30]; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[service->localport]; ++ int fourcc = service->base.fourcc; ++ int tx_pending, rx_pending; ++ if (service->remoteport != VCHIQ_PORT_FREE) { ++ int len2 = snprintf(remoteport, sizeof(remoteport), ++ "%d", service->remoteport); ++ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) ++ snprintf(remoteport + len2, ++ sizeof(remoteport) - len2, ++ " (client %x)", service->client_id); ++ } else ++ strcpy(remoteport, "n/a"); ++ ++ len += snprintf(buf + len, sizeof(buf) - len, ++ " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)", ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ remoteport, ++ service_quota->message_use_count, ++ service_quota->message_quota, ++ service_quota->slot_use_count, ++ service_quota->slot_quota); ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ tx_pending = service->bulk_tx.local_insert - ++ service->bulk_tx.remote_insert; ++ ++ rx_pending = service->bulk_rx.local_insert - ++ service->bulk_rx.remote_insert; ++ ++ len = snprintf(buf, sizeof(buf), ++ " Bulk: tx_pending=%d (size %d)," ++ " rx_pending=%d (size %d)", ++ tx_pending, ++ tx_pending ? service->bulk_tx.bulks[ ++ BULK_INDEX(service->bulk_tx.remove)].size : 0, ++ rx_pending, ++ rx_pending ? service->bulk_rx.bulks[ ++ BULK_INDEX(service->bulk_rx.remove)].size : 0); ++ ++ if (VCHIQ_ENABLE_STATS) { ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Ctrl: tx_count=%d, tx_bytes=%llu, " ++ "rx_count=%d, rx_bytes=%llu", ++ service->stats.ctrl_tx_count, ++ service->stats.ctrl_tx_bytes, ++ service->stats.ctrl_rx_count, ++ service->stats.ctrl_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Bulk: tx_count=%d, tx_bytes=%llu, " ++ "rx_count=%d, rx_bytes=%llu", ++ service->stats.bulk_tx_count, ++ service->stats.bulk_tx_bytes, ++ service->stats.bulk_rx_count, ++ service->stats.bulk_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " %d quota stalls, %d slot stalls, " ++ "%d bulk stalls, %d aborted, %d errors", ++ service->stats.quota_stalls, ++ service->stats.slot_stalls, ++ service->stats.bulk_stalls, ++ service->stats.bulk_aborted_count, ++ service->stats.error_count); ++ } ++ } ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_FREE) ++ vchiq_dump_platform_service_state(dump_context, service); ++} ++ ++ ++void ++vchiq_loud_error_header(void) ++{ ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, "====="); ++} ++ ++void ++vchiq_loud_error_footer(void) ++{ ++ vchiq_log_error(vchiq_core_log_level, "====="); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++} ++ ++ ++VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, ++ size_t numBytes) ++{ ++ const uint8_t *mem = (const uint8_t *)voidMem; ++ size_t offset; ++ char lineBuf[100]; ++ char *s; ++ ++ while (numBytes > 0) { ++ s = lineBuf; ++ ++ for (offset = 0; offset < 16; offset++) { ++ if (offset < numBytes) ++ s += snprintf(s, 4, "%02x ", mem[offset]); ++ else ++ s += snprintf(s, 4, " "); ++ } ++ ++ for (offset = 0; offset < 16; offset++) { ++ if (offset < numBytes) { ++ uint8_t ch = mem[offset]; ++ ++ if ((ch < ' ') || (ch > '~')) ++ ch = '.'; ++ *s++ = (char)ch; ++ } ++ } ++ *s++ = '\0'; ++ ++ if ((label != NULL) && (*label != '\0')) ++ vchiq_log_trace(VCHIQ_LOG_TRACE, ++ "%s: %08x: %s", label, addr, lineBuf); ++ else ++ vchiq_log_trace(VCHIQ_LOG_TRACE, ++ "%08x: %s", addr, lineBuf); ++ ++ addr += 16; ++ mem += 16; ++ if (numBytes > 16) ++ numBytes -= 16; ++ else ++ numBytes = 0; ++ } ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +new file mode 100644 +index 0000000..9be484c +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +@@ -0,0 +1,712 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CORE_H ++#define VCHIQ_CORE_H ++ ++#include ++#include ++#include ++ ++#include "vchiq_cfg.h" ++ ++#include "vchiq.h" ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++#define VCHIQ_LOG_DEFAULT 4 ++#define VCHIQ_LOG_ERROR 3 ++#define VCHIQ_LOG_WARNING 4 ++#define VCHIQ_LOG_INFO 6 ++#define VCHIQ_LOG_TRACE 7 ++ ++#define VCHIQ_LOG_PREFIX KERN_INFO "vchiq: " ++ ++#ifndef vchiq_log_error ++#define vchiq_log_error(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_ERROR) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_warning ++#define vchiq_log_warning(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_WARNING) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_info ++#define vchiq_log_info(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_INFO) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_trace ++#define vchiq_log_trace(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_TRACE) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) +#endif + -+static int bcm2708_dma_chan_init(struct bcm2835_dmadev *d, -+ void __iomem *chan_base, int chan_id, int irq) -+{ -+ struct bcm2835_chan *c; ++#define vchiq_loud_error(...) \ ++ vchiq_log_error(vchiq_core_log_level, "===== " __VA_ARGS__) + -+ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); -+ if (!c) -+ return -ENOMEM; ++#ifndef vchiq_static_assert ++#define vchiq_static_assert(cond) __attribute__((unused)) \ ++ extern int vchiq_static_assert[(cond) ? 1 : -1] ++#endif + -+ c->vc.desc_free = bcm2835_dma_desc_free; -+ vchan_init(&c->vc, &d->ddev); -+ INIT_LIST_HEAD(&c->node); ++#define IS_POW2(x) (x && ((x & (x - 1)) == 0)) + -+ d->ddev.chancnt++; ++/* Ensure that the slot size and maximum number of slots are powers of 2 */ ++vchiq_static_assert(IS_POW2(VCHIQ_SLOT_SIZE)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); + -+ c->chan_base = chan_base; -+ c->ch = chan_id; -+ c->irq_number = irq; ++#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) ++#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) ++#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \ ++ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) + -+ return 0; -+} ++#define VCHIQ_MSG_PADDING 0 /* - */ ++#define VCHIQ_MSG_CONNECT 1 /* - */ ++#define VCHIQ_MSG_OPEN 2 /* + (srcport, -), fourcc, client_id */ ++#define VCHIQ_MSG_OPENACK 3 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_CLOSE 4 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_DATA 5 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_BULK_RX 6 /* + (srcport, dstport), data, size */ ++#define VCHIQ_MSG_BULK_TX 7 /* + (srcport, dstport), data, size */ ++#define VCHIQ_MSG_BULK_RX_DONE 8 /* + (srcport, dstport), actual */ ++#define VCHIQ_MSG_BULK_TX_DONE 9 /* + (srcport, dstport), actual */ ++#define VCHIQ_MSG_PAUSE 10 /* - */ ++#define VCHIQ_MSG_RESUME 11 /* - */ ++#define VCHIQ_MSG_REMOTE_USE 12 /* - */ ++#define VCHIQ_MSG_REMOTE_RELEASE 13 /* - */ ++#define VCHIQ_MSG_REMOTE_USE_ACTIVE 14 /* - */ + ++#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) ++#define VCHIQ_PORT_FREE 0x1000 ++#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE) ++#define VCHIQ_MAKE_MSG(type, srcport, dstport) \ ++ ((type<<24) | (srcport<<12) | (dstport<<0)) ++#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24) ++#define VCHIQ_MSG_SRCPORT(msgid) \ ++ (unsigned short)(((unsigned int)msgid >> 12) & 0xfff) ++#define VCHIQ_MSG_DSTPORT(msgid) \ ++ ((unsigned short)msgid & 0xfff) + -+static void bcm2835_dma_free(struct bcm2835_dmadev *od) -+{ -+ struct bcm2835_chan *c, *next; ++#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \ ++ ((fourcc) >> 24) & 0xff, \ ++ ((fourcc) >> 16) & 0xff, \ ++ ((fourcc) >> 8) & 0xff, \ ++ (fourcc) & 0xff + -+ list_for_each_entry_safe(c, next, &od->ddev.channels, -+ vc.chan.device_node) { -+ list_del(&c->vc.chan.device_node); -+ tasklet_kill(&c->vc.task); -+ } -+} ++/* Ensure the fields are wide enough */ ++vchiq_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) ++ == 0); ++vchiq_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0, VCHIQ_PORT_MAX, 0)) == 0); ++vchiq_static_assert((unsigned int)VCHIQ_PORT_MAX < ++ (unsigned int)VCHIQ_PORT_FREE); + -+static const struct of_device_id bcm2835_dma_of_match[] = { -+ { .compatible = "brcm,bcm2835-dma", }, -+ {}, ++#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING, 0, 0) ++#define VCHIQ_MSGID_CLAIMED 0x40000000 ++ ++#define VCHIQ_FOURCC_INVALID 0x00000000 ++#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID) ++ ++#define VCHIQ_BULK_ACTUAL_ABORTED -1 ++ ++typedef uint32_t BITSET_T; ++ ++vchiq_static_assert((sizeof(BITSET_T) * 8) == 32); ++ ++#define BITSET_SIZE(b) ((b + 31) >> 5) ++#define BITSET_WORD(b) (b >> 5) ++#define BITSET_BIT(b) (1 << (b & 31)) ++#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs)) ++#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b)) ++#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b)) ++#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b)) ++ ++#if VCHIQ_ENABLE_STATS ++#define VCHIQ_STATS_INC(state, stat) (state->stats. stat++) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat++) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) \ ++ (service->stats. stat += addend) ++#else ++#define VCHIQ_STATS_INC(state, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0) ++#endif ++ ++enum { ++ DEBUG_ENTRIES, ++#if VCHIQ_ENABLE_DEBUG ++ DEBUG_SLOT_HANDLER_COUNT, ++ DEBUG_SLOT_HANDLER_LINE, ++ DEBUG_PARSE_LINE, ++ DEBUG_PARSE_HEADER, ++ DEBUG_PARSE_MSGID, ++ DEBUG_AWAIT_COMPLETION_LINE, ++ DEBUG_DEQUEUE_MESSAGE_LINE, ++ DEBUG_SERVICE_CALLBACK_LINE, ++ DEBUG_MSG_QUEUE_FULL_COUNT, ++ DEBUG_COMPLETION_QUEUE_FULL_COUNT, ++#endif ++ DEBUG_MAX +}; -+MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); + -+#ifdef CONFIG_OF -+static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, -+ struct of_dma *ofdma) ++#if VCHIQ_ENABLE_DEBUG ++ ++#define DEBUG_INITIALISE(local) int *debug_ptr = (local)->debug; ++#define DEBUG_TRACE(d) \ ++ do { debug_ptr[DEBUG_ ## d] = __LINE__; dsb(); } while (0) ++#define DEBUG_VALUE(d, v) \ ++ do { debug_ptr[DEBUG_ ## d] = (v); dsb(); } while (0) ++#define DEBUG_COUNT(d) \ ++ do { debug_ptr[DEBUG_ ## d]++; dsb(); } while (0) ++ ++#else /* VCHIQ_ENABLE_DEBUG */ ++ ++#define DEBUG_INITIALISE(local) ++#define DEBUG_TRACE(d) ++#define DEBUG_VALUE(d, v) ++#define DEBUG_COUNT(d) ++ ++#endif /* VCHIQ_ENABLE_DEBUG */ ++ ++typedef enum { ++ VCHIQ_CONNSTATE_DISCONNECTED, ++ VCHIQ_CONNSTATE_CONNECTING, ++ VCHIQ_CONNSTATE_CONNECTED, ++ VCHIQ_CONNSTATE_PAUSING, ++ VCHIQ_CONNSTATE_PAUSE_SENT, ++ VCHIQ_CONNSTATE_PAUSED, ++ VCHIQ_CONNSTATE_RESUMING, ++ VCHIQ_CONNSTATE_PAUSE_TIMEOUT, ++ VCHIQ_CONNSTATE_RESUME_TIMEOUT ++} VCHIQ_CONNSTATE_T; ++ ++enum { ++ VCHIQ_SRVSTATE_FREE, ++ VCHIQ_SRVSTATE_HIDDEN, ++ VCHIQ_SRVSTATE_LISTENING, ++ VCHIQ_SRVSTATE_OPENING, ++ VCHIQ_SRVSTATE_OPEN, ++ VCHIQ_SRVSTATE_OPENSYNC, ++ VCHIQ_SRVSTATE_CLOSESENT, ++ VCHIQ_SRVSTATE_CLOSERECVD, ++ VCHIQ_SRVSTATE_CLOSEWAIT, ++ VCHIQ_SRVSTATE_CLOSED ++}; ++ ++enum { ++ VCHIQ_POLL_TERMINATE, ++ VCHIQ_POLL_REMOVE, ++ VCHIQ_POLL_TXNOTIFY, ++ VCHIQ_POLL_RXNOTIFY, ++ VCHIQ_POLL_COUNT ++}; ++ ++typedef enum { ++ VCHIQ_BULK_TRANSMIT, ++ VCHIQ_BULK_RECEIVE ++} VCHIQ_BULK_DIR_T; ++ ++typedef void (*VCHIQ_USERDATA_TERM_T)(void *userdata); ++ ++typedef struct vchiq_bulk_struct { ++ short mode; ++ short dir; ++ void *userdata; ++ VCHI_MEM_HANDLE_T handle; ++ void *data; ++ int size; ++ void *remote_data; ++ int remote_size; ++ int actual; ++} VCHIQ_BULK_T; ++ ++typedef struct vchiq_bulk_queue_struct { ++ int local_insert; /* Where to insert the next local bulk */ ++ int remote_insert; /* Where to insert the next remote bulk (master) */ ++ int process; /* Bulk to transfer next */ ++ int remote_notify; /* Bulk to notify the remote client of next (mstr) */ ++ int remove; /* Bulk to notify the local client of, and remove, ++ ** next */ ++ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS]; ++} VCHIQ_BULK_QUEUE_T; ++ ++typedef struct remote_event_struct { ++ int armed; ++ int fired; ++ struct semaphore *event; ++} REMOTE_EVENT_T; ++ ++typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T; ++ ++typedef struct vchiq_state_struct VCHIQ_STATE_T; ++ ++typedef struct vchiq_slot_struct { ++ char data[VCHIQ_SLOT_SIZE]; ++} VCHIQ_SLOT_T; ++ ++typedef struct vchiq_slot_info_struct { ++ /* Use two counters rather than one to avoid the need for a mutex. */ ++ short use_count; ++ short release_count; ++} VCHIQ_SLOT_INFO_T; ++ ++typedef struct vchiq_service_struct { ++ VCHIQ_SERVICE_BASE_T base; ++ VCHIQ_SERVICE_HANDLE_T handle; ++ unsigned int ref_count; ++ int srvstate; ++ VCHIQ_USERDATA_TERM_T userdata_term; ++ unsigned int localport; ++ unsigned int remoteport; ++ int public_fourcc; ++ int client_id; ++ char auto_close; ++ char sync; ++ char closing; ++ char trace; ++ atomic_t poll_flags; ++ short version; ++ short version_min; ++ short peer_version; ++ ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance; ++ ++ int service_use_count; ++ ++ VCHIQ_BULK_QUEUE_T bulk_tx; ++ VCHIQ_BULK_QUEUE_T bulk_rx; ++ ++ struct semaphore remove_event; ++ struct semaphore bulk_remove_event; ++ struct mutex bulk_mutex; ++ ++ struct service_stats_struct { ++ int quota_stalls; ++ int slot_stalls; ++ int bulk_stalls; ++ int error_count; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int bulk_tx_count; ++ int bulk_rx_count; ++ int bulk_aborted_count; ++ uint64_t ctrl_tx_bytes; ++ uint64_t ctrl_rx_bytes; ++ uint64_t bulk_tx_bytes; ++ uint64_t bulk_rx_bytes; ++ } stats; ++} VCHIQ_SERVICE_T; ++ ++/* The quota information is outside VCHIQ_SERVICE_T so that it can be ++ statically allocated, since for accounting reasons a service's slot ++ usage is carried over between users of the same port number. ++ */ ++typedef struct vchiq_service_quota_struct { ++ unsigned short slot_quota; ++ unsigned short slot_use_count; ++ unsigned short message_quota; ++ unsigned short message_use_count; ++ struct semaphore quota_event; ++ int previous_tx_index; ++} VCHIQ_SERVICE_QUOTA_T; ++ ++typedef struct vchiq_shared_state_struct { ++ ++ /* A non-zero value here indicates that the content is valid. */ ++ int initialised; ++ ++ /* The first and last (inclusive) slots allocated to the owner. */ ++ int slot_first; ++ int slot_last; ++ ++ /* The slot allocated to synchronous messages from the owner. */ ++ int slot_sync; ++ ++ /* Signalling this event indicates that owner's slot handler thread ++ ** should run. */ ++ REMOTE_EVENT_T trigger; ++ ++ /* Indicates the byte position within the stream where the next message ++ ** will be written. The least significant bits are an index into the ++ ** slot. The next bits are the index of the slot in slot_queue. */ ++ int tx_pos; ++ ++ /* This event should be signalled when a slot is recycled. */ ++ REMOTE_EVENT_T recycle; ++ ++ /* The slot_queue index where the next recycled slot will be written. */ ++ int slot_queue_recycle; ++ ++ /* This event should be signalled when a synchronous message is sent. */ ++ REMOTE_EVENT_T sync_trigger; ++ ++ /* This event should be signalled when a synchronous message has been ++ ** released. */ ++ REMOTE_EVENT_T sync_release; ++ ++ /* A circular buffer of slot indexes. */ ++ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE]; ++ ++ /* Debugging state */ ++ int debug[DEBUG_MAX]; ++} VCHIQ_SHARED_STATE_T; ++ ++typedef struct vchiq_slot_zero_struct { ++ int magic; ++ short version; ++ short version_min; ++ int slot_zero_size; ++ int slot_size; ++ int max_slots; ++ int max_slots_per_side; ++ int platform_data[2]; ++ VCHIQ_SHARED_STATE_T master; ++ VCHIQ_SHARED_STATE_T slave; ++ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS]; ++} VCHIQ_SLOT_ZERO_T; ++ ++struct vchiq_state_struct { ++ int id; ++ int initialised; ++ VCHIQ_CONNSTATE_T conn_state; ++ int is_master; ++ short version_common; ++ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_SLOT_T *slot_data; ++ ++ unsigned short default_slot_quota; ++ unsigned short default_message_quota; ++ ++ /* Event indicating connect message received */ ++ struct semaphore connect; ++ ++ /* Mutex protecting services */ ++ struct mutex mutex; ++ VCHIQ_INSTANCE_T *instance; ++ ++ /* Processes incoming messages */ ++ struct task_struct *slot_handler_thread; ++ ++ /* Processes recycled slots */ ++ struct task_struct *recycle_thread; ++ ++ /* Processes synchronous messages */ ++ struct task_struct *sync_thread; ++ ++ /* Local implementation of the trigger remote event */ ++ struct semaphore trigger_event; ++ ++ /* Local implementation of the recycle remote event */ ++ struct semaphore recycle_event; ++ ++ /* Local implementation of the sync trigger remote event */ ++ struct semaphore sync_trigger_event; ++ ++ /* Local implementation of the sync release remote event */ ++ struct semaphore sync_release_event; ++ ++ char *tx_data; ++ char *rx_data; ++ VCHIQ_SLOT_INFO_T *rx_info; ++ ++ struct mutex slot_mutex; ++ ++ struct mutex recycle_mutex; ++ ++ struct mutex sync_mutex; ++ ++ struct mutex bulk_transfer_mutex; ++ ++ /* Indicates the byte position within the stream from where the next ++ ** message will be read. The least significant bits are an index into ++ ** the slot.The next bits are the index of the slot in ++ ** remote->slot_queue. */ ++ int rx_pos; ++ ++ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read ++ from remote->tx_pos. */ ++ int local_tx_pos; ++ ++ /* The slot_queue index of the slot to become available next. */ ++ int slot_queue_available; ++ ++ /* A flag to indicate if any poll has been requested */ ++ int poll_needed; ++ ++ /* Ths index of the previous slot used for data messages. */ ++ int previous_data_index; ++ ++ /* The number of slots occupied by data messages. */ ++ unsigned short data_use_count; ++ ++ /* The maximum number of slots to be occupied by data messages. */ ++ unsigned short data_quota; ++ ++ /* An array of bit sets indicating which services must be polled. */ ++ atomic_t poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ ++ /* The number of the first unused service */ ++ int unused_service; ++ ++ /* Signalled when a free slot becomes available. */ ++ struct semaphore slot_available_event; ++ ++ struct semaphore slot_remove_event; ++ ++ /* Signalled when a free data slot becomes available. */ ++ struct semaphore data_quota_event; ++ ++ /* Incremented when there are bulk transfers which cannot be processed ++ * whilst paused and must be processed on resume */ ++ int deferred_bulks; ++ ++ struct state_stats_struct { ++ int slot_stalls; ++ int data_stalls; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int error_count; ++ } stats; ++ ++ VCHIQ_SERVICE_T * services[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; ++ ++ VCHIQ_PLATFORM_STATE_T platform_state; ++}; ++ ++struct bulk_waiter { ++ VCHIQ_BULK_T *bulk; ++ struct semaphore event; ++ int actual; ++}; ++ ++extern spinlock_t bulk_waiter_spinlock; ++ ++extern int vchiq_core_log_level; ++extern int vchiq_core_msg_log_level; ++extern int vchiq_sync_log_level; ++ ++extern VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; ++ ++extern const char * ++get_conn_state_name(VCHIQ_CONNSTATE_T conn_state); ++ ++extern VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size); ++ ++extern VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, ++ int is_master); ++ ++extern VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term); ++ ++extern VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id); ++ ++extern VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd); ++ ++extern void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state); ++ ++extern void ++remote_event_pollall(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir); ++ ++extern void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_loud_error_header(void); ++ ++extern void ++vchiq_loud_error_footer(void); ++ ++extern void ++request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type); ++ ++static inline VCHIQ_SERVICE_T * ++handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) +{ -+ struct bcm2835_dmadev *d = ofdma->of_dma_data; -+ struct dma_chan *chan; -+ -+ chan = dma_get_any_slave_channel(&d->ddev); -+ if (!chan) ++ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & ++ (VCHIQ_MAX_STATES - 1)]; ++ if (!state) + return NULL; + -+ /* Set DREQ from param */ -+ to_bcm2835_dma_chan(chan)->dreq = spec->args[0]; -+ -+ return chan; -+} -+#endif -+ -+static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, -+ struct dma_slave_caps *caps) -+{ -+ caps->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); -+ caps->dstn_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); -+ caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); -+ caps->cmd_pause = false; -+ caps->cmd_terminate = true; -+ -+ return 0; ++ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; +} + -+static int bcm2835_dma_probe(struct platform_device *pdev) -+{ -+ struct bcm2835_dmadev *od; -+#ifdef CONFIG_OF -+ struct resource *res; -+ void __iomem *base; -+ uint32_t chans_available; -+#endif -+ int rc; -+ int i; -+ int irq; ++extern VCHIQ_SERVICE_T * ++find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++find_service_by_port(VCHIQ_STATE_T *state, int localport); ++ ++extern VCHIQ_SERVICE_T * ++find_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, ++ int *pidx); ++ ++extern void ++lock_service(VCHIQ_SERVICE_T *service); ++ ++extern void ++unlock_service(VCHIQ_SERVICE_T *service); ++ ++/* The following functions are called from vchiq_core, and external ++** implementations must be provided. */ ++ ++extern VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir); ++ ++extern void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk); ++ ++extern void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk); ++ ++extern VCHIQ_STATUS_T ++vchiq_copy_from_user(void *dst, const void *src, int size); ++ ++extern void ++remote_event_signal(REMOTE_EVENT_T *event); ++ ++void ++vchiq_platform_check_suspend(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_paused(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_resumed(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump(void *dump_context, const char *str, int len); ++ ++extern void ++vchiq_dump_platform_state(void *dump_context); ++ ++extern void ++vchiq_dump_platform_instances(void *dump_context); ++ ++extern void ++vchiq_dump_platform_service_state(void *dump_context, ++ VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_on_remote_use(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_on_remote_release(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_init_state(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_on_remote_use_active(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_release(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use_active(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, ++ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate); ++ ++extern void ++vchiq_platform_handle_timeout(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate); + + -+ if (!pdev->dev.dma_mask) -+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; -+ -+ /* If CONFIG_OF is selected, device tree is used */ -+ /* hence the difference between probing */ -+ -+#ifndef CONFIG_OF -+ -+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); -+ if (rc) -+ return rc; -+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); -+ -+ -+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); -+ if (!od) -+ return -ENOMEM; -+ -+ pdev->dev.dma_parms = &od->dma_parms; -+ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); -+ -+ -+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); -+ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); -+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); -+ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; -+ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; -+ od->ddev.device_tx_status = bcm2835_dma_tx_status; -+ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; -+ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; -+ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; -+ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; -+ od->ddev.device_control = bcm2835_dma_control; -+ od->ddev.dev = &pdev->dev; -+ INIT_LIST_HEAD(&od->ddev.channels); -+ spin_lock_init(&od->lock); -+ -+ platform_set_drvdata(pdev, od); -+ -+ for (i = 0; i < 5; i++) { -+ void __iomem *chan_base; -+ int chan_id; -+ -+ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_LITE, -+ &chan_base, -+ &irq); -+ -+ if (chan_id < 0) -+ break; -+ -+ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); -+ if (rc) -+ goto err_no_dma; -+ } -+#else -+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); -+ if (rc) -+ return rc; -+ -+ -+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); -+ if (!od) -+ return -ENOMEM; -+ -+ pdev->dev.dma_parms = &od->dma_parms; -+ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); -+ -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ od->base = base; -+ -+ -+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); -+ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); -+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); -+ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; -+ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; -+ od->ddev.device_tx_status = bcm2835_dma_tx_status; -+ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; -+ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; -+ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; -+ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; -+ od->ddev.device_control = bcm2835_dma_control; -+ od->ddev.dev = &pdev->dev; -+ INIT_LIST_HEAD(&od->ddev.channels); -+ spin_lock_init(&od->lock); -+ -+ platform_set_drvdata(pdev, od); -+ -+ -+ /* Request DMA channel mask from device tree */ -+ if (of_property_read_u32(pdev->dev.of_node, -+ "brcm,dma-channel-mask", -+ &chans_available)) { -+ dev_err(&pdev->dev, "Failed to get channel mask\n"); -+ rc = -EINVAL; -+ goto err_no_dma; -+ } -+ -+ -+ /* -+ * Do not use the FIQ and BULK channels, -+ * because they are used by the GPU. -+ */ -+ chans_available &= ~(BCM2835_DMA_FIQ_MASK | BCM2835_DMA_BULK_MASK); -+ -+ -+ for (i = 0; i < pdev->num_resources; i++) { -+ irq = platform_get_irq(pdev, i); -+ if (irq < 0) -+ break; -+ -+ if (chans_available & (1 << i)) { -+ rc = bcm2835_dma_chan_init(od, i, irq); -+ if (rc) -+ goto err_no_dma; -+ } -+ } -+ -+ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); -+ -+ /* Device-tree DMA controller registration */ -+ rc = of_dma_controller_register(pdev->dev.of_node, -+ bcm2835_dma_xlate, od); -+ if (rc) { -+ dev_err(&pdev->dev, "Failed to register DMA controller\n"); -+ goto err_no_dma; -+ } -+#endif -+ -+ rc = dma_async_device_register(&od->ddev); -+ if (rc) { -+ dev_err(&pdev->dev, -+ "Failed to register slave DMA engine device: %d\n", rc); -+ goto err_no_dma; -+ } -+ -+ dev_info(&pdev->dev, "Load BCM2835 DMA engine driver\n"); -+ -+ return 0; -+ -+err_no_dma: -+ bcm2835_dma_free(od); -+ return rc; -+} -+ -+static int bcm2835_dma_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_dmadev *od = platform_get_drvdata(pdev); -+ -+ dma_async_device_unregister(&od->ddev); -+ bcm2835_dma_free(od); -+ -+ return 0; -+} -+ -+#ifndef CONFIG_OF -+ -+ -+static struct platform_driver bcm2835_dma_driver = { -+ .probe = bcm2835_dma_probe, -+ .remove = bcm2835_dma_remove, -+ .driver = { -+ .name = "bcm2708-dmaengine", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_device *pdev; -+ -+static const struct platform_device_info bcm2835_dma_dev_info = { -+ .name = "bcm2708-dmaengine", -+ .id = -1, -+}; -+ -+static int bcm2835_dma_init(void) -+{ -+ int rc = platform_driver_register(&bcm2835_dma_driver); -+ -+ if (rc == 0) { -+ pdev = platform_device_register_full(&bcm2835_dma_dev_info); -+ if (IS_ERR(pdev)) { -+ platform_driver_unregister(&bcm2835_dma_driver); -+ rc = PTR_ERR(pdev); -+ } -+ } -+ -+ return rc; -+} -+module_init(bcm2835_dma_init); /* preferable to subsys_initcall */ -+ -+static void __exit bcm2835_dma_exit(void) -+{ -+ platform_device_unregister(pdev); -+ platform_driver_unregister(&bcm2835_dma_driver); -+} -+module_exit(bcm2835_dma_exit); -+ -+#else -+ -+static struct platform_driver bcm2835_dma_driver = { -+ .probe = bcm2835_dma_probe, -+ .remove = bcm2835_dma_remove, -+ .driver = { -+ .name = "bcm2835-dma", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(bcm2835_dma_of_match), -+ }, -+}; -+ -+module_platform_driver(bcm2835_dma_driver); ++extern void ++vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, ++ size_t numBytes); + +#endif -+ -+MODULE_ALIAS("platform:bcm2835-dma"); -+MODULE_DESCRIPTION("BCM2835 DMA engine driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_AUTHOR("Gellert Weisz "); -+MODULE_LICENSE("GPL v2"); - -From 1cd0b002163ef24b26370db2efdc928aab23357b Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:33:38 +0100 -Subject: [PATCH 031/172] ASoC: Add support for BCM2708 - -This driver adds support for digital audio (I2S) -for the BCM2708 SoC that is used by the -Raspberry Pi. External audio codecs can be -connected to the Raspberry Pi via P5 header. - -It relies on cyclic DMA engine support for BCM2708. - -Signed-off-by: Florian Meier - -ASoC: BCM2708: Add 24 bit support - -This adds 24 bit support to the I2S driver of the BCM2708. -Besides enabling the 24 bit flags, it includes two bug fixes: - -MMAP is not supported. Claiming this leads to strange issues -when the format of driver and file do not match. - -The datasheet states that the width extension bit should be set -for widths greater than 24, but greater or equal would be correct. -This follows from the definition of the width field. - -Signed-off-by: Florian Meier - -bcm2708-i2s: Update bclk_ratio to more correct values - -Move GPIO setup to hw_params. - -This is used to stop the I2S driver from breaking -the GPIO setup for other uses of the PCM interface - -Configure GPIOs for I2S based on revision/card settings - -With RPi model B+, assignment of the I2S GPIO pins has changed. -This patch uses the board revision to auto-detect the GPIOs used -for I2S. It also allows sound card drivers to set the GPIOs that -should be used. This is especially important with the Compute -Module. - -bcm2708-i2s: Avoid leak from iomap when accessing gpio - -bcm2708: Eliminate i2s debugfs directory error - -Qualify the two regmap ranges uses by bcm2708-i2s ('-i2s' and '-clk') -to avoid the name clash when registering debugfs entries. ---- - sound/soc/bcm/Kconfig | 11 + - sound/soc/bcm/Makefile | 4 + - sound/soc/bcm/bcm2708-i2s.c | 998 ++++++++++++++++++++++++++++++++++++++++++++ - sound/soc/bcm/bcm2708-i2s.h | 35 ++ - 4 files changed, 1048 insertions(+) - create mode 100644 sound/soc/bcm/bcm2708-i2s.c - create mode 100644 sound/soc/bcm/bcm2708-i2s.h - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 6a834e1..7e5b945 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -7,3 +7,14 @@ config SND_BCM2835_SOC_I2S - Say Y or M if you want to add support for codecs attached to - the BCM2835 I2S interface. You will also need - to select the audio interfaces to support below. -+ -+config SND_BCM2708_SOC_I2S -+ tristate "SoC Audio support for the Broadcom BCM2708 I2S module" -+ depends on MACH_BCM2708 -+ select REGMAP_MMIO -+ select SND_SOC_DMAENGINE_PCM -+ select SND_SOC_GENERIC_DMAENGINE_PCM -+ help -+ Say Y or M if you want to add support for codecs attached to -+ the BCM2708 I2S interface. You will also need -+ to select the audio interfaces to support below. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index bc816b7..f8bbe1f 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o - - obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o - -+# BCM2708 Platform Support -+snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o -+ -+obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o -diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c new file mode 100644 -index 0000000..0b5322a +index 0000000..7e03213 --- /dev/null -+++ b/sound/soc/bcm/bcm2708-i2s.c -@@ -0,0 +1,998 @@ -+/* -+ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c +@@ -0,0 +1,383 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * -+ * Author: Florian Meier -+ * Copyright 2013 ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. + * -+ * Based on -+ * Raspberry Pi PCM I2S ALSA Driver -+ * Copyright (c) by Phil Poole 2013 ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. + * -+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor -+ * Vladimir Barinov, -+ * Copyright (C) 2007 MontaVista Software, Inc., -+ * -+ * OMAP ALSA SoC DAI driver using McBSP port -+ * Copyright (C) 2008 Nokia Corporation -+ * Contact: Jarkko Nikula -+ * Peter Ujfalusi -+ * -+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver -+ * Author: Timur Tabi -+ * Copyright 2007-2010 Freescale Semiconductor, Inc. -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + -+#include "bcm2708-i2s.h" + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include "vchiq_core.h" ++#include "vchiq_arm.h" ++#include "vchiq_debugfs.h" + -+#include -+#include -+#include -+#include -+#include -+#include ++#ifdef CONFIG_DEBUG_FS + -+#include ++/**************************************************************************** ++* ++* log category entries ++* ++***************************************************************************/ ++#define DEBUGFS_WRITE_BUF_SIZE 256 + -+/* Clock registers */ -+#define BCM2708_CLK_PCMCTL_REG 0x00 -+#define BCM2708_CLK_PCMDIV_REG 0x04 ++#define VCHIQ_LOG_ERROR_STR "error" ++#define VCHIQ_LOG_WARNING_STR "warning" ++#define VCHIQ_LOG_INFO_STR "info" ++#define VCHIQ_LOG_TRACE_STR "trace" + -+/* Clock register settings */ -+#define BCM2708_CLK_PASSWD (0x5a000000) -+#define BCM2708_CLK_PASSWD_MASK (0xff000000) -+#define BCM2708_CLK_MASH(v) ((v) << 9) -+#define BCM2708_CLK_FLIP BIT(8) -+#define BCM2708_CLK_BUSY BIT(7) -+#define BCM2708_CLK_KILL BIT(5) -+#define BCM2708_CLK_ENAB BIT(4) -+#define BCM2708_CLK_SRC(v) (v) + -+#define BCM2708_CLK_SHIFT (12) -+#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) -+#define BCM2708_CLK_DIVF(v) (v) -+#define BCM2708_CLK_DIVF_MASK (0xFFF) ++/* Top-level debug info */ ++struct vchiq_debugfs_info { ++ /* Global 'vchiq' debugfs entry used by all instances */ ++ struct dentry *vchiq_cfg_dir; + -+enum { -+ BCM2708_CLK_MASH_0 = 0, -+ BCM2708_CLK_MASH_1, -+ BCM2708_CLK_MASH_2, -+ BCM2708_CLK_MASH_3, ++ /* one entry per client process */ ++ struct dentry *clients; ++ ++ /* log categories */ ++ struct dentry *log_categories; +}; + -+enum { -+ BCM2708_CLK_SRC_GND = 0, -+ BCM2708_CLK_SRC_OSC, -+ BCM2708_CLK_SRC_DBG0, -+ BCM2708_CLK_SRC_DBG1, -+ BCM2708_CLK_SRC_PLLA, -+ BCM2708_CLK_SRC_PLLC, -+ BCM2708_CLK_SRC_PLLD, -+ BCM2708_CLK_SRC_HDMI, ++static struct vchiq_debugfs_info debugfs_info; ++ ++/* Log category debugfs entries */ ++struct vchiq_debugfs_log_entry { ++ const char *name; ++ int *plevel; ++ struct dentry *dir; +}; + -+/* Most clocks are not useable (freq = 0) */ -+static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { -+ [BCM2708_CLK_SRC_GND] = 0, -+ [BCM2708_CLK_SRC_OSC] = 19200000, -+ [BCM2708_CLK_SRC_DBG0] = 0, -+ [BCM2708_CLK_SRC_DBG1] = 0, -+ [BCM2708_CLK_SRC_PLLA] = 0, -+ [BCM2708_CLK_SRC_PLLC] = 0, -+ [BCM2708_CLK_SRC_PLLD] = 500000000, -+ [BCM2708_CLK_SRC_HDMI] = 0, ++static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { ++ { "core", &vchiq_core_log_level }, ++ { "msg", &vchiq_core_msg_log_level }, ++ { "sync", &vchiq_sync_log_level }, ++ { "susp", &vchiq_susp_log_level }, ++ { "arm", &vchiq_arm_log_level }, +}; -+ -+/* I2S registers */ -+#define BCM2708_I2S_CS_A_REG 0x00 -+#define BCM2708_I2S_FIFO_A_REG 0x04 -+#define BCM2708_I2S_MODE_A_REG 0x08 -+#define BCM2708_I2S_RXC_A_REG 0x0c -+#define BCM2708_I2S_TXC_A_REG 0x10 -+#define BCM2708_I2S_DREQ_A_REG 0x14 -+#define BCM2708_I2S_INTEN_A_REG 0x18 -+#define BCM2708_I2S_INTSTC_A_REG 0x1c -+#define BCM2708_I2S_GRAY_REG 0x20 -+ -+/* I2S register settings */ -+#define BCM2708_I2S_STBY BIT(25) -+#define BCM2708_I2S_SYNC BIT(24) -+#define BCM2708_I2S_RXSEX BIT(23) -+#define BCM2708_I2S_RXF BIT(22) -+#define BCM2708_I2S_TXE BIT(21) -+#define BCM2708_I2S_RXD BIT(20) -+#define BCM2708_I2S_TXD BIT(19) -+#define BCM2708_I2S_RXR BIT(18) -+#define BCM2708_I2S_TXW BIT(17) -+#define BCM2708_I2S_CS_RXERR BIT(16) -+#define BCM2708_I2S_CS_TXERR BIT(15) -+#define BCM2708_I2S_RXSYNC BIT(14) -+#define BCM2708_I2S_TXSYNC BIT(13) -+#define BCM2708_I2S_DMAEN BIT(9) -+#define BCM2708_I2S_RXTHR(v) ((v) << 7) -+#define BCM2708_I2S_TXTHR(v) ((v) << 5) -+#define BCM2708_I2S_RXCLR BIT(4) -+#define BCM2708_I2S_TXCLR BIT(3) -+#define BCM2708_I2S_TXON BIT(2) -+#define BCM2708_I2S_RXON BIT(1) -+#define BCM2708_I2S_EN (1) -+ -+#define BCM2708_I2S_CLKDIS BIT(28) -+#define BCM2708_I2S_PDMN BIT(27) -+#define BCM2708_I2S_PDME BIT(26) -+#define BCM2708_I2S_FRXP BIT(25) -+#define BCM2708_I2S_FTXP BIT(24) -+#define BCM2708_I2S_CLKM BIT(23) -+#define BCM2708_I2S_CLKI BIT(22) -+#define BCM2708_I2S_FSM BIT(21) -+#define BCM2708_I2S_FSI BIT(20) -+#define BCM2708_I2S_FLEN(v) ((v) << 10) -+#define BCM2708_I2S_FSLEN(v) (v) -+ -+#define BCM2708_I2S_CHWEX BIT(15) -+#define BCM2708_I2S_CHEN BIT(14) -+#define BCM2708_I2S_CHPOS(v) ((v) << 4) -+#define BCM2708_I2S_CHWID(v) (v) -+#define BCM2708_I2S_CH1(v) ((v) << 16) -+#define BCM2708_I2S_CH2(v) (v) -+ -+#define BCM2708_I2S_TX_PANIC(v) ((v) << 24) -+#define BCM2708_I2S_RX_PANIC(v) ((v) << 16) -+#define BCM2708_I2S_TX(v) ((v) << 8) -+#define BCM2708_I2S_RX(v) (v) -+ -+#define BCM2708_I2S_INT_RXERR BIT(3) -+#define BCM2708_I2S_INT_TXERR BIT(2) -+#define BCM2708_I2S_INT_RXR BIT(1) -+#define BCM2708_I2S_INT_TXW BIT(0) -+ -+/* I2S DMA interface */ -+#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004 -+#define BCM2708_DMA_DREQ_PCM_TX 2 -+#define BCM2708_DMA_DREQ_PCM_RX 3 -+ -+/* I2S pin configuration */ -+static int bcm2708_i2s_gpio=BCM2708_I2S_GPIO_AUTO; -+ -+/* General device struct */ -+struct bcm2708_i2s_dev { -+ struct device *dev; -+ struct snd_dmaengine_dai_dma_data dma_data[2]; -+ unsigned int fmt; -+ unsigned int bclk_ratio; -+ -+ struct regmap *i2s_regmap; -+ struct regmap *clk_regmap; -+}; -+ -+void bcm2708_i2s_set_gpio(int gpio) { -+ bcm2708_i2s_gpio=gpio; -+} -+EXPORT_SYMBOL(bcm2708_i2s_set_gpio); ++static int n_log_entries = ++ sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); + + -+static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev) ++static struct dentry *vchiq_clients_top(void); ++static struct dentry *vchiq_debugfs_top(void); ++ ++static int debugfs_log_show(struct seq_file *f, void *offset) +{ -+ /* Start the clock if in master mode */ -+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ int *levp = f->private; ++ char *log_value = NULL; + -+ switch (master) { -+ case SND_SOC_DAIFMT_CBS_CFS: -+ case SND_SOC_DAIFMT_CBS_CFM: -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ switch (*levp) { ++ case VCHIQ_LOG_ERROR: ++ log_value = VCHIQ_LOG_ERROR_STR; ++ break; ++ case VCHIQ_LOG_WARNING: ++ log_value = VCHIQ_LOG_WARNING_STR; ++ break; ++ case VCHIQ_LOG_INFO: ++ log_value = VCHIQ_LOG_INFO_STR; ++ break; ++ case VCHIQ_LOG_TRACE: ++ log_value = VCHIQ_LOG_TRACE_STR; + break; + default: + break; + } -+} + -+static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev) -+{ -+ uint32_t clkreg; -+ int timeout = 1000; -+ -+ /* Stop clock */ -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD); -+ -+ /* Wait for the BUSY flag going down */ -+ while (--timeout) { -+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); -+ if (!(clkreg & BCM2708_CLK_BUSY)) -+ break; -+ } -+ -+ if (!timeout) { -+ /* KILL the clock */ -+ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK, -+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); -+ } -+} -+ -+static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev, -+ bool tx, bool rx) -+{ -+ int timeout = 1000; -+ uint32_t syncval; -+ uint32_t csreg; -+ uint32_t i2s_active_state; -+ uint32_t clkreg; -+ uint32_t clk_active_state; -+ uint32_t off; -+ uint32_t clr; -+ -+ off = tx ? BCM2708_I2S_TXON : 0; -+ off |= rx ? BCM2708_I2S_RXON : 0; -+ -+ clr = tx ? BCM2708_I2S_TXCLR : 0; -+ clr |= rx ? BCM2708_I2S_RXCLR : 0; -+ -+ /* Backup the current state */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON); -+ -+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); -+ clk_active_state = clkreg & BCM2708_CLK_ENAB; -+ -+ /* Start clock if not running */ -+ if (!clk_active_state) { -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); -+ } -+ -+ /* Stop I2S module */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0); -+ -+ /* -+ * Clear the FIFOs -+ * Requires at least 2 PCM clock cycles to take effect -+ */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr); -+ -+ /* Wait for 2 PCM clock cycles */ -+ -+ /* -+ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back -+ * FIXME: This does not seem to work for slave mode! -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval); -+ syncval &= BCM2708_I2S_SYNC; -+ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_SYNC, ~syncval); -+ -+ /* Wait for the SYNC flag changing it's state */ -+ while (--timeout) { -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ if ((csreg & BCM2708_I2S_SYNC) != syncval) -+ break; -+ } -+ -+ if (!timeout) -+ dev_err(dev->dev, "I2S SYNC error!\n"); -+ -+ /* Stop clock if it was not running before */ -+ if (!clk_active_state) -+ bcm2708_i2s_stop_clock(dev); -+ -+ /* Restore I2S state */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state); -+} -+ -+static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai, -+ unsigned int fmt) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ dev->fmt = fmt; -+ return 0; -+} -+ -+static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, -+ unsigned int ratio) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ dev->bclk_ratio = ratio; -+ return 0; -+} -+ -+ -+static int bcm2708_i2s_set_function(unsigned offset, int function) -+{ -+ #define GPIOFSEL(x) (0x00+(x)*4) -+ void __iomem *gpio = __io_address(GPIO_BASE); -+ unsigned alt = function <= 3 ? function + 4: function == 4 ? 3 : 2; -+ unsigned gpiodir; -+ unsigned gpio_bank = offset / 10; -+ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ gpiodir = readl(gpio + GPIOFSEL(gpio_bank)); -+ gpiodir &= ~(7 << gpio_field_offset); -+ gpiodir |= alt << gpio_field_offset; -+ writel(gpiodir, gpio + GPIOFSEL(gpio_bank)); -+ return 0; -+} -+ -+static void bcm2708_i2s_setup_gpio(void) -+{ -+ /* -+ * This is the common way to handle the GPIO pins for -+ * the Raspberry Pi. -+ * TODO Better way would be to handle -+ * this in the device tree! -+ */ -+ int pin,pinconfig,startpin,alt; -+ -+ /* SPI is on different GPIOs on different boards */ -+ /* for Raspberry Pi B+, this is pin GPIO18-21, for original on 28-31 */ -+ if (bcm2708_i2s_gpio==BCM2708_I2S_GPIO_AUTO) { -+ if ((system_rev & 0xffffff) >= 0x10) { -+ /* Model B+ */ -+ pinconfig=BCM2708_I2S_GPIO_PIN18; -+ } else { -+ /* original */ -+ pinconfig=BCM2708_I2S_GPIO_PIN28; -+ } -+ } else { -+ pinconfig=bcm2708_i2s_gpio; -+ } -+ -+ if (pinconfig==BCM2708_I2S_GPIO_PIN18) { -+ startpin=18; -+ alt=BCM2708_I2S_GPIO_PIN18_ALT; -+ } else if (pinconfig==BCM2708_I2S_GPIO_PIN28) { -+ startpin=28; -+ alt=BCM2708_I2S_GPIO_PIN28_ALT; -+ } else { -+ printk(KERN_INFO "Can't configure I2S GPIOs, unknown pin mode for I2S: %i\n",pinconfig); -+ return; -+ } -+ -+ /* configure I2S pins to correct ALT mode */ -+ for (pin = startpin; pin <= startpin+3; pin++) { -+ bcm2708_i2s_set_function(pin, alt); -+ } -+} -+ -+static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ unsigned int sampling_rate = params_rate(params); -+ unsigned int data_length, data_delay, bclk_ratio; -+ unsigned int ch1pos, ch2pos, mode, format; -+ unsigned int mash = BCM2708_CLK_MASH_1; -+ unsigned int divi, divf, target_frequency; -+ int clk_src = -1; -+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; -+ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS -+ || master == SND_SOC_DAIFMT_CBS_CFM); -+ -+ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS -+ || master == SND_SOC_DAIFMT_CBM_CFS); -+ uint32_t csreg; -+ -+ /* -+ * If a stream is already enabled, -+ * the registers are already set properly. -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ -+ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON)) -+ return 0; -+ -+ -+ bcm2708_i2s_setup_gpio(); -+ -+ /* -+ * Adjust the data length according to the format. -+ * We prefill the half frame length with an integer -+ * divider of 2400 as explained at the clock settings. -+ * Maybe it is overwritten there, if the Integer mode -+ * does not apply. -+ */ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ data_length = 16; -+ bclk_ratio = 50; -+ break; -+ case SNDRV_PCM_FORMAT_S24_LE: -+ data_length = 24; -+ bclk_ratio = 50; -+ break; -+ case SNDRV_PCM_FORMAT_S32_LE: -+ data_length = 32; -+ bclk_ratio = 100; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* If bclk_ratio already set, use that one. */ -+ if (dev->bclk_ratio) -+ bclk_ratio = dev->bclk_ratio; -+ -+ /* -+ * Clock Settings -+ * -+ * The target frequency of the bit clock is -+ * sampling rate * frame length -+ * -+ * Integer mode: -+ * Sampling rates that are multiples of 8000 kHz -+ * can be driven by the oscillator of 19.2 MHz -+ * with an integer divider as long as the frame length -+ * is an integer divider of 19200000/8000=2400 as set up above. -+ * This is no longer possible if the sampling rate -+ * is too high (e.g. 192 kHz), because the oscillator is too slow. -+ * -+ * MASH mode: -+ * For all other sampling rates, it is not possible to -+ * have an integer divider. Approximate the clock -+ * with the MASH module that induces a slight frequency -+ * variance. To minimize that it is best to have the fastest -+ * clock here. That is PLLD with 500 MHz. -+ */ -+ target_frequency = sampling_rate * bclk_ratio; -+ clk_src = BCM2708_CLK_SRC_OSC; -+ mash = BCM2708_CLK_MASH_0; -+ -+ if (bcm2708_clk_freq[clk_src] % target_frequency == 0 -+ && bit_master && frame_master) { -+ divi = bcm2708_clk_freq[clk_src] / target_frequency; -+ divf = 0; -+ } else { -+ uint64_t dividend; -+ -+ if (!dev->bclk_ratio) { -+ /* -+ * Overwrite bclk_ratio, because the -+ * above trick is not needed or can -+ * not be used. -+ */ -+ bclk_ratio = 2 * data_length; -+ } -+ -+ target_frequency = sampling_rate * bclk_ratio; -+ -+ clk_src = BCM2708_CLK_SRC_PLLD; -+ mash = BCM2708_CLK_MASH_1; -+ -+ dividend = bcm2708_clk_freq[clk_src]; -+ dividend <<= BCM2708_CLK_SHIFT; -+ do_div(dividend, target_frequency); -+ divi = dividend >> BCM2708_CLK_SHIFT; -+ divf = dividend & BCM2708_CLK_DIVF_MASK; -+ } -+ -+ /* Set clock divider */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_DIVI(divi) -+ | BCM2708_CLK_DIVF(divf)); -+ -+ /* Setup clock, but don't start it yet */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_MASH(mash) -+ | BCM2708_CLK_SRC(clk_src)); -+ -+ /* Setup the frame format */ -+ format = BCM2708_I2S_CHEN; -+ -+ if (data_length >= 24) -+ format |= BCM2708_I2S_CHWEX; -+ -+ format |= BCM2708_I2S_CHWID((data_length-8)&0xf); -+ -+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { -+ case SND_SOC_DAIFMT_I2S: -+ data_delay = 1; -+ break; -+ default: -+ /* -+ * TODO -+ * Others are possible but are not implemented at the moment. -+ */ -+ dev_err(dev->dev, "%s:bad format\n", __func__); -+ return -EINVAL; -+ } -+ -+ ch1pos = data_delay; -+ ch2pos = bclk_ratio / 2 + data_delay; -+ -+ switch (params_channels(params)) { -+ case 2: -+ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format); -+ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos)); -+ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos)); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* -+ * Set format for both streams. -+ * We cannot set another frame length -+ * (and therefore word length) anyway, -+ * so the format will be the same. -+ */ -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format); -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format); -+ -+ /* Setup the I2S mode */ -+ mode = 0; -+ -+ if (data_length <= 16) { -+ /* -+ * Use frame packed mode (2 channels per 32 bit word) -+ * We cannot set another frame length in the second stream -+ * (and therefore word length) anyway, -+ * so the format will be the same. -+ */ -+ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP; -+ } -+ -+ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1); -+ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2); -+ -+ /* Master or slave? */ -+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { -+ case SND_SOC_DAIFMT_CBS_CFS: -+ /* CPU is master */ -+ break; -+ case SND_SOC_DAIFMT_CBM_CFS: -+ /* -+ * CODEC is bit clock master -+ * CPU is frame master -+ */ -+ mode |= BCM2708_I2S_CLKM; -+ break; -+ case SND_SOC_DAIFMT_CBS_CFM: -+ /* -+ * CODEC is frame master -+ * CPU is bit clock master -+ */ -+ mode |= BCM2708_I2S_FSM; -+ break; -+ case SND_SOC_DAIFMT_CBM_CFM: -+ /* CODEC is master */ -+ mode |= BCM2708_I2S_CLKM; -+ mode |= BCM2708_I2S_FSM; -+ break; -+ default: -+ dev_err(dev->dev, "%s:bad master\n", __func__); -+ return -EINVAL; -+ } -+ -+ /* -+ * Invert clocks? -+ * -+ * The BCM approach seems to be inverted to the classical I2S approach. -+ */ -+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { -+ case SND_SOC_DAIFMT_NB_NF: -+ /* None. Therefore, both for BCM */ -+ mode |= BCM2708_I2S_CLKI; -+ mode |= BCM2708_I2S_FSI; -+ break; -+ case SND_SOC_DAIFMT_IB_IF: -+ /* Both. Therefore, none for BCM */ -+ break; -+ case SND_SOC_DAIFMT_NB_IF: -+ /* -+ * Invert only frame sync. Therefore, -+ * invert only bit clock for BCM -+ */ -+ mode |= BCM2708_I2S_CLKI; -+ break; -+ case SND_SOC_DAIFMT_IB_NF: -+ /* -+ * Invert only bit clock. Therefore, -+ * invert only frame sync for BCM -+ */ -+ mode |= BCM2708_I2S_FSI; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode); -+ -+ /* Setup the DMA parameters */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_RXTHR(1) -+ | BCM2708_I2S_TXTHR(1) -+ | BCM2708_I2S_DMAEN, 0xffffffff); -+ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG, -+ BCM2708_I2S_TX_PANIC(0x10) -+ | BCM2708_I2S_RX_PANIC(0x30) -+ | BCM2708_I2S_TX(0x30) -+ | BCM2708_I2S_RX(0x20), 0xffffffff); -+ -+ /* Clear FIFOs */ -+ bcm2708_i2s_clear_fifos(dev, true, true); ++ seq_printf(f, "%s\n", log_value ? log_value : "(null)"); + + return 0; +} + -+static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++static int debugfs_log_open(struct inode *inode, struct file *file) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ uint32_t cs_reg; -+ -+ bcm2708_i2s_start_clock(dev); -+ -+ /* -+ * Clear both FIFOs if the one that should be started -+ * is not empty at the moment. This should only happen -+ * after overrun. Otherwise, hw_params would have cleared -+ * the FIFO. -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg); -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK -+ && !(cs_reg & BCM2708_I2S_TXE)) -+ bcm2708_i2s_clear_fifos(dev, true, false); -+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE -+ && (cs_reg & BCM2708_I2S_RXD)) -+ bcm2708_i2s_clear_fifos(dev, false, true); -+ -+ return 0; ++ return single_open(file, debugfs_log_show, inode->i_private); +} + -+static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev, -+ struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++static int debugfs_log_write(struct file *file, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) +{ -+ uint32_t mask; ++ struct seq_file *f = (struct seq_file *)file->private_data; ++ int *levp = f->private; ++ char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1]; + -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ mask = BCM2708_I2S_RXON; ++ memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1); ++ if (count >= DEBUGFS_WRITE_BUF_SIZE) ++ count = DEBUGFS_WRITE_BUF_SIZE; ++ ++ if (copy_from_user(kbuf, buffer, count) != 0) ++ return -EFAULT; ++ kbuf[count - 1] = 0; ++ ++ if (strncmp("error", kbuf, strlen("error")) == 0) ++ *levp = VCHIQ_LOG_ERROR; ++ else if (strncmp("warning", kbuf, strlen("warning")) == 0) ++ *levp = VCHIQ_LOG_WARNING; ++ else if (strncmp("info", kbuf, strlen("info")) == 0) ++ *levp = VCHIQ_LOG_INFO; ++ else if (strncmp("trace", kbuf, strlen("trace")) == 0) ++ *levp = VCHIQ_LOG_TRACE; + else -+ mask = BCM2708_I2S_TXON; ++ *levp = VCHIQ_LOG_DEFAULT; + -+ regmap_update_bits(dev->i2s_regmap, -+ BCM2708_I2S_CS_A_REG, mask, 0); ++ *ppos += count; + -+ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ -+ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT)) -+ bcm2708_i2s_stop_clock(dev); ++ return count; +} + -+static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) ++static const struct file_operations debugfs_log_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_log_open, ++ .write = debugfs_log_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* create an entry under /vchiq/log for each log category */ ++static int vchiq_debugfs_create_log_entries(struct dentry *top) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ uint32_t mask; ++ struct dentry *dir; ++ size_t i; ++ int ret = 0; ++ dir = debugfs_create_dir("log", vchiq_debugfs_top()); ++ if (!dir) ++ return -ENOMEM; ++ debugfs_info.log_categories = dir; + -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ bcm2708_i2s_start_clock(dev); ++ for (i = 0; i < n_log_entries; i++) { ++ void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; ++ dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, ++ 0644, ++ debugfs_info.log_categories, ++ levp, ++ &debugfs_log_fops); ++ if (!dir) { ++ ret = -ENOMEM; ++ break; ++ } + -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ mask = BCM2708_I2S_RXON; -+ else -+ mask = BCM2708_I2S_TXON; ++ vchiq_debugfs_log_entries[i].dir = dir; ++ } ++ return ret; ++} + -+ regmap_update_bits(dev->i2s_regmap, -+ BCM2708_I2S_CS_A_REG, mask, mask); ++static int debugfs_usecount_show(struct seq_file *f, void *offset) ++{ ++ VCHIQ_INSTANCE_T instance = f->private; ++ int use_count; ++ ++ use_count = vchiq_instance_get_use_count(instance); ++ seq_printf(f, "%d\n", use_count); ++ ++ return 0; ++} ++ ++static int debugfs_usecount_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debugfs_usecount_show, inode->i_private); ++} ++ ++static const struct file_operations debugfs_usecount_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_usecount_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int debugfs_trace_show(struct seq_file *f, void *offset) ++{ ++ VCHIQ_INSTANCE_T instance = f->private; ++ int trace; ++ ++ trace = vchiq_instance_get_trace(instance); ++ seq_printf(f, "%s\n", trace ? "Y" : "N"); ++ ++ return 0; ++} ++ ++static int debugfs_trace_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debugfs_trace_show, inode->i_private); ++} ++ ++static int debugfs_trace_write(struct file *file, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *f = (struct seq_file *)file->private_data; ++ VCHIQ_INSTANCE_T instance = f->private; ++ char firstchar; ++ ++ if (copy_from_user(&firstchar, buffer, 1) != 0) ++ return -EFAULT; ++ ++ switch (firstchar) { ++ case 'Y': ++ case 'y': ++ case '1': ++ vchiq_instance_set_trace(instance, 1); + break; -+ -+ case SNDRV_PCM_TRIGGER_STOP: -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ bcm2708_i2s_stop(dev, substream, dai); ++ case 'N': ++ case 'n': ++ case '0': ++ vchiq_instance_set_trace(instance, 0); + break; + default: -+ return -EINVAL; ++ break; + } + ++ *ppos += count; ++ ++ return count; ++} ++ ++static const struct file_operations debugfs_trace_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_trace_open, ++ .write = debugfs_trace_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* add an instance (process) to the debugfs entries */ ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) ++{ ++ char pidstr[16]; ++ struct dentry *top, *use_count, *trace; ++ struct dentry *clients = vchiq_clients_top(); ++ ++ snprintf(pidstr, sizeof(pidstr), "%d", ++ vchiq_instance_get_pid(instance)); ++ ++ top = debugfs_create_dir(pidstr, clients); ++ if (!top) ++ goto fail_top; ++ ++ use_count = debugfs_create_file("use_count", ++ 0444, top, ++ instance, ++ &debugfs_usecount_fops); ++ if (!use_count) ++ goto fail_use_count; ++ ++ trace = debugfs_create_file("trace", ++ 0644, top, ++ instance, ++ &debugfs_trace_fops); ++ if (!trace) ++ goto fail_trace; ++ ++ vchiq_instance_get_debugfs_node(instance)->dentry = top; ++ ++ return 0; ++ ++fail_trace: ++ debugfs_remove(use_count); ++fail_use_count: ++ debugfs_remove(top); ++fail_top: ++ return -ENOMEM; ++} ++ ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); ++ debugfs_remove_recursive(node->dentry); ++} ++ ++ ++int vchiq_debugfs_init(void) ++{ ++ BUG_ON(debugfs_info.vchiq_cfg_dir != NULL); ++ ++ debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL); ++ if (debugfs_info.vchiq_cfg_dir == NULL) ++ goto fail; ++ ++ debugfs_info.clients = debugfs_create_dir("clients", ++ vchiq_debugfs_top()); ++ if (!debugfs_info.clients) ++ goto fail; ++ ++ if (vchiq_debugfs_create_log_entries(vchiq_debugfs_top()) != 0) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ vchiq_debugfs_deinit(); ++ vchiq_log_error(vchiq_arm_log_level, ++ "%s: failed to create debugfs directory", ++ __func__); ++ ++ return -ENOMEM; ++} ++ ++/* remove all the debugfs entries */ ++void vchiq_debugfs_deinit(void) ++{ ++ debugfs_remove_recursive(vchiq_debugfs_top()); ++} ++ ++static struct dentry *vchiq_clients_top(void) ++{ ++ return debugfs_info.clients; ++} ++ ++static struct dentry *vchiq_debugfs_top(void) ++{ ++ BUG_ON(debugfs_info.vchiq_cfg_dir == NULL); ++ return debugfs_info.vchiq_cfg_dir; ++} ++ ++#else /* CONFIG_DEBUG_FS */ ++ ++int vchiq_debugfs_init(void) ++{ + return 0; +} + -+static int bcm2708_i2s_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++void vchiq_debugfs_deinit(void) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ if (dai->active) -+ return 0; -+ -+ /* Should this still be running stop it */ -+ bcm2708_i2s_stop_clock(dev); -+ -+ /* Enable PCM block */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_EN, BCM2708_I2S_EN); -+ -+ /* -+ * Disable STBY. -+ * Requires at least 4 PCM clock cycles to take effect. -+ */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_STBY, BCM2708_I2S_STBY); ++} + ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) ++{ + return 0; +} + -+static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ bcm2708_i2s_stop(dev, substream, dai); -+ -+ /* If both streams are stopped, disable module and clock */ -+ if (dai->active) -+ return; -+ -+ /* Disable the module */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_EN, 0); -+ -+ /* -+ * Stopping clock is necessary, because stop does -+ * not stop the clock when SND_SOC_DAIFMT_CONT -+ */ -+ bcm2708_i2s_stop_clock(dev); +} + -+static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = { -+ .startup = bcm2708_i2s_startup, -+ .shutdown = bcm2708_i2s_shutdown, -+ .prepare = bcm2708_i2s_prepare, -+ .trigger = bcm2708_i2s_trigger, -+ .hw_params = bcm2708_i2s_hw_params, -+ .set_fmt = bcm2708_i2s_set_dai_fmt, -+ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio -+}; ++#endif /* CONFIG_DEBUG_FS */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +new file mode 100644 +index 0000000..4d6a378 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +@@ -0,0 +1,52 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + -+static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) ++#ifndef VCHIQ_DEBUGFS_H ++#define VCHIQ_DEBUGFS_H ++ ++#include "vchiq_core.h" ++ ++typedef struct vchiq_debugfs_node_struct +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ struct dentry *dentry; ++} VCHIQ_DEBUGFS_NODE_T; + -+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; -+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; ++int vchiq_debugfs_init(void); + -+ return 0; ++void vchiq_debugfs_deinit(void); ++ ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance); ++ ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance); ++ ++#endif /* VCHIQ_DEBUGFS_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion +new file mode 100644 +index 0000000..9f5b634 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion +@@ -0,0 +1,87 @@ ++#!/usr/bin/perl -w ++ ++use strict; ++ ++# ++# Generate a version from available information ++# ++ ++my $prefix = shift @ARGV; ++my $root = shift @ARGV; ++ ++ ++if ( not defined $root ) { ++ die "usage: $0 prefix root-dir\n"; +} + -+static struct snd_soc_dai_driver bcm2708_i2s_dai = { -+ .name = "bcm2708-i2s", -+ .probe = bcm2708_i2s_dai_probe, -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE -+ }, -+ .capture = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE -+ }, -+ .ops = &bcm2708_i2s_dai_ops, -+ .symmetric_rates = 1 -+}; -+ -+static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case BCM2708_I2S_CS_A_REG: -+ case BCM2708_I2S_FIFO_A_REG: -+ case BCM2708_I2S_INTSTC_A_REG: -+ case BCM2708_I2S_GRAY_REG: -+ return true; -+ default: -+ return false; -+ }; ++if ( ! -d $root ) { ++ die "root directory $root not found\n"; +} + -+static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case BCM2708_I2S_FIFO_A_REG: -+ return true; -+ default: -+ return false; -+ }; ++my $version = "unknown"; ++my $tainted = ""; ++ ++if ( -d "$root/.git" ) { ++ # attempt to work out git version. only do so ++ # on a linux build host, as cygwin builds are ++ # already slow enough ++ ++ if ( -f "/usr/bin/git" || -f "/usr/local/bin/git" ) { ++ if (not open(F, "git --git-dir $root/.git rev-parse --verify HEAD|")) { ++ $version = "no git version"; ++ } ++ else { ++ $version = ; ++ $version =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++ $version =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ } ++ ++ if (open(G, "git --git-dir $root/.git status --porcelain|")) { ++ $tainted = ; ++ $tainted =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++ $tainted =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ if (length $tainted) { ++ $version = join ' ', $version, "(tainted)"; ++ } ++ else { ++ $version = join ' ', $version, "(clean)"; ++ } ++ } ++ } +} + -+static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg) ++my $hostname = `hostname`; ++$hostname =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++$hostname =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ ++ ++print STDERR "Version $version\n"; ++print < ++ ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_hostname, "$hostname" ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_version, "$version" ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_time, __TIME__ ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_date, __DATE__ ); ++ ++const char *vchiq_get_build_hostname( void ) +{ -+ switch (reg) { -+ case BCM2708_CLK_PCMCTL_REG: -+ return true; -+ default: -+ return false; -+ }; ++ return vchiq_build_hostname; +} + -+static const struct regmap_config bcm2708_regmap_config[] = { -+ { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = BCM2708_I2S_GRAY_REG, -+ .precious_reg = bcm2708_i2s_precious_reg, -+ .volatile_reg = bcm2708_i2s_volatile_reg, -+ .cache_type = REGCACHE_RBTREE, -+ .name = "i2s", -+ }, -+ { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = BCM2708_CLK_PCMDIV_REG, -+ .volatile_reg = bcm2708_clk_volatile_reg, -+ .cache_type = REGCACHE_RBTREE, -+ .name = "clk", -+ }, -+}; -+ -+static const struct snd_soc_component_driver bcm2708_i2s_component = { -+ .name = "bcm2708-i2s-comp", -+}; -+ -+static const struct snd_pcm_hardware bcm2708_pcm_hardware = { -+ .info = SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_JOINT_DUPLEX, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE, -+ .period_bytes_min = 32, -+ .period_bytes_max = 64 * PAGE_SIZE, -+ .periods_min = 2, -+ .periods_max = 255, -+ .buffer_bytes_max = 128 * PAGE_SIZE, -+}; -+ -+static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = { -+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, -+ .pcm_hardware = &bcm2708_pcm_hardware, -+ .prealloc_buffer_size = 256 * PAGE_SIZE, -+}; -+ -+ -+static int bcm2708_i2s_probe(struct platform_device *pdev) ++const char *vchiq_get_build_version( void ) +{ -+ struct bcm2708_i2s_dev *dev; -+ int i; -+ int ret; -+ struct regmap *regmap[2]; -+ struct resource *mem[2]; ++ return vchiq_build_version; ++} + -+ /* Request both ioareas */ -+ for (i = 0; i <= 1; i++) { -+ void __iomem *base; ++const char *vchiq_get_build_date( void ) ++{ ++ return vchiq_build_date; ++} + -+ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); -+ base = devm_ioremap_resource(&pdev->dev, mem[i]); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); ++const char *vchiq_get_build_time( void ) ++{ ++ return vchiq_build_time; ++} ++EOF +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +new file mode 100644 +index 0000000..8067bbe +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +@@ -0,0 +1,189 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + -+ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, -+ &bcm2708_regmap_config[i]); -+ if (IS_ERR(regmap[i])) { -+ dev_err(&pdev->dev, "I2S probe: regmap init failed\n"); -+ return PTR_ERR(regmap[i]); ++#ifndef VCHIQ_IF_H ++#define VCHIQ_IF_H ++ ++#include "interface/vchi/vchi_mh.h" ++ ++#define VCHIQ_SERVICE_HANDLE_INVALID 0 ++ ++#define VCHIQ_SLOT_SIZE 4096 ++#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) ++#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ ++ ++#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) \ ++ (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) ++#define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) ++#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) ++ ++typedef enum { ++ VCHIQ_SERVICE_OPENED, /* service, -, - */ ++ VCHIQ_SERVICE_CLOSED, /* service, -, - */ ++ VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */ ++ VCHIQ_BULK_TRANSMIT_DONE, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */ ++} VCHIQ_REASON_T; ++ ++typedef enum { ++ VCHIQ_ERROR = -1, ++ VCHIQ_SUCCESS = 0, ++ VCHIQ_RETRY = 1 ++} VCHIQ_STATUS_T; ++ ++typedef enum { ++ VCHIQ_BULK_MODE_CALLBACK, ++ VCHIQ_BULK_MODE_BLOCKING, ++ VCHIQ_BULK_MODE_NOCALLBACK, ++ VCHIQ_BULK_MODE_WAITING /* Reserved for internal use */ ++} VCHIQ_BULK_MODE_T; ++ ++typedef enum { ++ VCHIQ_SERVICE_OPTION_AUTOCLOSE, ++ VCHIQ_SERVICE_OPTION_SLOT_QUOTA, ++ VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, ++ VCHIQ_SERVICE_OPTION_SYNCHRONOUS, ++ VCHIQ_SERVICE_OPTION_TRACE ++} VCHIQ_SERVICE_OPTION_T; ++ ++typedef struct vchiq_header_struct { ++ /* The message identifier - opaque to applications. */ ++ int msgid; ++ ++ /* Size of message data. */ ++ unsigned int size; ++ ++ char data[0]; /* message */ ++} VCHIQ_HEADER_T; ++ ++typedef struct { ++ const void *data; ++ unsigned int size; ++} VCHIQ_ELEMENT_T; ++ ++typedef unsigned int VCHIQ_SERVICE_HANDLE_T; ++ ++typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, ++ VCHIQ_SERVICE_HANDLE_T, void *); ++ ++typedef struct vchiq_service_base_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++} VCHIQ_SERVICE_BASE_T; ++ ++typedef struct vchiq_service_params_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++ short version; /* Increment for non-trivial changes */ ++ short version_min; /* Update for incompatible changes */ ++} VCHIQ_SERVICE_PARAMS_T; ++ ++typedef struct vchiq_config_struct { ++ unsigned int max_msg_size; ++ unsigned int bulk_threshold; /* The message size above which it ++ is better to use a bulk transfer ++ (<= max_msg_size) */ ++ unsigned int max_outstanding_bulks; ++ unsigned int max_services; ++ short version; /* The version of VCHIQ */ ++ short version_min; /* The minimum compatible version of VCHIQ */ ++} VCHIQ_CONFIG_T; ++ ++typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; ++typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void *cb_arg); ++ ++extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); ++extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_use_service_no_resume( ++ VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); ++ ++extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, ++ const VCHIQ_ELEMENT_T *elements, unsigned int count); ++extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, ++ VCHIQ_HEADER_T *header); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, ++ const void *data, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, ++ void *data, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle( ++ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, ++ const void *offset, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle( ++ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, ++ void *offset, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, ++ const void *data, unsigned int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, ++ void *data, unsigned int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, ++ VCHI_MEM_HANDLE_T handle, const void *offset, unsigned int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, ++ VCHI_MEM_HANDLE_T handle, void *offset, unsigned int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode); ++extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); ++extern void *vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T service); ++extern int vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, VCHIQ_CONFIG_T *pconfig); ++extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, ++ VCHIQ_SERVICE_OPTION_T option, int value); ++ ++extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, ++ VCHIQ_REMOTE_USE_CALLBACK_T callback, void *cb_arg); ++extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T vchiq_dump_phys_mem(VCHIQ_SERVICE_HANDLE_T service, ++ void *ptr, size_t num_bytes); ++ ++extern VCHIQ_STATUS_T vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, ++ short *peer_version); ++ ++#endif /* VCHIQ_IF_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +new file mode 100644 +index 0000000..6137ae9 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +@@ -0,0 +1,131 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_IOCTLS_H ++#define VCHIQ_IOCTLS_H ++ ++#include ++#include "vchiq_if.h" ++ ++#define VCHIQ_IOC_MAGIC 0xc4 ++#define VCHIQ_INVALID_HANDLE (~0) ++ ++typedef struct { ++ VCHIQ_SERVICE_PARAMS_T params; ++ int is_open; ++ int is_vchi; ++ unsigned int handle; /* OUT */ ++} VCHIQ_CREATE_SERVICE_T; ++ ++typedef struct { ++ unsigned int handle; ++ unsigned int count; ++ const VCHIQ_ELEMENT_T *elements; ++} VCHIQ_QUEUE_MESSAGE_T; ++ ++typedef struct { ++ unsigned int handle; ++ void *data; ++ unsigned int size; ++ void *userdata; ++ VCHIQ_BULK_MODE_T mode; ++} VCHIQ_QUEUE_BULK_TRANSFER_T; ++ ++typedef struct { ++ VCHIQ_REASON_T reason; ++ VCHIQ_HEADER_T *header; ++ void *service_userdata; ++ void *bulk_userdata; ++} VCHIQ_COMPLETION_DATA_T; ++ ++typedef struct { ++ unsigned int count; ++ VCHIQ_COMPLETION_DATA_T *buf; ++ unsigned int msgbufsize; ++ unsigned int msgbufcount; /* IN/OUT */ ++ void **msgbufs; ++} VCHIQ_AWAIT_COMPLETION_T; ++ ++typedef struct { ++ unsigned int handle; ++ int blocking; ++ unsigned int bufsize; ++ void *buf; ++} VCHIQ_DEQUEUE_MESSAGE_T; ++ ++typedef struct { ++ unsigned int config_size; ++ VCHIQ_CONFIG_T *pconfig; ++} VCHIQ_GET_CONFIG_T; ++ ++typedef struct { ++ unsigned int handle; ++ VCHIQ_SERVICE_OPTION_T option; ++ int value; ++} VCHIQ_SET_SERVICE_OPTION_T; ++ ++typedef struct { ++ void *virt_addr; ++ size_t num_bytes; ++} VCHIQ_DUMP_MEM_T; ++ ++#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) ++#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) ++#define VCHIQ_IOC_CREATE_SERVICE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) ++#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) ++#define VCHIQ_IOC_QUEUE_MESSAGE \ ++ _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) ++#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \ ++ _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_AWAIT_COMPLETION \ ++ _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) ++#define VCHIQ_IOC_DEQUEUE_MESSAGE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) ++#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) ++#define VCHIQ_IOC_GET_CONFIG \ ++ _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) ++#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) ++#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) ++#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) ++#define VCHIQ_IOC_SET_SERVICE_OPTION \ ++ _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) ++#define VCHIQ_IOC_DUMP_PHYS_MEM \ ++ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) ++#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) ++#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) ++#define VCHIQ_IOC_MAX 17 ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +new file mode 100644 +index 0000000..25e7011 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +@@ -0,0 +1,458 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#include ++#include ++#include ++ ++#include "vchiq_core.h" ++#include "vchiq_arm.h" ++#include "vchiq_killable.h" ++ ++/* ---- Public Variables ------------------------------------------------- */ ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++struct bulk_waiter_node { ++ struct bulk_waiter bulk_waiter; ++ int pid; ++ struct list_head list; ++}; ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ ++ int connected; ++ ++ struct list_head bulk_waiter_list; ++ struct mutex bulk_waiter_list_mutex; ++}; ++ ++static VCHIQ_STATUS_T ++vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, VCHIQ_BULK_DIR_T dir); ++ ++/**************************************************************************** ++* ++* vchiq_initialise ++* ++***************************************************************************/ ++#define VCHIQ_INIT_RETRIES 10 ++VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance = NULL; ++ int i; ++ ++ vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); ++ ++ /* VideoCore may not be ready due to boot up timing. ++ It may never be ready if kernel and firmware are mismatched, so don't block forever. */ ++ for (i=0; i0) { ++ vchiq_log_warning(vchiq_core_log_level, ++ "%s: videocore initialized after %d retries\n", __func__, i); ++ } ++ ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%s: error allocating vchiq instance\n", __func__); ++ goto failed; ++ } ++ ++ instance->connected = 0; ++ instance->state = state; ++ mutex_init(&instance->bulk_waiter_list_mutex); ++ INIT_LIST_HEAD(&instance->bulk_waiter_list); ++ ++ *instanceOut = instance; ++ ++ status = VCHIQ_SUCCESS; ++ ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_initialise); ++ ++/**************************************************************************** ++* ++* vchiq_shutdown ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ if (mutex_lock_interruptible(&state->mutex) != 0) ++ return VCHIQ_RETRY; ++ ++ /* Remove all services */ ++ status = vchiq_shutdown_internal(state, instance); ++ ++ mutex_unlock(&state->mutex); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ if (status == VCHIQ_SUCCESS) { ++ struct list_head *pos, *next; ++ list_for_each_safe(pos, next, ++ &instance->bulk_waiter_list) { ++ struct bulk_waiter_node *waiter; ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ vchiq_log_info(vchiq_arm_log_level, ++ "bulk_waiter - cleaned up %x " ++ "for pid %d", ++ (unsigned int)waiter, waiter->pid); ++ kfree(waiter); ++ } ++ kfree(instance); ++ } ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_shutdown); ++ ++/**************************************************************************** ++* ++* vchiq_is_connected ++* ++***************************************************************************/ ++ ++int vchiq_is_connected(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->connected; ++} ++ ++/**************************************************************************** ++* ++* vchiq_connect ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ if (mutex_lock_interruptible(&state->mutex) != 0) { ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s: call to mutex_lock failed", __func__); ++ status = VCHIQ_RETRY; ++ goto failed; ++ } ++ status = vchiq_connect_internal(state, instance); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ ++ mutex_unlock(&state->mutex); ++ ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_connect); ++ ++/**************************************************************************** ++* ++* vchiq_add_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_add_service( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *phandle) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service = NULL; ++ int srvstate; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; ++ ++ srvstate = vchiq_is_connected(instance) ++ ? VCHIQ_SRVSTATE_LISTENING ++ : VCHIQ_SRVSTATE_HIDDEN; ++ ++ service = vchiq_add_service_internal( ++ state, ++ params, ++ srvstate, ++ instance, ++ NULL); ++ ++ if (service) { ++ *phandle = service->handle; ++ status = VCHIQ_SUCCESS; ++ } else ++ status = VCHIQ_ERROR; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_add_service); ++ ++/**************************************************************************** ++* ++* vchiq_open_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_open_service( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *phandle) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service = NULL; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; ++ ++ if (!vchiq_is_connected(instance)) ++ goto failed; ++ ++ service = vchiq_add_service_internal(state, ++ params, ++ VCHIQ_SRVSTATE_OPENING, ++ instance, ++ NULL); ++ ++ if (service) { ++ *phandle = service->handle; ++ status = vchiq_open_service_internal(service, current->pid); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_remove_service(service->handle); ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + } + } + -+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), -+ GFP_KERNEL); -+ if (IS_ERR(dev)) -+ return PTR_ERR(dev); ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); + -+ dev->i2s_regmap = regmap[0]; -+ dev->clk_regmap = regmap[1]; ++ return status; ++} ++EXPORT_SYMBOL(vchiq_open_service); + -+ /* Set the DMA address */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = -+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, ++ const void *data, unsigned int size, void *userdata) ++{ ++ return vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); ++} ++EXPORT_SYMBOL(vchiq_queue_bulk_transmit); + -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = -+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, void *userdata) ++{ ++ return vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); ++} ++EXPORT_SYMBOL(vchiq_queue_bulk_receive); + -+ /* Set the DREQ */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id = -+ BCM2708_DMA_DREQ_PCM_TX; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id = -+ BCM2708_DMA_DREQ_PCM_RX; ++VCHIQ_STATUS_T ++vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, ++ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_STATUS_T status; + -+ /* Set the bus width */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = -+ DMA_SLAVE_BUSWIDTH_4_BYTES; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = -+ DMA_SLAVE_BUSWIDTH_4_BYTES; -+ -+ /* Set burst */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; -+ -+ /* BCLK ratio - use default */ -+ dev->bclk_ratio = 0; -+ -+ /* Store the pdev */ -+ dev->dev = &pdev->dev; -+ dev_set_drvdata(&pdev->dev, dev); -+ -+ ret = snd_soc_register_component(&pdev->dev, -+ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1); -+ -+ if (ret) { -+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); -+ ret = -ENOMEM; -+ return ret; ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ status = vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ mode, VCHIQ_BULK_TRANSMIT); ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ status = vchiq_blocking_bulk_transfer(handle, ++ (void *)data, size, VCHIQ_BULK_TRANSMIT); ++ break; ++ default: ++ return VCHIQ_ERROR; + } + -+ ret = snd_dmaengine_pcm_register(&pdev->dev, -+ &bcm2708_dmaengine_pcm_config, -+ SND_DMAENGINE_PCM_FLAG_COMPAT); -+ if (ret) { -+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); -+ snd_soc_unregister_component(&pdev->dev); -+ return ret; ++ return status; ++} ++EXPORT_SYMBOL(vchiq_bulk_transmit); ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_STATUS_T status; ++ ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ status = vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ mode, VCHIQ_BULK_RECEIVE); ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ status = vchiq_blocking_bulk_transfer(handle, ++ (void *)data, size, VCHIQ_BULK_RECEIVE); ++ break; ++ default: ++ return VCHIQ_ERROR; + } + -+ return 0; ++ return status; +} ++EXPORT_SYMBOL(vchiq_bulk_receive); + -+static int bcm2708_i2s_remove(struct platform_device *pdev) ++static VCHIQ_STATUS_T ++vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, VCHIQ_BULK_DIR_T dir) +{ -+ snd_dmaengine_pcm_unregister(&pdev->dev); -+ snd_soc_unregister_component(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver bcm2708_i2s_driver = { -+ .probe = bcm2708_i2s_probe, -+ .remove = bcm2708_i2s_remove, -+ .driver = { -+ .name = "bcm2708-i2s", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(bcm2708_i2s_driver); -+ -+MODULE_ALIAS("platform:bcm2708-i2s"); -+MODULE_DESCRIPTION("BCM2708 I2S interface"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); -diff --git a/sound/soc/bcm/bcm2708-i2s.h b/sound/soc/bcm/bcm2708-i2s.h -new file mode 100644 -index 0000000..6fdcbc1 ---- /dev/null -+++ b/sound/soc/bcm/bcm2708-i2s.h -@@ -0,0 +1,35 @@ -+/* -+ * I2S configuration for sound cards. -+ * -+ * Copyright (c) 2014 Daniel Matuschek -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef BCM2708_I2S_H -+#define BCM2708_I2S_H -+ -+/* I2S pin assignment */ -+#define BCM2708_I2S_GPIO_AUTO 0 -+#define BCM2708_I2S_GPIO_PIN18 1 -+#define BCM2708_I2S_GPIO_PIN28 2 -+ -+/* Alt mode to enable I2S */ -+#define BCM2708_I2S_GPIO_PIN18_ALT 0 -+#define BCM2708_I2S_GPIO_PIN28_ALT 2 -+ -+extern void bcm2708_i2s_set_gpio(int gpio); -+ -+#endif - -From 2ef6c0731bcb96af4407789a69e4bcf345ec7902 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:59:51 +0100 -Subject: [PATCH 032/172] ASoC: Add support for PCM5102A codec - -Some definitions to support the PCM5102A codec -by Texas Instruments. - -Signed-off-by: Florian Meier ---- - sound/soc/codecs/Kconfig | 4 +++ - sound/soc/codecs/Makefile | 2 ++ - sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 69 insertions(+) - create mode 100644 sound/soc/codecs/pcm5102a.c - -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index ef2c70e..1fc4390 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -81,6 +81,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_PCM512x_I2C if I2C - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RT286 if I2C -+ select SND_SOC_PCM5102A if I2C - select SND_SOC_RT5631 if I2C - select SND_SOC_RT5640 if I2C - select SND_SOC_RT5645 if I2C -@@ -502,6 +503,9 @@ config SND_SOC_RT286 - tristate - depends on I2C - -+config SND_SOC_PCM5102A -+ tristate -+ - config SND_SOC_RT5631 - tristate "Realtek ALC5631/RT5631 CODEC" - depends on I2C -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index bbdfd1e..3dd07b1 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -76,6 +76,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o - snd-soc-pcm512x-spi-objs := pcm512x-spi.o - snd-soc-rl6231-objs := rl6231.o - snd-soc-rt286-objs := rt286.o -+snd-soc-pcm5102a-objs := pcm5102a.o - snd-soc-rt5631-objs := rt5631.o - snd-soc-rt5640-objs := rt5640.o - snd-soc-rt5645-objs := rt5645.o -@@ -257,6 +258,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o - obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o - obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o -+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o - obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o - obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o - obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o -diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c -new file mode 100644 -index 0000000..126f1e9 ---- /dev/null -+++ b/sound/soc/codecs/pcm5102a.c -@@ -0,0 +1,63 @@ -+/* -+ * Driver for the PCM5102A codec -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static struct snd_soc_dai_driver pcm5102a_dai = { -+ .name = "pcm5102a-hifi", -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE -+ }, -+}; -+ -+static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; -+ -+static int pcm5102a_probe(struct platform_device *pdev) -+{ -+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, -+ &pcm5102a_dai, 1); -+} -+ -+static int pcm5102a_remove(struct platform_device *pdev) -+{ -+ snd_soc_unregister_codec(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver pcm5102a_codec_driver = { -+ .probe = pcm5102a_probe, -+ .remove = pcm5102a_remove, -+ .driver = { -+ .name = "pcm5102a-codec", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(pcm5102a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); - -From 2bac890f221788793aff1fcefaeab3eaa383c536 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:04:54 +0100 -Subject: [PATCH 033/172] BCM2708: Add I2S support to board file - -Adds the required initializations for I2S -to the board file of mach-bcm2708. - -Signed-off-by: Florian Meier ---- - arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a740344..dca28ad 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -587,6 +587,28 @@ static struct platform_device bcm2835_thermal_device = { - .name = "bcm2835_thermal", - }; - -+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+static struct resource bcm2708_i2s_resources[] = { -+ { -+ .start = I2S_BASE, -+ .end = I2S_BASE + 0x20, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = PCM_CLOCK_BASE, -+ .end = PCM_CLOCK_BASE + 0x02, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_i2s_device = { -+ .name = "bcm2708-i2s", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), -+ .resource = bcm2708_i2s_resources, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -707,6 +729,10 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); - -+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+ bcm_register_device(&bcm2708_i2s_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); - -From 1395417905d319eb6d2299a90e8fee3931ebc40c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:19:08 +0100 -Subject: [PATCH 034/172] ASoC: Add support for HifiBerry DAC - -This adds a machine driver for the HifiBerry DAC. -It is a sound card that can -be stacked onto the Raspberry Pi. - -Signed-off-by: Florian Meier ---- - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 5 +++ - sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 112 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_dac.c - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 7e5b945..b36a62f 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -18,3 +18,10 @@ config SND_BCM2708_SOC_I2S - Say Y or M if you want to add support for codecs attached to - the BCM2708 I2S interface. You will also need - to select the audio interfaces to support below. -+ -+config SND_BCM2708_SOC_HIFIBERRY_DAC -+ tristate "Support for HifiBerry DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM5102A -+ help -+ Say Y or M if you want to add support for HifiBerry DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index f8bbe1f..be90a49cb 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -7,3 +7,8 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o - snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o - - obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o -+ -+# BCM2708 Machine Support -+snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+ -+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -diff --git a/sound/soc/bcm/hifiberry_dac.c b/sound/soc/bcm/hifiberry_dac.c -new file mode 100644 -index 0000000..4b70b45 ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_dac.c -@@ -0,0 +1,100 @@ -+/* -+ * ASoC Driver for HifiBerry DAC -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ unsigned int sample_bits = -+ snd_pcm_format_physical_width(params_format(params)); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = { -+ .hw_params = snd_rpi_hifiberry_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = { -+{ -+ .name = "HifiBerry DAC", -+ .stream_name = "HifiBerry DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm5102a-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm5102a-codec", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_hifiberry_dac_ops, -+ .init = snd_rpi_hifiberry_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_hifiberry_dac = { -+ .name = "snd_rpi_hifiberry_dac", -+ .dai_link = snd_rpi_hifiberry_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), -+}; -+ -+static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac); -+} -+ -+static struct platform_driver snd_rpi_hifiberry_dac_driver = { -+ .driver = { -+ .name = "snd-hifiberry-dac", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_hifiberry_dac_probe, -+ .remove = snd_rpi_hifiberry_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_hifiberry_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC"); -+MODULE_LICENSE("GPL v2"); - -From da52bd290dc3f3af3f0836a958f42b34531018cb Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:21:34 +0100 -Subject: [PATCH 035/172] BCM2708: Add HifiBerry DAC to board file - -This adds the initalization of the HifiBerry DAC -to the mach-bcm2708 board file. - -Signed-off-by: Florian Meier ---- - arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index dca28ad..50d4991 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -609,6 +609,20 @@ static struct platform_device bcm2708_i2s_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+static struct platform_device snd_hifiberry_dac_device = { -+ .name = "snd-hifiberry-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm5102a_codec_device = { -+ .name = "pcm5102a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -733,6 +747,11 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_i2s_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+ bcm_register_device(&snd_hifiberry_dac_device); -+ bcm_register_device(&snd_pcm5102a_codec_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); - -From c413288cdaabf5aa977f0afec318222b06adf10f Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Mon, 2 Dec 2013 20:28:22 +0100 -Subject: [PATCH 036/172] BCM2708: Add I2S and DMA support to default config - -This commit adds several modules that are needed for -I2S support for the Raspberry Pi to the defconfig. - -Signed-off-by: Florian Meier ---- - arch/arm/configs/bcmrpi_defconfig | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 63a6546..9a0e922 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -779,6 +779,10 @@ CONFIG_SND_USB_UA101=m - CONFIG_SND_USB_CAIAQ=m - CONFIG_SND_USB_CAIAQ_INPUT=y - CONFIG_SND_USB_6FIRE=m -+CONFIG_SND_SOC=m -+CONFIG_SND_BCM2708_SOC_I2S=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y - CONFIG_HID_A4TECH=m -@@ -977,6 +981,8 @@ CONFIG_RTC_DRV_RS5C348=m - CONFIG_RTC_DRV_DS3234=m - CONFIG_RTC_DRV_PCF2123=m - CONFIG_RTC_DRV_RX4581=m -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y - CONFIG_UIO=m - CONFIG_UIO_PDRV_GENIRQ=m - CONFIG_STAGING=y - -From 272884346f761c002999fa0aafdfa64e3c038b3c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 6 Dec 2013 20:50:28 +0100 -Subject: [PATCH 037/172] ASoC: BCM2708: Add support for RPi-DAC - -This adds a machine driver for the RPi-DAC. - -Signed-off-by: Florian Meier ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++ - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/Kconfig | 4 ++ - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/pcm1794a.c | 62 +++++++++++++++++++++++++ - 8 files changed, 194 insertions(+) - create mode 100644 sound/soc/bcm/rpi-dac.c - create mode 100644 sound/soc/codecs/pcm1794a.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 9a0e922..3f7a237 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 50d4991..100c223 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -623,6 +623,20 @@ static struct platform_device snd_pcm5102a_codec_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+static struct platform_device snd_rpi_dac_device = { -+ .name = "snd-rpi-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm1794a_codec_device = { -+ .name = "pcm1794a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -752,6 +766,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+ bcm_register_device(&snd_rpi_dac_device); -+ bcm_register_device(&snd_pcm1794a_codec_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index b36a62f..714841d 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -25,3 +25,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC - select SND_SOC_PCM5102A - help - Say Y or M if you want to add support for HifiBerry DAC. -+ -+config SND_BCM2708_SOC_RPI_DAC -+ tristate "Support for RPi-DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM1794A -+ help -+ Say Y or M if you want to add support for RPi-DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index be90a49cb..ccc9809 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -10,5 +10,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - - # BCM2708 Machine Support - snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+snd-soc-rpi-dac-objs := rpi-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c -new file mode 100644 -index 0000000..6d6e0ba ---- /dev/null -+++ b/sound/soc/bcm/rpi-dac.c -@@ -0,0 +1,97 @@ -+/* -+ * ASoC Driver for RPi-DAC. -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ return 0; -+} -+ -+static int snd_rpi_rpi_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_rpi_dac_ops = { -+ .hw_params = snd_rpi_rpi_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = { -+{ -+ .name = "RPi-DAC", -+ .stream_name = "RPi-DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm1794a-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm1794a-codec", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_rpi_dac_ops, -+ .init = snd_rpi_rpi_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_rpi_dac = { -+ .name = "snd_rpi_rpi_dac", -+ .dai_link = snd_rpi_rpi_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), -+}; -+ -+static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_rpi_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_rpi_dac); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_rpi_dac); -+} -+ -+static struct platform_driver snd_rpi_rpi_dac_driver = { -+ .driver = { -+ .name = "snd-rpi-dac", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_rpi_dac_probe, -+ .remove = snd_rpi_rpi_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_rpi_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); -+MODULE_LICENSE("GPL v2"); -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index 1fc4390..0a7aaf7 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RT286 if I2C - select SND_SOC_PCM5102A if I2C -+ select SND_SOC_PCM1794A if I2C - select SND_SOC_RT5631 if I2C - select SND_SOC_RT5640 if I2C - select SND_SOC_RT5645 if I2C -@@ -503,6 +504,9 @@ config SND_SOC_RT286 - tristate - depends on I2C - -+config SND_SOC_PCM1794A -+ tristate -+ - config SND_SOC_PCM5102A - tristate - -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index 3dd07b1..1548bf33 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -76,6 +76,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o - snd-soc-pcm512x-spi-objs := pcm512x-spi.o - snd-soc-rl6231-objs := rl6231.o - snd-soc-rt286-objs := rt286.o -+snd-soc-pcm1794a-objs := pcm1794a.o - snd-soc-pcm5102a-objs := pcm5102a.o - snd-soc-rt5631-objs := rt5631.o - snd-soc-rt5640-objs := rt5640.o -@@ -258,6 +259,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o - obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o - obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o -+obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o - obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o - obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o - obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o -diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c -new file mode 100644 -index 0000000..b4eaa44 ---- /dev/null -+++ b/sound/soc/codecs/pcm1794a.c -@@ -0,0 +1,62 @@ -+/* -+ * Driver for the PCM1794A codec -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static struct snd_soc_dai_driver pcm1794a_dai = { -+ .name = "pcm1794a-hifi", -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE -+ }, -+}; -+ -+static struct snd_soc_codec_driver soc_codec_dev_pcm1794a; -+ -+static int pcm1794a_probe(struct platform_device *pdev) -+{ -+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a, -+ &pcm1794a_dai, 1); -+} -+ -+static int pcm1794a_remove(struct platform_device *pdev) -+{ -+ snd_soc_unregister_codec(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver pcm1794a_codec_driver = { -+ .probe = pcm1794a_probe, -+ .remove = pcm1794a_remove, -+ .driver = { -+ .name = "pcm1794a-codec", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(pcm1794a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); - -From 38defc6ccd2879e3b61ee7fbeb71da1982fda8fc Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:41:23 +0100 -Subject: [PATCH 038/172] ASoC: wm8804: Implement MCLK configuration options, - add 32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs - for most sample rates. At 192kHz only 128xfs is supported. The existing - driver selects 128xfs automatically for some lower samples rates. By using an - additional mclk_div divider, it is now possible to control the behaviour. - This allows using 256xfs PLL frequency on all sample rates up to 96kHz. It - should allow lower jitter and better signal quality. The behavior has to be - controlled by the sound card driver, because some sample frequency share the - same setting. e.g. 192kHz and 96kHz use 24.576MHz master clock. The only - difference is the MCLK divider. - -This also added support for 32bit data. - -Signed-off-by: Daniel Matuschek ---- - sound/soc/codecs/wm8804.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c -index 1315f76..3d86ba8 100644 ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -278,6 +278,7 @@ static int wm8804_hw_params(struct snd_pcm_substream *substream, - blen = 0x1; - break; - case 24: -+ case 32: - blen = 0x2; - break; - default: -@@ -621,7 +622,7 @@ static const struct snd_soc_dai_ops wm8804_dai_ops = { - }; - - #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ -- SNDRV_PCM_FMTBIT_S24_LE) -+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) - - #define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ - -From 061cb19834ec2ebf649ae2028efbcaff9b4cf516 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:42:08 +0100 -Subject: [PATCH 039/172] ASoC: BCM:Add support for HiFiBerry Digi. Driver is - based on the patched WM8804 driver. - -Signed-off-by: Daniel Matuschek ---- - sound/soc/bcm/Kconfig | 7 ++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_digi.c | 153 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 162 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_digi.c - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 714841d..e563dbc 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC - help - Say Y or M if you want to add support for HifiBerry DAC. - -+config SND_BCM2708_SOC_HIFIBERRY_DIGI -+ tristate "Support for HifiBerry Digi" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_WM8804 -+ help -+ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. -+ - config SND_BCM2708_SOC_RPI_DAC - tristate "Support for RPi-DAC" - depends on SND_BCM2708_SOC_I2S -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index ccc9809..826df7d 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -10,7 +10,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - - # BCM2708 Machine Support - snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+snd-soc-hifiberry-digi-objs := hifiberry_digi.o - snd-soc-rpi-dac-objs := rpi-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o - obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -new file mode 100644 -index 0000000..e4f769d ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_digi.c -@@ -0,0 +1,153 @@ -+/* -+ * ASoC Driver for HifiBerry Digi -+ * -+ * Author: Daniel Matuschek -+ * based on the HifiBerry DAC driver by Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+#include "../codecs/wm8804.h" -+ -+static int samplerate=44100; -+ -+static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ /* enable TX output */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); -+ -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *codec_dai = rtd->codec_dai; -+ struct snd_soc_codec *codec = rtd->codec; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ int sysclk = 27000000; /* This is fixed on this board */ -+ -+ long mclk_freq=0; -+ int mclk_div=1; -+ -+ int ret; -+ -+ samplerate = params_rate(params); -+ -+ switch (samplerate) { -+ case 44100: -+ case 48000: -+ case 88200: -+ case 96000: -+ mclk_freq=samplerate*256; -+ mclk_div=WM8804_MCLKDIV_256FS; ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_SERVICE_T *service; ++ VCHIQ_STATUS_T status; ++ struct bulk_waiter_node *waiter = NULL; ++ struct list_head *pos; ++ ++ service = find_service_by_handle(handle); ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ instance = service->instance; ++ ++ unlock_service(service); ++ ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_for_each(pos, &instance->bulk_waiter_list) { ++ if (list_entry(pos, struct bulk_waiter_node, ++ list)->pid == current->pid) { ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); + break; -+ case 176400: -+ case 192000: -+ mclk_freq=samplerate*128; -+ mclk_div=WM8804_MCLKDIV_128FS; -+ break; -+ default: -+ dev_err(substream->pcm->dev, -+ "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); -+ } -+ -+ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); -+ snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); -+ -+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, -+ sysclk, SND_SOC_CLOCK_OUT); -+ if (ret < 0) { -+ dev_err(substream->pcm->dev, -+ "Failed to set WM8804 SYSCLK: %d\n", ret); -+ return ret; -+ } -+ -+ /* Enable TX output */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); -+ -+ /* Power on */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai,64); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { -+ .hw_params = snd_rpi_hifiberry_digi_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { -+{ -+ .name = "HifiBerry Digi", -+ .stream_name = "HifiBerry Digi HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "wm8804-spdif", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "wm8804.1-003b", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBM_CFM, -+ .ops = &snd_rpi_hifiberry_digi_ops, -+ .init = snd_rpi_hifiberry_digi_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_hifiberry_digi = { -+ .name = "snd_rpi_hifiberry_digi", -+ .dai_link = snd_rpi_hifiberry_digi_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), -+}; -+ -+static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_digi.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_hifiberry_digi); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_digi); -+} -+ -+static struct platform_driver snd_rpi_hifiberry_digi_driver = { -+ .driver = { -+ .name = "snd-hifiberry-digi", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_hifiberry_digi_probe, -+ .remove = snd_rpi_hifiberry_digi_remove, -+}; -+ -+module_platform_driver(snd_rpi_hifiberry_digi_driver); -+ -+MODULE_AUTHOR("Daniel Matuschek "); -+MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi"); -+MODULE_LICENSE("GPL v2"); - -From 0d3ca97a92ba10f025d4beb3bd8f58e097847840 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:26:08 +0100 -Subject: [PATCH 040/172] BCM2708: Added support for HiFiBerry Digi board Board - initalization by I2C - -Signed-off-by: Daniel Matuschek ---- - arch/arm/mach-bcm2708/bcm2708.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 100c223..a57cb85 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -623,6 +623,21 @@ static struct platform_device snd_pcm5102a_codec_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+static struct platform_device snd_hifiberry_digi_device = { -+ .name = "snd-hifiberry-digi", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("wm8804", 0x3b) -+ }, -+}; -+ -+#endif -+ - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - static struct platform_device snd_rpi_dac_device = { - .name = "snd-rpi-dac", -@@ -766,6 +781,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+ bcm_register_device(&snd_hifiberry_digi_device); -+ i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+#endif -+ - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - bcm_register_device(&snd_rpi_dac_device); - bcm_register_device(&snd_pcm1794a_codec_device); - -From b8e27019fe2d79dc6f12b04d4a2f4de1f7a7dc75 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:27:28 +0100 -Subject: [PATCH 041/172] BCM2708: Added HiFiBerry Digi configuration option It - will be compiled as a module by default. This also includes the WM8804 - driver. - -Signed-off-by: Daniel Matuschek ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 3f7a237..cbd53b1 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - -From b8243e95d60019cefde3bac2db44f25c773946d5 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:36:35 +0100 -Subject: [PATCH 042/172] ASoC: wm8804: Set idle_bias_off to false Idle bias - has been change to remove warning on driver startup - -Signed-off-by: Daniel Matuschek ---- - sound/soc/codecs/wm8804.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c -index 3d86ba8..34f7f76 100644 ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -653,7 +653,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { - .probe = wm8804_probe, - .remove = wm8804_remove, - .set_bias_level = wm8804_set_bias_level, -- .idle_bias_off = true, -+ .idle_bias_off = false, - - .controls = wm8804_snd_controls, - .num_controls = ARRAY_SIZE(wm8804_snd_controls), - -From 88ba2e5743d41046c39726fd5a483b1bb02f76ce Mon Sep 17 00:00:00 2001 -From: Gordon Garrity -Date: Sat, 8 Mar 2014 16:56:57 +0000 -Subject: [PATCH 043/172] Add IQaudIO Sound Card support for Raspberry Pi - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 22 ++++++++ - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/iqaudio-dac.c | 111 ++++++++++++++++++++++++++++++++++++++ - 5 files changed, 143 insertions(+) - create mode 100644 sound/soc/bcm/iqaudio-dac.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index cbd53b1..7ba662a 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -784,6 +784,7 @@ CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m -+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a57cb85..633be19 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -652,6 +652,22 @@ static struct platform_device snd_pcm1794a_codec_device = { - }; - #endif - -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+static struct platform_device snd_rpi_iqaudio_dac_device = { -+ .name = "snd-rpi-iqaudio-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+// Use the actual device name rather than generic driver name -+static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4c) -+ }, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -791,6 +807,12 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm1794a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+ bcm_register_device(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+#endif -+ -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index e563dbc..c621a5e 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -39,3 +39,10 @@ config SND_BCM2708_SOC_RPI_DAC - select SND_SOC_PCM1794A - help - Say Y or M if you want to add support for RPi-DAC. -+ -+config SND_BCM2708_SOC_IQAUDIO_DAC -+ tristate "Support for IQaudIO-DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM512x_I2C -+ help -+ Say Y or M if you want to add support for IQaudIO-DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index 826df7d..d597fb0 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -12,7 +12,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - snd-soc-hifiberry-dac-objs := hifiberry_dac.o - snd-soc-hifiberry-digi-objs := hifiberry_digi.o - snd-soc-rpi-dac-objs := rpi-dac.o -+snd-soc-iqaudio-dac-objs := iqaudio-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o - obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o -diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c -new file mode 100644 -index 0000000..8d0e2ae ---- /dev/null -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -0,0 +1,111 @@ -+/* -+ * ASoC Driver for IQaudIO DAC -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ -+ return 0; -+} -+ -+static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai; -+// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ unsigned int sample_bits = -+ snd_pcm_format_physical_width(params_format(params)); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = { -+ .hw_params = snd_rpi_iqaudio_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = { -+{ -+ .name = "IQaudIO DAC", -+ .stream_name = "IQaudIO DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm512x-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm512x.1-004c", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_iqaudio_dac_ops, -+ .init = snd_rpi_iqaudio_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_iqaudio_dac = { -+ .name = "IQaudIODAC", -+ .dai_link = snd_rpi_iqaudio_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), -+}; -+ -+static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_iqaudio_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); -+ if (ret) -+ dev_err(&pdev->dev, -+ "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac); -+} -+ -+static const struct of_device_id iqaudio_of_match[] = { -+ { .compatible = "iqaudio,iqaudio-dac", }, -+ {}, -+}; -+ -+static struct platform_driver snd_rpi_iqaudio_dac_driver = { -+ .driver = { -+ .name = "snd-rpi-iqaudio-dac", -+ .owner = THIS_MODULE, -+ .of_match_table = iqaudio_of_match, -+ }, -+ .probe = snd_rpi_iqaudio_dac_probe, -+ .remove = snd_rpi_iqaudio_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_iqaudio_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC"); -+MODULE_LICENSE("GPL v2"); - -From 397ad3a8dabc60f4ce6b1c261de7e2c31b7b1030 Mon Sep 17 00:00:00 2001 -From: Howard Mitchell -Date: Mon, 2 Mar 2015 17:28:02 +0000 -Subject: [PATCH 044/172] Set a limit of 0dB on Digital Volume Control - -The main volume control in the PCM512x DAC has a range up to -+24dB. This is dangerously loud and can potentially cause massive -clipping in the output stages. Therefore this sets a sensible -limit of 0dB for this control. ---- - sound/soc/bcm/iqaudio-dac.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c -index 8d0e2ae..aff7377 100644 ---- a/sound/soc/bcm/iqaudio-dac.c -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -25,7 +25,13 @@ - - static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) - { --// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ ret = snd_soc_limit_volume(codec, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); - - return 0; - } - -From 11c1054be4dcd9cb41f69225c2ec6ddf6f863d0a Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Jun 2014 13:42:01 +0100 -Subject: [PATCH 045/172] vmstat: Workaround for issue where dirty page count - goes negative - -See: -https://github.com/raspberrypi/linux/issues/617 -http://www.spinics.net/lists/linux-mm/msg72236.html ---- - include/linux/vmstat.h | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h -index 82e7db7..f87d16d 100644 ---- a/include/linux/vmstat.h -+++ b/include/linux/vmstat.h -@@ -241,7 +241,11 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item) - static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) - { - atomic_long_dec(&zone->vm_stat[item]); -+ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&zone->vm_stat[item]) < 0)) -+ atomic_long_set(&zone->vm_stat[item], 0); - atomic_long_dec(&vm_stat[item]); -+ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&vm_stat[item]) < 0)) -+ atomic_long_set(&vm_stat[item], 0); - } - - static inline void __inc_zone_page_state(struct page *page, - -From 6bc933a1c5417c9ce3d1f14fabd36ec806467de5 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 20 Jun 2014 16:03:12 +0100 -Subject: [PATCH 046/172] dwc_otg: Fix various issues with root port and - transaction errors - -Process the host port interrupts correctly (and don't trample them). -Root port hotplug now functional again. - -Fix a few thinkos with the transaction error passthrough for fiq_fsm. ---- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 7 +++---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 6 +++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2 +- - 3 files changed, 9 insertions(+), 6 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 065807f..96c76e3 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -1348,10 +1348,9 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - local_fiq_disable(); - /* Pull in the interrupts that the FIQ has masked */ - gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ gintmsk.d32 |= gintmsk_common.d32; - /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ -- reenable_gintmsk->d32 |= gintmsk.d32; -- reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -- reenable_gintmsk->d32 &= gintmsk_common.d32; -+ reenable_gintmsk->d32 = gintmsk.d32; - local_fiq_enable(); - } - -@@ -1535,7 +1534,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); -- if (retval) { -+ if (retval && fiq_enable) { - DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); - } - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 1be6e71..284f902 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -696,7 +696,11 @@ static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_c - fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); - if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { - fiq_print(FIQDBG_ERR, state, "RESET %02d", n); -- st->nr_errors = 0; -+ /* In some random cases we can get a NAK interrupt coincident with a Xacterr -+ * interrupt, after the device has disappeared. -+ */ -+ if (!hcint.b.xacterr) -+ st->nr_errors = 0; - hcintmsk.b.nak = 0; - hcintmsk.b.ack = 0; - hcintmsk.b.datatglerr = 0; -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 4195ff2..a5566bc 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -2619,7 +2619,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - case FIQ_PASSTHROUGH_ERRORSTATE: - /* Hook into the error count */ - fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); -- if (dwc_otg_hcd->fiq_state->channel[num].nr_errors) { -+ if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) { - qtd->error_count = 0; - fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); - } - -From 2a2ae2efa4a1c663642cefb333a2042590818785 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 20 Jun 2014 17:23:20 +0100 -Subject: [PATCH 047/172] fiq_fsm: Implement hack for Split Interrupt - transactions - -Hubs aren't too picky about which endpoint we send Control type split -transactions to. By treating Interrupt transfers as Control, it is -possible to use the non-periodic queue in the OTG core as well as the -non-periodic FIFOs in the hub itself. This massively reduces the -microframe exclusivity/contention that periodic split transactions -otherwise have to enforce. - -It goes without saying that this is a fairly egregious USB specification -violation, but it works. - -Original idea by Hans Petter Selasky @ FreeBSD.org. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 32 ++++++++++++++++++++++++++------ - 1 file changed, 26 insertions(+), 6 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 130096b..68d4f3b 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1055,10 +1055,11 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - for (i=0; i < hcd->core_if->core_params->host_channels; i++) { - dwc_otg_cleanup_fiq_channel(hcd, i); - } -- DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s", -+ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s%s", - (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", - (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", -- (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : ""); -+ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "", -+ (fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : ""); - } - } - -@@ -1789,6 +1790,20 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcintmsk_copy.b.chhltd = 1; - st->hcintmsk_copy.b.ahberr = 1; - -+ /* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions -+ * as Control puts the transfer into the non-periodic request queue and the -+ * non-periodic handler in the hub. Makes things lots easier. -+ */ -+ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) { -+ st->hcchar_copy.b.multicnt = 0; -+ st->hcchar_copy.b.oddfrm = 0; -+ st->hcchar_copy.b.eptype = UE_CONTROL; -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); + } + } - DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); - DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -@@ -1842,6 +1857,9 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - } - } -+ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) -+ start_immediate = 1; ++ mutex_unlock(&instance->bulk_waiter_list_mutex); + - fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); - fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); - //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); -@@ -1873,11 +1891,13 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - break; - case UE_INTERRUPT: -- if (start_immediate) { -+ if (fiq_fsm_mask & 0x8) { -+ st->fsm = FIQ_NP_SSPLIT_STARTED; -+ } else if (start_immediate) { - st->fsm = FIQ_PER_SSPLIT_STARTED; -- } else { -- st->fsm = FIQ_PER_SSPLIT_QUEUED; -- } -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ if (waiter) { ++ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; ++ if (bulk) { ++ /* This thread has an outstanding bulk transfer. */ ++ if ((bulk->data != data) || ++ (bulk->size != size)) { ++ /* This is not a retry of the previous one. ++ ** Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); + } - default: - break; - } - -From f8598c5aa941bdd6ebd3e83c1e2634f1ac6057c9 Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 6 Jul 2014 12:07:25 +0200 -Subject: [PATCH 048/172] spi-bcm2708: Prepare for Common Clock Framework - migration - -As part of migrating to use the Common Clock Framework, replace clk_enable() -with clk_prepare_enable() and clk_disable() with clk_disable_unprepare(). -This does not affect behaviour under the current clock implementation. - -Also add a missing clk_disable_unprepare() in the probe error path. - -Signed-off-by: Noralf Tronnes ---- - drivers/spi/spi-bcm2708.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -index b04a57d..349d21f 100644 ---- a/drivers/spi/spi-bcm2708.c -+++ b/drivers/spi/spi-bcm2708.c -@@ -545,7 +545,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - } - - /* initialise the hardware */ -- clk_enable(clk); -+ clk_prepare_enable(clk); - bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); - - err = spi_register_master(master); -@@ -561,6 +561,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - - out_free_irq: - free_irq(bs->irq, master); -+ clk_disable_unprepare(bs->clk); - out_workqueue: - destroy_workqueue(bs->workq); - out_iounmap: -@@ -585,7 +586,7 @@ static int bcm2708_spi_remove(struct platform_device *pdev) - - flush_work(&bs->work); - -- clk_disable(bs->clk); -+ clk_disable_unprepare(bs->clk); - clk_put(bs->clk); - free_irq(bs->irq, master); - iounmap(bs->base); - -From 0e203cd8312e0f2ecbaaa91b84302ebd08b7c7ab Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 6 Jul 2014 12:09:30 +0200 -Subject: [PATCH 049/172] BCM2708: Migrate to the Common Clock Framework - -As part of moving towards using Device Tree, the Common Clock Framework -has to be used instead of the BCM2708 clock implementation. - -Selecting COMMON_CLK removes the need to set CLKDEV_LOOKUP and HAVE_CLK explicitly. - -CONFIG_ARCH_BCM2708_CHIPIT #ifdef's are removed. They are no longer in use. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/Kconfig | 3 +- - arch/arm/mach-bcm2708/Makefile | 2 +- - arch/arm/mach-bcm2708/bcm2708.c | 79 +++++++++++++++++------------------------ - arch/arm/mach-bcm2708/clock.c | 61 ------------------------------- - arch/arm/mach-bcm2708/clock.h | 24 ------------- - 5 files changed, 34 insertions(+), 135 deletions(-) - delete mode 100644 arch/arm/mach-bcm2708/clock.c - delete mode 100644 arch/arm/mach-bcm2708/clock.h - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index f607868..96f98ce 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -371,11 +371,10 @@ config ARCH_BCM2708 - bool "Broadcom BCM2708 family" - select CPU_V6 - select ARM_AMBA -- select HAVE_CLK - select HAVE_SCHED_CLOCK - select NEED_MACH_GPIO_H - select NEED_MACH_MEMORY_H -- select CLKDEV_LOOKUP -+ select COMMON_CLK - select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select ARM_ERRATA_411920 -diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile -index a722f3f..21e3521 100644 ---- a/arch/arm/mach-bcm2708/Makefile -+++ b/arch/arm/mach-bcm2708/Makefile -@@ -2,6 +2,6 @@ - # Makefile for the linux kernel. - # - --obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o -+obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o dma.o - obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o - obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 633be19..ef12cb8 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -27,6 +27,8 @@ - #include - #include - #include -+#include -+#include - #include - #include - #include -@@ -58,7 +60,6 @@ - - #include "bcm2708.h" - #include "armctrl.h" --#include "clock.h" - - #ifdef CONFIG_BCM_VC_CMA - #include -@@ -84,7 +85,7 @@ - - /* command line parameters */ - static unsigned boardrev, serial; --static unsigned uart_clock; -+static unsigned uart_clock = UART0_CLOCK; - static unsigned disk_led_gpio = 16; - static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; -@@ -196,51 +197,39 @@ static void __init bcm2708_clocksource_init(void) - } - } - -+struct clk __init *bcm2708_clk_register(const char *name, unsigned long fixed_rate) -+{ -+ struct clk *clk; - --/* -- * These are fixed clocks. -- */ --static struct clk ref24_clk = { -- .rate = UART0_CLOCK, /* The UART is clocked at 3MHz via APB_CLK */ --}; -+ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, -+ fixed_rate); -+ if (IS_ERR(clk)) -+ pr_err("%s not registered\n", name); - --static struct clk osc_clk = { --#ifdef CONFIG_ARCH_BCM2708_CHIPIT -- .rate = 27000000, --#else -- .rate = 500000000, /* ARM clock is set from the VideoCore booter */ --#endif --}; -+ return clk; -+} - --/* warning - the USB needs a clock > 34MHz */ -+void __init bcm2708_register_clkdev(struct clk *clk, const char *name) -+{ -+ int ret; - --static struct clk sdhost_clk = { --#ifdef CONFIG_ARCH_BCM2708_CHIPIT -- .rate = 4000000, /* 4MHz */ --#else -- .rate = 250000000, /* 250MHz */ --#endif --}; -+ ret = clk_register_clkdev(clk, NULL, name); -+ if (ret) -+ pr_err("%s alias not registered\n", name); -+} - --static struct clk_lookup lookups[] = { -- { /* UART0 */ -- .dev_id = "dev:f1", -- .clk = &ref24_clk, -- }, -- { /* USB */ -- .dev_id = "bcm2708_usb", -- .clk = &osc_clk, -- }, { /* SPI */ -- .dev_id = "bcm2708_spi.0", -- .clk = &sdhost_clk, -- }, { /* BSC0 */ -- .dev_id = "bcm2708_i2c.0", -- .clk = &sdhost_clk, -- }, { /* BSC1 */ -- .dev_id = "bcm2708_i2c.1", -- .clk = &sdhost_clk, -- } --}; -+void __init bcm2708_init_clocks(void) -+{ -+ struct clk *clk; -+ -+ clk = bcm2708_clk_register("uart0_clk", uart_clock); -+ bcm2708_register_clkdev(clk, "dev:f1"); -+ -+ clk = bcm2708_clk_register("sdhost_clk", 250000000); -+ bcm2708_register_clkdev(clk, "bcm2708_spi.0"); -+ bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); -+ bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); -+} - - #define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } - #define UART0_DMA { 15, 14 } -@@ -755,11 +744,7 @@ void __init bcm2708_init(void) - printk("bcm2708.uart_clock = %d\n", uart_clock); - pm_power_off = bcm2708_power_off; - -- if (uart_clock) -- lookups[0].clk->rate = uart_clock; -- -- for (i = 0; i < ARRAY_SIZE(lookups); i++) -- clkdev_add(&lookups[i]); -+ bcm2708_init_clocks(); - - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); -diff --git a/arch/arm/mach-bcm2708/clock.c b/arch/arm/mach-bcm2708/clock.c -deleted file mode 100644 -index 4fc556e..0000000 ---- a/arch/arm/mach-bcm2708/clock.c -+++ /dev/null -@@ -1,61 +0,0 @@ --/* -- * linux/arch/arm/mach-bcm2708/clock.c -- * -- * Copyright (C) 2010 Broadcom -- * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version. -- * -- * This program is distributed in the hope that it will be useful, -- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- */ --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "clock.h" -- --int clk_enable(struct clk *clk) --{ -- return 0; --} --EXPORT_SYMBOL(clk_enable); -- --void clk_disable(struct clk *clk) --{ --} --EXPORT_SYMBOL(clk_disable); -- --unsigned long clk_get_rate(struct clk *clk) --{ -- return clk->rate; --} --EXPORT_SYMBOL(clk_get_rate); -- --long clk_round_rate(struct clk *clk, unsigned long rate) --{ -- return clk->rate; --} --EXPORT_SYMBOL(clk_round_rate); -- --int clk_set_rate(struct clk *clk, unsigned long rate) --{ -- return -EIO; --} --EXPORT_SYMBOL(clk_set_rate); -diff --git a/arch/arm/mach-bcm2708/clock.h b/arch/arm/mach-bcm2708/clock.h -deleted file mode 100644 -index 5f9d725..0000000 ---- a/arch/arm/mach-bcm2708/clock.h -+++ /dev/null -@@ -1,24 +0,0 @@ --/* -- * linux/arch/arm/mach-bcm2708/clock.h -- * -- * Copyright (C) 2010 Broadcom -- * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version. -- * -- * This program is distributed in the hope that it will be useful, -- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- */ --struct module; -- --struct clk { -- unsigned long rate; --}; - -From ec2d83be8df77ac09c47dac5f3ac7218d92e1619 Mon Sep 17 00:00:00 2001 -From: notro -Date: Wed, 9 Jul 2014 14:46:08 +0200 -Subject: [PATCH 050/172] BCM2708: Add core Device Tree support - -Add the bare minimum needed to boot BCM2708 from a Device Tree. - -Signed-off-by: Noralf Tronnes - -BCM2708: DT: change 'axi' nodename to 'soc' - -Change DT node named 'axi' to 'soc' so it matches ARCH_BCM2835. -The VC4 bootloader fills in certain properties in the 'axi' subtree, -but since this is part of an upstreaming effort, the name is changed. - -Signed-off-by: Noralf Tronnes notro@tronnes.org ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 ++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 27 +++++++++++++++++++++++++++ - arch/arm/mach-bcm2708/Kconfig | 8 ++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 24 ++++++++++++++++++++++++ - 5 files changed, 68 insertions(+) - create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b.dts - create mode 100644 arch/arm/boot/dts/bcm2708.dtsi - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 91bd5bd..d177201 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -53,6 +53,7 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb -+dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -new file mode 100644 -index 0000000..e319c8e ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -0,0 +1,8 @@ -+/dts-v1/; -+ -+/include/ "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi"; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -new file mode 100644 -index 0000000..50da059 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -0,0 +1,27 @@ -+/include/ "skeleton.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "BCM2708"; -+ -+ chosen { -+ /* -+ bootargs must be 1024 characters long because the -+ VC bootloader can't expand it -+ */ -+ bootargs = "console=ttyAMA0 "; -+ }; -+ -+ soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges = <0x7e000000 0x20000000 0x02000000>; -+ }; -+ -+ clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+}; -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index e151ed4..182e7ba 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -9,6 +9,14 @@ config MACH_BCM2708 - help - Include support for the Broadcom(R) BCM2708 platform. - -+config BCM2708_DT -+ bool "BCM2708 Device Tree support" -+ depends on MACH_BCM2708 -+ default n -+ select USE_OF -+ help -+ Enable Device Tree support for BCM2708 -+ - config BCM2708_GPIO - bool "BCM2708 gpio support" - depends on MACH_BCM2708 -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index ef12cb8..747e27a 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -734,6 +735,22 @@ static void bcm2708_power_off(void) - } - } - -+#ifdef CONFIG_OF -+static void __init bcm2708_dt_init(void) -+{ -+ int ret; -+ -+ of_clk_init(NULL); -+ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -+ if (ret) { -+ pr_err("of_platform_populate failed: %d\n", ret); -+ BUG(); -+ } -+} -+#else -+static void __init bcm2708_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ - void __init bcm2708_init(void) - { - int i; -@@ -745,6 +762,7 @@ void __init bcm2708_init(void) - pm_power_off = bcm2708_power_off; - - bcm2708_init_clocks(); -+ bcm2708_dt_init(); - - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); -@@ -965,6 +983,11 @@ static void __init board_reserve(void) - #endif - } - -+static const char * const bcm2708_compat[] = { -+ "brcm,bcm2708", -+ NULL -+}; -+ - MACHINE_START(BCM2708, "BCM2708") - /* Maintainer: Broadcom Europe Ltd. */ - .map_io = bcm2708_map_io, -@@ -974,6 +997,7 @@ MACHINE_START(BCM2708, "BCM2708") - .init_early = bcm2708_init_early, - .reserve = board_reserve, - .restart = bcm2708_restart, -+ .dt_compat = bcm2708_compat, - MACHINE_END - - module_param(boardrev, uint, 0644); - -From be8c3d8a94f6744f2e6b3f3d41c3604d4c6cb217 Mon Sep 17 00:00:00 2001 -From: notro -Date: Wed, 9 Jul 2014 14:47:48 +0200 -Subject: [PATCH 051/172] BCM2708: armctrl: Add IRQ Device Tree support - -Add Device Tree IRQ support for BCM2708. -Usage is the same as for irq-bcm2835. -See binding document: brcm,bcm2835-armctrl-ic.txt - -A bank 3 is added to handle GPIO interrupts. This is done because -armctrl also handles GPIO interrupts. - -Signed-off-by: Noralf Tronnes - -BCM2708: armctrl: remove irq bank 3 - -irq bank 3 was needed by the pinctrl-bcm2708 and bcm2708_gpio -combination. It is no longer required. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708.dtsi | 9 ++++ - arch/arm/mach-bcm2708/armctrl.c | 96 +++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 105 insertions(+) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 50da059..a06f5b8 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -4,6 +4,8 @@ - compatible = "brcm,bcm2708"; - model = "BCM2708"; - -+ interrupt-parent = <&intc>; -+ - chosen { - /* - bootargs must be 1024 characters long because the -@@ -17,6 +19,13 @@ - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x7e000000 0x20000000 0x02000000>; -+ -+ intc: interrupt-controller { -+ compatible = "brcm,bcm2708-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c -index 96fa9b9..74bacb3 100644 ---- a/arch/arm/mach-bcm2708/armctrl.c -+++ b/arch/arm/mach-bcm2708/armctrl.c -@@ -23,6 +23,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -79,6 +81,99 @@ static void armctrl_unmask_irq(struct irq_data *d) - } - } - -+#ifdef CONFIG_OF -+ -+#define NR_IRQS_BANK0 21 -+#define NR_BANKS 3 -+#define IRQS_PER_BANK 32 -+ -+/* from drivers/irqchip/irq-bcm2835.c */ -+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, -+ const u32 *intspec, unsigned int intsize, -+ unsigned long *out_hwirq, unsigned int *out_type) -+{ -+ if (WARN_ON(intsize != 2)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] >= NR_BANKS)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) -+ return -EINVAL; -+ -+ if (intspec[0] == 0) -+ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; -+ else if (intspec[0] == 1) -+ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; -+ else -+ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; -+ -+ /* reverse remap_irqs[] */ -+ switch (*out_hwirq) { -+ case INTERRUPT_VC_JPEG: -+ *out_hwirq = INTERRUPT_JPEG; -+ break; -+ case INTERRUPT_VC_USB: -+ *out_hwirq = INTERRUPT_USB; -+ break; -+ case INTERRUPT_VC_3D: -+ *out_hwirq = INTERRUPT_3D; -+ break; -+ case INTERRUPT_VC_DMA2: -+ *out_hwirq = INTERRUPT_DMA2; -+ break; -+ case INTERRUPT_VC_DMA3: -+ *out_hwirq = INTERRUPT_DMA3; -+ break; -+ case INTERRUPT_VC_I2C: -+ *out_hwirq = INTERRUPT_I2C; -+ break; -+ case INTERRUPT_VC_SPI: -+ *out_hwirq = INTERRUPT_SPI; -+ break; -+ case INTERRUPT_VC_I2SPCM: -+ *out_hwirq = INTERRUPT_I2SPCM; -+ break; -+ case INTERRUPT_VC_SDIO: -+ *out_hwirq = INTERRUPT_SDIO; -+ break; -+ case INTERRUPT_VC_UART: -+ *out_hwirq = INTERRUPT_UART; -+ break; -+ case INTERRUPT_VC_ARASANSDIO: -+ *out_hwirq = INTERRUPT_ARASANSDIO; -+ break; ++ } + } + -+ *out_type = IRQ_TYPE_NONE; -+ return 0; ++ if (!waiter) { ++ waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); ++ if (!waiter) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%s - out of memory", __func__); ++ return VCHIQ_ERROR; ++ } ++ } ++ ++ status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, ++ data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, ++ dir); ++ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || ++ !waiter->bulk_waiter.bulk) { ++ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; ++ if (bulk) { ++ /* Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); ++ } ++ kfree(waiter); ++ } else { ++ waiter->pid = current->pid; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_add(&waiter->list, &instance->bulk_waiter_list); ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ vchiq_log_info(vchiq_arm_log_level, ++ "saved bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); ++ } ++ ++ return status; +} -+ -+static struct irq_domain_ops armctrl_ops = { -+ .xlate = armctrl_xlate -+}; -+ -+void __init armctrl_dt_init(void) -+{ -+ struct device_node *np; -+ struct irq_domain *domain; -+ -+ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); -+ if (!np) -+ return; -+ -+ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, -+ IRQ_ARMCTRL_START, 0, -+ &armctrl_ops, NULL); -+ WARN_ON(!domain); -+} -+#else -+void __init armctrl_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ - #if defined(CONFIG_PM) - - /* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ -@@ -215,5 +310,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, - - armctrl_pm_register(base, irq_start, resume_sources); - init_FIQ(FIQ_START); -+ armctrl_dt_init(); - return 0; - } - -From 235c1c651b6b85fc9f83940c08bc2055902767c9 Mon Sep 17 00:00:00 2001 -From: notro -Date: Thu, 10 Jul 2014 13:59:47 +0200 -Subject: [PATCH 052/172] BCM2708: use pinctrl-bcm2835 - -Use pinctrl-bcm2835 instead of the pinctrl-bcm2708 and bcm2708_gpio -combination. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708.dtsi | 12 ++++++++++++ - arch/arm/mach-bcm2708/Kconfig | 3 +++ - arch/arm/mach-bcm2708/bcm2708.c | 2 +- - drivers/pinctrl/pinctrl-bcm2835.c | 2 +- - 4 files changed, 17 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index a06f5b8..b2920c8 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -26,6 +26,18 @@ - interrupt-controller; - #interrupt-cells = <2>; - }; -+ -+ gpio: gpio { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ interrupts = <2 17>, <2 18>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index 182e7ba..4cfae55 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -14,6 +14,9 @@ config BCM2708_DT - depends on MACH_BCM2708 - default n - select USE_OF -+ select ARCH_REQUIRE_GPIOLIB -+ select PINCTRL -+ select PINCTRL_BCM2835 - help - Enable Device Tree support for BCM2708 - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 747e27a..0e5bd43 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -767,7 +767,7 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); - #ifdef CONFIG_BCM2708_GPIO -- bcm_register_device(&bcm2708_gpio_device); -+ bcm_register_device_dt(&bcm2708_gpio_device); - #endif - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; -diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index 9aa8a3f..9d1149e 100644 ---- a/drivers/pinctrl/pinctrl-bcm2835.c -+++ b/drivers/pinctrl/pinctrl-bcm2835.c -@@ -382,7 +382,7 @@ static struct gpio_chip bcm2835_gpio_chip = { - .get = bcm2835_gpio_get, - .set = bcm2835_gpio_set, - .to_irq = bcm2835_gpio_to_irq, -- .base = -1, -+ .base = 0, - .ngpio = BCM2835_NUM_GPIOS, - .can_sleep = false, - }; - -From 2fddb74c345dcb1cd2b03b60f64725bb5fbb26f0 Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 27 Jul 2014 20:12:58 +0200 -Subject: [PATCH 053/172] spi: bcm2708: add device tree support - -Add DT support to driver and add to .dtsi file. -Setup pins and spidev in .dts file. -SPI is disabled by default. - -Signed-off-by: Noralf Tronnes - -BCM2708: don't register SPI controller when using DT - -The device for the SPI controller is in the Device Tree. -Only register the device when not using DT. - -Signed-off-by: Noralf Tronnes - -spi: bcm2835: make driver available on ARCH_BCM2708 - -Make this driver available on ARCH_BCM2708 - -Signed-off-by: Noralf Tronnes - -bcm2708: Remove the prohibition on mixing SPIDEV and DT ---- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 32 ++++++++++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 18 ++++++++++++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++++++++++--- - drivers/spi/Kconfig | 2 +- - drivers/spi/spi-bcm2708.c | 8 ++++++++ - 5 files changed, 75 insertions(+), 4 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index e319c8e..30107fb 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -5,4 +5,36 @@ - / { - compatible = "brcm,bcm2708"; - model = "Raspberry Pi"; -+ -+ aliases { -+ spi0 = &spi0; -+ }; -+}; -+ -+&gpio { -+ spi0_pins: spi0_pins { -+ brcm,pins = <7 8 9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins>; -+ -+ spidev@0{ -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+ -+ spidev@1{ -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; - }; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index b2920c8..e90bf4c 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -38,11 +38,29 @@ - interrupt-controller; - #interrupt-cells = <2>; - }; -+ -+ spi0: spi@7e204000 { -+ compatible = "brcm,bcm2708-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clk_spi>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; - }; - - clocks { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; -+ -+ clk_spi: clock@2 { -+ compatible = "fixed-clock"; -+ reg = <2>; -+ #clock-cells = <0>; -+ clock-output-names = "spi"; -+ clock-frequency = <250000000>; -+ }; - }; - }; -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 0e5bd43..a2069f8 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -486,6 +486,7 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+#ifndef CONFIG_OF - static struct resource bcm2708_spi_resources[] = { - { - .start = SPI0_BASE, -@@ -509,6 +510,7 @@ static struct platform_device bcm2708_spi_device = { - .dma_mask = &bcm2708_spi_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, - }; -+#endif - - #ifdef CONFIG_BCM2708_SPIDEV - static struct spi_board_info bcm2708_spi_devices[] = { -@@ -670,6 +672,16 @@ int __init bcm_register_device(struct platform_device *pdev) - return ret; - } - -+/* -+ * Use this macro for platform devices that are present in the Device Tree. -+ * This way the device is only added on non-DT builds. +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h +new file mode 100644 +index 0000000..335446e +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h +@@ -0,0 +1,69 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -+#ifdef CONFIG_OF -+#define bcm_register_device_dt(pdev) -+#else -+#define bcm_register_device_dt(pdev) bcm_register_device(pdev) ++ ++#ifndef VCHIQ_KILLABLE_H ++#define VCHIQ_KILLABLE_H ++ ++#include ++#include ++ ++#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT)) ++ ++static inline int __must_check down_interruptible_killable(struct semaphore *sem) ++{ ++ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ ++ int ret; ++ sigset_t blocked, oldset; ++ siginitsetinv(&blocked, SHUTDOWN_SIGS); ++ sigprocmask(SIG_SETMASK, &blocked, &oldset); ++ ret = down_interruptible(sem); ++ sigprocmask(SIG_SETMASK, &oldset, NULL); ++ return ret; ++} ++#define down_interruptible down_interruptible_killable ++ ++ ++static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock) ++{ ++ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ ++ int ret; ++ sigset_t blocked, oldset; ++ siginitsetinv(&blocked, SHUTDOWN_SIGS); ++ sigprocmask(SIG_SETMASK, &blocked, &oldset); ++ ret = mutex_lock_interruptible(lock); ++ sigprocmask(SIG_SETMASK, &oldset, NULL); ++ return ret; ++} ++#define mutex_lock_interruptible mutex_lock_interruptible_killable ++ +#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +new file mode 100644 +index 0000000..d02e776 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +@@ -0,0 +1,71 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + - int calc_rsts(int partition) - { - return PM_PASSWORD | -@@ -784,7 +796,7 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -- bcm_register_device(&bcm2708_spi_device); -+ bcm_register_device_dt(&bcm2708_spi_device); - bcm_register_device(&bcm2708_bsc0_device); - bcm_register_device(&bcm2708_bsc1_device); - -@@ -824,8 +836,9 @@ void __init bcm2708_init(void) - system_serial_low = serial; - - #ifdef CONFIG_BCM2708_SPIDEV -- spi_register_board_info(bcm2708_spi_devices, -- ARRAY_SIZE(bcm2708_spi_devices)); -+ if (!use_dt) -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); - #endif - } - -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index ae4ea48..9f202dd 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -77,7 +77,7 @@ config SPI_ATMEL - - config SPI_BCM2835 - tristate "BCM2835 SPI controller" -- depends on ARCH_BCM2835 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || COMPILE_TEST - help - This selects a driver for the Broadcom BCM2835 SPI master. - -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -index 349d21f..041b5e2 100644 ---- a/drivers/spi/spi-bcm2708.c -+++ b/drivers/spi/spi-bcm2708.c -@@ -512,6 +512,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - master->setup = bcm2708_spi_setup; - master->transfer = bcm2708_spi_transfer; - master->cleanup = bcm2708_spi_cleanup; -+ master->dev.of_node = pdev->dev.of_node; - platform_set_drvdata(pdev, master); - - bs = spi_master_get_devdata(master); -@@ -596,10 +597,17 @@ static int bcm2708_spi_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_spi_match[] = { -+ { .compatible = "brcm,bcm2708-spi", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_spi_match); ++#ifndef VCHIQ_MEMDRV_H ++#define VCHIQ_MEMDRV_H + - static struct platform_driver bcm2708_spi_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_spi_match, - }, - .probe = bcm2708_spi_probe, - .remove = bcm2708_spi_remove, - -From a9dfef3e2161f0ec30100b32639de3d6d0682836 Mon Sep 17 00:00:00 2001 -From: notro -Date: Tue, 29 Jul 2014 11:04:49 +0200 -Subject: [PATCH 054/172] i2c: bcm2708: add device tree support - -Add DT support to driver and add to .dtsi file. -Setup pins in .dts file. -i2c is disabled by default. - -Signed-off-by: Noralf Tronnes - -bcm2708: don't register i2c controllers when using DT - -The devices for the i2c controllers are in the Device Tree. -Only register devices when not using DT. - -Signed-off-by: Noralf Tronnes - -i2c: bcm2835: make driver available on ARCH_BCM2708 - -Make this driver available on ARCH_BCM2708 - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 24 ++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 27 +++++++++++++++++++++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 6 ++++-- - drivers/i2c/busses/Kconfig | 2 +- - drivers/i2c/busses/i2c-bcm2708.c | 24 ++++++++++++++++++++++++ - 5 files changed, 80 insertions(+), 3 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 30107fb..5893122 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -8,6 +8,8 @@ - - aliases { - spi0 = &spi0; -+ i2c0 = &i2c0; -+ i2c1 = &i2c1; - }; - }; - -@@ -16,6 +18,16 @@ - brcm,pins = <7 8 9 10 11>; - brcm,function = <4>; /* alt0 */ - }; ++/* ---- Include Files ----------------------------------------------------- */ + -+ i2c0_pins: i2c0 { -+ brcm,pins = <0 1>; -+ brcm,function = <4>; -+ }; ++#include ++#include "vchiq_if.h" + -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; -+ }; - }; - - &spi0 { -@@ -38,3 +50,15 @@ - spi-max-frequency = <500000>; - }; - }; ++/* ---- Constants and Types ---------------------------------------------- */ + -+&i2c0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c0_pins>; -+ clock-frequency = <100000>; -+}; ++typedef struct { ++ void *armSharedMemVirt; ++ dma_addr_t armSharedMemPhys; ++ size_t armSharedMemSize; + -+&i2c1 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c1_pins>; -+ clock-frequency = <100000>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index e90bf4c..2ca6d63 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -48,6 +48,26 @@ - #size-cells = <0>; - status = "disabled"; - }; ++ void *vcSharedMemVirt; ++ dma_addr_t vcSharedMemPhys; ++ size_t vcSharedMemSize; ++} VCHIQ_SHARED_MEM_INFO_T; + -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; ++/* ---- Variable Externs ------------------------------------------------- */ + -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; - }; - - clocks { -@@ -55,6 +75,13 @@ - #address-cells = <1>; - #size-cells = <0>; - -+ clk_i2c: i2c { -+ compatible = "fixed-clock"; -+ reg = <1>; -+ #clock-cells = <0>; -+ clock-frequency = <250000000>; -+ }; ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_get_shared_mem_info(VCHIQ_SHARED_MEM_INFO_T *info); ++ ++VCHIQ_STATUS_T vchiq_memdrv_initialise(void); ++ ++VCHIQ_STATUS_T vchiq_userdrv_create_instance( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ ++VCHIQ_STATUS_T vchiq_userdrv_suspend( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ ++VCHIQ_STATUS_T vchiq_userdrv_resume( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); + - clk_spi: clock@2 { - compatible = "fixed-clock"; - reg = <2>; -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a2069f8..b45f327 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -532,6 +532,7 @@ static struct spi_board_info bcm2708_spi_devices[] = { - }; - #endif - -+#ifndef CONFIG_OF - static struct resource bcm2708_bsc0_resources[] = { - { - .start = BSC0_BASE, -@@ -570,6 +571,7 @@ static struct platform_device bcm2708_bsc1_device = { - .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), - .resource = bcm2708_bsc1_resources, - }; +#endif - - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", -@@ -797,8 +799,8 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_alsa_devices[i]); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device(&bcm2708_bsc0_device); -- bcm_register_device(&bcm2708_bsc1_device); -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); - - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 68362ab..7b7ba6f 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -362,7 +362,7 @@ config I2C_AXXIA - - config I2C_BCM2835 - tristate "Broadcom BCM2835 I2C controller" -- depends on ARCH_BCM2835 -+ depends on ARCH_BCM2835 || ARCH_BCM2708 - help - If you say yes to this option, support will be included for the - BCM2835 I2C controller. -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -index 7d385a3..526129b 100644 ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -303,6 +304,21 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) - unsigned long bus_hz; - u32 cdiv; - -+ if (pdev->dev.of_node) { -+ u32 bus_clk_rate; -+ pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); -+ if (pdev->id < 0) { -+ dev_err(&pdev->dev, "alias is missing\n"); -+ return -EINVAL; -+ } -+ if (!of_property_read_u32(pdev->dev.of_node, -+ "clock-frequency", &bus_clk_rate)) -+ baudrate = bus_clk_rate; -+ else -+ dev_warn(&pdev->dev, -+ "Could not read clock-frequency property\n"); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +new file mode 100644 +index 0000000..54a3ece +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +@@ -0,0 +1,58 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_PAGELIST_H ++#define VCHIQ_PAGELIST_H ++ ++#ifndef PAGE_SIZE ++#define PAGE_SIZE 4096 ++#endif ++#define CACHE_LINE_SIZE 32 ++#define PAGELIST_WRITE 0 ++#define PAGELIST_READ 1 ++#define PAGELIST_READ_WITH_FRAGMENTS 2 ++ ++typedef struct pagelist_struct { ++ unsigned long length; ++ unsigned short type; ++ unsigned short offset; ++ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following ++ pages at consecutive addresses. */ ++} PAGELIST_T; ++ ++typedef struct fragments_struct { ++ char headbuf[CACHE_LINE_SIZE]; ++ char tailbuf[CACHE_LINE_SIZE]; ++} FRAGMENTS_T; ++ ++#endif /* VCHIQ_PAGELIST_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +new file mode 100644 +index 0000000..8072ff6 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -0,0 +1,860 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include ++#include ++ ++#include "interface/vchi/vchi.h" ++#include "vchiq.h" ++#include "vchiq_core.h" ++ ++#include "vchiq_util.h" ++ ++#include ++ ++#define vchiq_status_to_vchi(status) ((int32_t)status) ++ ++typedef struct { ++ VCHIQ_SERVICE_HANDLE_T handle; ++ ++ VCHIU_QUEUE_T queue; ++ ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++} SHIM_SERVICE_T; ++ ++/* ---------------------------------------------------------------------- ++ * return pointer to the mphi message driver function table ++ * -------------------------------------------------------------------- */ ++const VCHI_MESSAGE_DRIVER_T * ++vchi_mphi_message_driver_func_table(void) ++{ ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- ++ * return a pointer to the 'single' connection driver fops ++ * -------------------------------------------------------------------- */ ++const VCHI_CONNECTION_API_T * ++single_get_func_table(void) ++{ ++ return NULL; ++} ++ ++VCHI_CONNECTION_T *vchi_create_connection( ++ const VCHI_CONNECTION_API_T *function_table, ++ const VCHI_MESSAGE_DRIVER_T *low_level) ++{ ++ (void)function_table; ++ (void)low_level; ++ return NULL; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_peek ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ ++ ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to return a pointer to the current message (to allow in ++ * place processing). The message can be removed using ++ * vchi_msg_remove when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_peek(VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_peek(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_peek); ++ ++/*********************************************************** ++ * Name: vchi_msg_remove ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * ++ * Description: Routine to remove a message (after it has been read with ++ * vchi_msg_peek) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_remove(VCHI_SERVICE_HANDLE_T handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_remove); ++ ++/*********************************************************** ++ * Name: vchi_msg_queue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle, ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_ELEMENT_T element = {data, data_size}; ++ VCHIQ_STATUS_T status; ++ ++ (void)msg_handle; ++ ++ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ status = vchiq_queue_message(service->handle, &element, 1); ++ ++ /* vchiq_queue_message() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_queue_message(service->handle, &element, 1); + } + - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_err(&pdev->dev, "could not get IO memory\n"); -@@ -336,6 +352,7 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) - adap->dev.parent = &pdev->dev; - adap->nr = pdev->id; - strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); -+ adap->dev.of_node = pdev->dev.of_node; - - switch (pdev->id) { - case 0: -@@ -416,10 +433,17 @@ static int bcm2708_i2c_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_i2c_of_match[] = { -+ { .compatible = "brcm,bcm2708-i2c" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_msg_queue); + - static struct platform_driver bcm2708_i2c_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_i2c_of_match, - }, - .probe = bcm2708_i2c_probe, - .remove = bcm2708_i2c_remove, - -From 822709b4718fcc2922306017b9307f1523eb49ed Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 14 Jul 2014 22:02:09 +0100 -Subject: [PATCH 055/172] hid: Reduce default mouse polling interval to 60Hz - -Reduces overhead when using X ---- - drivers/hid/usbhid/hid-core.c | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c -index bfbe1be..a738b25 100644 ---- a/drivers/hid/usbhid/hid-core.c -+++ b/drivers/hid/usbhid/hid-core.c -@@ -49,7 +49,7 @@ - * Module parameters. - */ - --static unsigned int hid_mousepoll_interval; -+static unsigned int hid_mousepoll_interval = ~0; - module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); - MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); - -@@ -1090,8 +1090,12 @@ static int usbhid_start(struct hid_device *hid) - } - - /* Change the polling interval of mice. */ -- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) -- interval = hid_mousepoll_interval; -+ if (hid->collection->usage == HID_GD_MOUSE) { -+ if (hid_mousepoll_interval == ~0 && interval < 16) -+ interval = 16; -+ else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0) -+ interval = hid_mousepoll_interval; ++/*********************************************************** ++ * Name: vchi_bulk_queue_receive ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * void *data_dst, ++ * const uint32_t data_size, ++ * VCHI_FLAGS_T flags ++ * void *bulk_handle ++ * ++ * Description: Routine to setup a rcv buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_receive(VCHI_SERVICE_HANDLE_T handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE ++ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ WARN_ON(!service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ WARN(1, "unsupported message\n"); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_receive(service->handle, data_dst, data_size, ++ bulk_handle, mode); ++ ++ /* vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_bulk_receive(service->handle, data_dst, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_bulk_queue_receive); ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_transmit ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * const void *data_src, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *bulk_handle ++ * ++ * Description: Routine to transmit some data ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_transmit(VCHI_SERVICE_HANDLE_T handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE ++ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ WARN_ON(!service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ WARN(1, "unsupported message\n"); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_transmit(service->handle, data_src, data_size, ++ bulk_handle, mode); ++ ++ /* vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_bulk_transmit(service->handle, data_src, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_bulk_queue_transmit); ++ ++/*********************************************************** ++ * Name: vchi_msg_dequeue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void *data, ++ * uint32_t max_data_size_to_read, ++ * uint32_t *actual_msg_size ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to dequeue a message into the supplied buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_dequeue(VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ memcpy(data, header->data, header->size < max_data_size_to_read ? ++ header->size : max_data_size_to_read); ++ ++ *actual_msg_size = header->size; ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_dequeue); ++ ++/*********************************************************** ++ * Name: vchi_msg_queuev ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * VCHI_MSG_VECTOR_T *vector, ++ * uint32_t count, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++vchiq_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); ++vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == ++ offsetof(VCHIQ_ELEMENT_T, data)); ++vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == ++ offsetof(VCHIQ_ELEMENT_T, size)); ++ ++int32_t vchi_msg_queuev(VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ ++ (void)msg_handle; ++ ++ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, ++ (const VCHIQ_ELEMENT_T *)vector, count)); ++} ++EXPORT_SYMBOL(vchi_msg_queuev); ++ ++/*********************************************************** ++ * Name: vchi_held_msg_release ++ * ++ * Arguments: VCHI_HELD_MSG_T *message ++ * ++ * Description: Routine to release a held message (after it has been read with ++ * vchi_msg_hold) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) ++{ ++ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, ++ (VCHIQ_HEADER_T *)message->message); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_held_msg_release); ++ ++/*********************************************************** ++ * Name: vchi_msg_hold ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags, ++ * VCHI_HELD_MSG_T *message_handle ++ * ++ * Description: Routine to return a pointer to the current message (to allow ++ * in place processing). The message is dequeued - don't forget ++ * to release the message using vchi_held_msg_release when you're ++ * finished. ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ message_handle->service = ++ (struct opaque_vchi_service_t *)service->handle; ++ message_handle->message = header; ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_hold); ++ ++/*********************************************************** ++ * Name: vchi_initialise ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * ++ * Description: Initialises the hardware but does not transmit anything ++ * When run as a Host App this will be called twice hence the need ++ * to malloc the state information ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++ ++int32_t vchi_initialise(VCHI_INSTANCE_T *instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_STATUS_T status; ++ ++ status = vchiq_initialise(&instance); ++ ++ *instance_handle = (VCHI_INSTANCE_T)instance; ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_initialise); ++ ++/*********************************************************** ++ * Name: vchi_connect ++ * ++ * Arguments: VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * VCHI_INSTANCE_T instance_handle) ++ * ++ * Description: Starts the command service on each connection, ++ * causing INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_connect(VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ ++ (void)connections; ++ (void)num_connections; ++ ++ return vchiq_connect(instance); ++} ++EXPORT_SYMBOL(vchi_connect); ++ ++ ++/*********************************************************** ++ * Name: vchi_disconnect ++ * ++ * Arguments: VCHI_INSTANCE_T instance_handle ++ * ++ * Description: Stops the command service on each connection, ++ * causing DE-INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ return vchiq_status_to_vchi(vchiq_shutdown(instance)); ++} ++EXPORT_SYMBOL(vchi_disconnect); ++ ++ ++/*********************************************************** ++ * Name: vchi_service_open ++ * Name: vchi_service_create ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * SERVICE_CREATION_T *setup, ++ * VCHI_SERVICE_HANDLE_T *handle ++ * ++ * Description: Routine to open a service ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) ++{ ++ SHIM_SERVICE_T *service = ++ (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); ++ ++ if (!service->callback) ++ goto release; ++ ++ switch (reason) { ++ case VCHIQ_MESSAGE_AVAILABLE: ++ vchiu_queue_push(&service->queue, header); ++ ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_MSG_AVAILABLE, NULL); ++ ++ goto done; ++ break; ++ ++ case VCHIQ_BULK_TRANSMIT_DONE: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_SENT, bulk_user); ++ break; ++ ++ case VCHIQ_BULK_RECEIVE_DONE: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVED, bulk_user); ++ break; ++ ++ case VCHIQ_SERVICE_CLOSED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_SERVICE_CLOSED, NULL); ++ break; ++ ++ case VCHIQ_SERVICE_OPENED: ++ /* No equivalent VCHI reason */ ++ break; ++ ++ case VCHIQ_BULK_TRANSMIT_ABORTED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++ bulk_user); ++ break; ++ ++ case VCHIQ_BULK_RECEIVE_ABORTED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ bulk_user); ++ break; ++ ++ default: ++ WARN(1, "not supported\n"); ++ break; ++ } ++ ++release: ++ vchiq_release_message(service->handle, header); ++done: ++ return VCHIQ_SUCCESS; ++} ++ ++static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, ++ SERVICE_CREATION_T *setup) ++{ ++ SHIM_SERVICE_T *service = kzalloc(sizeof(SHIM_SERVICE_T), GFP_KERNEL); ++ ++ (void)instance; ++ ++ if (service) { ++ if (vchiu_queue_init(&service->queue, 64)) { ++ service->callback = setup->callback; ++ service->callback_param = setup->callback_param; ++ } else { ++ kfree(service); ++ service = NULL; + } - - ret = -ENOMEM; - if (usb_endpoint_dir_in(endpoint)) { ++ } ++ ++ return service; ++} ++ ++static void service_free(SHIM_SERVICE_T *service) ++{ ++ if (service) { ++ vchiu_queue_delete(&service->queue); ++ kfree(service); ++ } ++} ++ ++int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ if (service) { ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.callback = shim_callback; ++ params.userdata = service; ++ params.version = setup->version.version; ++ params.version_min = setup->version.version_min; ++ ++ status = vchiq_open_service(instance, ¶ms, ++ &service->handle); ++ if (status != VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ *handle = NULL; ++ } ++ } ++ ++ return (service != NULL) ? 0 : -1; ++} ++EXPORT_SYMBOL(vchi_service_open); ++ ++int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ if (service) { ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.callback = shim_callback; ++ params.userdata = service; ++ params.version = setup->version.version; ++ params.version_min = setup->version.version_min; ++ status = vchiq_add_service(instance, ¶ms, &service->handle); ++ ++ if (status != VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ *handle = NULL; ++ } ++ } ++ ++ return (service != NULL) ? 0 : -1; ++} ++EXPORT_SYMBOL(vchi_service_create); ++ ++int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) { ++ VCHIQ_STATUS_T status = vchiq_close_service(service->handle); ++ if (status == VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_close); ++ ++int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) { ++ VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); ++ if (status == VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_destroy); ++ ++int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_SERVICE_OPTION_T option, ++ int value) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_SERVICE_OPTION_T vchiq_option; ++ switch (option) { ++ case VCHI_SERVICE_OPTION_TRACE: ++ vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; ++ break; ++ case VCHI_SERVICE_OPTION_SYNCHRONOUS: ++ vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; ++ break; ++ default: ++ service = NULL; ++ break; ++ } ++ if (service) { ++ VCHIQ_STATUS_T status = ++ vchiq_set_service_option(service->handle, ++ vchiq_option, ++ value); ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_set_option); ++ ++int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ VCHIQ_STATUS_T status = vchiq_get_peer_version(service->handle, peer_version); ++ ret = vchiq_status_to_vchi( status ); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_get_peer_version); ++ ++/* ---------------------------------------------------------------------- ++ * read a uint32_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint32_t ++vchi_readbuf_uint32(const void *_ptr) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint32_t to buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint32(void *_ptr, uint32_t value) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (unsigned char)((value >> 0) & 0xFF); ++ ptr[1] = (unsigned char)((value >> 8) & 0xFF); ++ ptr[2] = (unsigned char)((value >> 16) & 0xFF); ++ ptr[3] = (unsigned char)((value >> 24) & 0xFF); ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint16_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint16_t ++vchi_readbuf_uint16(const void *_ptr) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint16_t into the buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint16(void *_ptr, uint16_t value) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (value >> 0) & 0xFF; ++ ptr[1] = (value >> 8) & 0xFF; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_use ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to increment refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) ++ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_use); ++ ++/*********************************************************** ++ * Name: vchi_service_release ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to decrement refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) ++ ret = vchiq_status_to_vchi( ++ vchiq_release_service(service->handle)); ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_release); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +new file mode 100644 +index 0000000..05e7979 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +@@ -0,0 +1,152 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_util.h" ++#include "vchiq_killable.h" ++ ++static inline int is_pow2(int i) ++{ ++ return i && !(i & (i - 1)); ++} ++ ++int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) ++{ ++ WARN_ON(!is_pow2(size)); ++ ++ queue->size = size; ++ queue->read = 0; ++ queue->write = 0; ++ ++ sema_init(&queue->pop, 0); ++ sema_init(&queue->push, 0); ++ ++ queue->storage = kzalloc(size * sizeof(VCHIQ_HEADER_T *), GFP_KERNEL); ++ if (queue->storage == NULL) { ++ vchiu_queue_delete(queue); ++ return 0; ++ } ++ return 1; ++} ++ ++void vchiu_queue_delete(VCHIU_QUEUE_T *queue) ++{ ++ if (queue->storage != NULL) ++ kfree(queue->storage); ++} ++ ++int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) ++{ ++ return queue->read == queue->write; ++} ++ ++int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) ++{ ++ return queue->write == queue->read + queue->size; ++} ++ ++void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) ++{ ++ while (queue->write == queue->read + queue->size) { ++ if (down_interruptible(&queue->pop) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ /* ++ * Write to queue->storage must be visible after read from ++ * queue->read ++ */ ++ smp_mb(); ++ ++ queue->storage[queue->write & (queue->size - 1)] = header; ++ ++ /* ++ * Write to queue->storage must be visible before write to ++ * queue->write ++ */ ++ smp_wmb(); ++ ++ queue->write++; ++ ++ up(&queue->push); ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) ++{ ++ while (queue->write == queue->read) { ++ if (down_interruptible(&queue->push) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ up(&queue->push); // We haven't removed anything from the queue. ++ ++ /* ++ * Read from queue->storage must be visible after read from ++ * queue->write ++ */ ++ smp_rmb(); ++ ++ return queue->storage[queue->read & (queue->size - 1)]; ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) ++{ ++ VCHIQ_HEADER_T *header; ++ ++ while (queue->write == queue->read) { ++ if (down_interruptible(&queue->push) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ /* ++ * Read from queue->storage must be visible after read from ++ * queue->write ++ */ ++ smp_rmb(); ++ ++ header = queue->storage[queue->read & (queue->size - 1)]; ++ ++ /* ++ * Read from queue->storage must be visible before write to ++ * queue->read ++ */ ++ smp_mb(); ++ ++ queue->read++; ++ ++ up(&queue->pop); ++ ++ return header; ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +new file mode 100644 +index 0000000..f4d0b66 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +@@ -0,0 +1,81 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_UTIL_H ++#define VCHIQ_UTIL_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for time_t */ ++#include ++#include ++ ++#include "vchiq_if.h" ++ ++typedef struct { ++ int size; ++ int read; ++ int write; ++ ++ struct semaphore pop; ++ struct semaphore push; ++ ++ VCHIQ_HEADER_T **storage; ++} VCHIU_QUEUE_T; ++ ++extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); ++extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); ++ ++extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); ++extern int vchiu_queue_is_full(VCHIU_QUEUE_T *queue); ++ ++extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); ++ ++extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); ++extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c +new file mode 100644 +index 0000000..b6bfa21 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c +@@ -0,0 +1,59 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "vchiq_build_info.h" ++#include ++ ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_hostname, "dc4-arm-01" ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)" ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_time, __TIME__ ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_date, __DATE__ ); ++ ++const char *vchiq_get_build_hostname( void ) ++{ ++ return vchiq_build_hostname; ++} ++ ++const char *vchiq_get_build_version( void ) ++{ ++ return vchiq_build_version; ++} ++ ++const char *vchiq_get_build_date( void ) ++{ ++ return vchiq_build_date; ++} ++ ++const char *vchiq_get_build_time( void ) ++{ ++ return vchiq_build_time; ++} -From 4da45e92b0bf783d7ab22c971310d2a5f0f5fda9 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 24 Jul 2014 21:24:03 +0100 -Subject: [PATCH 056/172] usb: core: make overcurrent messages more prominent - -Hub overcurrent messages are more serious than "debug". Increase loglevel. ---- - drivers/usb/core/hub.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index b4bfa3a..252a3f0 100644 ---- a/drivers/usb/core/hub.c -+++ b/drivers/usb/core/hub.c -@@ -4921,7 +4921,7 @@ static void port_event(struct usb_hub *hub, int port1) - if (portchange & USB_PORT_STAT_C_OVERCURRENT) { - u16 status = 0, unused; - -- dev_dbg(&port_dev->dev, "over-current change\n"); -+ dev_notice(&port_dev->dev, "over-current change\n"); - usb_clear_port_feature(hdev, port1, - USB_PORT_FEAT_C_OVER_CURRENT); - msleep(100); /* Cool down */ - -From 1da5f92cf41f89b2415bfb8d6726604b2fbb9aca Mon Sep 17 00:00:00 2001 +From ecd6be479b786f60efbd6937f3e9be1c42122474 Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Tue, 22 Jul 2014 15:41:04 +0100 -Subject: [PATCH 057/172] vcsm: VideoCore shared memory service for BCM2835 +Subject: [PATCH 012/104] vcsm: VideoCore shared memory service for BCM2835 Add experimental support for the VideoCore shared memory service. This allows user processes to allocate memory from VideoCore's @@ -114892,10 +102031,15093 @@ index 0000000..da1c523 +MODULE_DESCRIPTION("VideoCore SharedMemory Driver"); +MODULE_LICENSE("GPL v2"); -From c98028400be74c9edc4d170293f49dc2e27d5d80 Mon Sep 17 00:00:00 2001 +From 9e35eb3125ebff976e4c9ad85a74527f75f7426c Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:51:55 +0100 +Subject: [PATCH 013/104] Add hwrng (hardware random number generator) driver + +--- + drivers/char/hw_random/Kconfig | 11 ++++ + drivers/char/hw_random/Makefile | 1 + + drivers/char/hw_random/bcm2708-rng.c | 118 +++++++++++++++++++++++++++++++++++ + 3 files changed, 130 insertions(+) + create mode 100755 drivers/char/hw_random/bcm2708-rng.c + +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +index de57b38..1c97093 100644 +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -320,6 +320,17 @@ config HW_RANDOM_TPM + + If unsure, say Y. + ++config HW_RANDOM_BCM2708 ++ tristate "BCM2708 generic true random number generator support" ++ depends on HW_RANDOM && (ARCH_BCM2708 || ARCH_BCM2709) ++ ---help--- ++ This driver provides the kernel-side support for the BCM2708 hardware. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2708-rng. ++ ++ If unsure, say N. ++ + config HW_RANDOM_MSM + tristate "Qualcomm SoCs Random Number Generator support" + depends on HW_RANDOM && ARCH_QCOM +diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile +index 0b4cd57..78b019c 100644 +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o + obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o + obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o + obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o ++obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o + obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o + obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o +diff --git a/drivers/char/hw_random/bcm2708-rng.c b/drivers/char/hw_random/bcm2708-rng.c +new file mode 100755 +index 0000000..340f004 +--- /dev/null ++++ b/drivers/char/hw_random/bcm2708-rng.c +@@ -0,0 +1,118 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define RNG_CTRL (0x0) ++#define RNG_STATUS (0x4) ++#define RNG_DATA (0x8) ++#define RNG_FF_THRESHOLD (0xc) ++ ++/* enable rng */ ++#define RNG_RBGEN 0x1 ++/* double speed, less random mode */ ++#define RNG_RBG2X 0x2 ++ ++/* the initial numbers generated are "less random" so will be discarded */ ++#define RNG_WARMUP_COUNT 0x40000 ++ ++static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer) ++{ ++ void __iomem *rng_base = (void __iomem *)rng->priv; ++ unsigned words; ++ /* wait for a random number to be in fifo */ ++ do { ++ words = __raw_readl(rng_base + RNG_STATUS)>>24; ++ } ++ while (words == 0); ++ /* read the random number */ ++ *buffer = __raw_readl(rng_base + RNG_DATA); ++ return 4; ++} ++ ++static struct hwrng bcm2708_rng_ops = { ++ .name = "bcm2708", ++ .data_read = bcm2708_rng_data_read, ++}; ++ ++static int __init bcm2708_rng_init(void) ++{ ++ void __iomem *rng_base; ++ int err; ++ ++ /* map peripheral */ ++ rng_base = ioremap(RNG_BASE, 0x10); ++ pr_info("bcm2708_rng_init=%p\n", rng_base); ++ if (!rng_base) { ++ pr_err("bcm2708_rng_init failed to ioremap\n"); ++ return -ENOMEM; ++ } ++ bcm2708_rng_ops.priv = (unsigned long)rng_base; ++ ++ /* set warm-up count & enable */ ++ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); ++ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); ++ ++ /* register driver */ ++ err = hwrng_register(&bcm2708_rng_ops); ++ if (err) { ++ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err); ++ iounmap(rng_base); ++ } ++ return err; ++} ++ ++static void __exit bcm2708_rng_exit(void) ++{ ++ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv; ++ pr_info("bcm2708_rng_exit\n"); ++ /* disable rng hardware */ ++ __raw_writel(0, rng_base + RNG_CTRL); ++ /* unregister driver */ ++ hwrng_unregister(&bcm2708_rng_ops); ++ iounmap(rng_base); ++} ++ ++module_init(bcm2708_rng_init); ++module_exit(bcm2708_rng_exit); ++ ++MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver"); ++MODULE_LICENSE("GPL and additional rights"); + +From 0a983bacf33d058f1ab3c05daf9d384b82b320aa Mon Sep 17 00:00:00 2001 +From: Aron Szabo +Date: Sat, 16 Jun 2012 12:15:55 +0200 +Subject: [PATCH 014/104] lirc: added support for RaspberryPi GPIO + +lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others +See: https://github.com/raspberrypi/linux/issues/525 + +lirc: Remove restriction on gpio pins that can be used with lirc + +Compute Module, for example could use different pins + +lirc_rpi: Add parameter to specify input pin pull + +Depending on the connected IR circuitry it might be desirable to change the +gpios internal pull from it pull-down default behaviour. Add a module +parameter to allow the user to set it explicitly. + +Signed-off-by: Julian Scheel + +lirc-rpi: Use the higher-level irq control functions + +This module used to access the irq_chip methods of the +gpio controller directly, rather than going through the +standard enable_irq/irq_set_irq_type functions. This +caused problems on pinctrl-bcm2835 which only implements +the irq_enable/disable methods and not irq_unmask/mask. + +lirc-rpi: Correct the interrupt usage + +1) Correct the use of enable_irq (i.e. don't call it so often) +2) Correct the shutdown sequence. +3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier + +lirc-rpi: use getnstimeofday instead of read_current_timer + +read_current_timer isn't guaranteed to return values in +microseconds, and indeed it doesn't on a Pi2. + +Issue: linux#827 +--- + drivers/staging/media/lirc/Kconfig | 6 + + drivers/staging/media/lirc/Makefile | 1 + + drivers/staging/media/lirc/lirc_rpi.c | 667 ++++++++++++++++++++++++++++++++++ + 3 files changed, 674 insertions(+) + create mode 100644 drivers/staging/media/lirc/lirc_rpi.c + +diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig +index 6879c46..77c69f2 100644 +--- a/drivers/staging/media/lirc/Kconfig ++++ b/drivers/staging/media/lirc/Kconfig +@@ -32,6 +32,12 @@ config LIRC_PARALLEL + help + Driver for Homebrew Parallel Port Receivers + ++config LIRC_RPI ++ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" ++ depends on LIRC ++ help ++ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi ++ + config LIRC_SASEM + tristate "Sasem USB IR Remote" + depends on LIRC && USB +diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile +index 5430adf..9e53cd0 100644 +--- a/drivers/staging/media/lirc/Makefile ++++ b/drivers/staging/media/lirc/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o + obj-$(CONFIG_LIRC_IMON) += lirc_imon.o + obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o ++obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o + obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o + obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o + obj-$(CONFIG_LIRC_SIR) += lirc_sir.o +diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c +new file mode 100644 +index 0000000..8aa452d +--- /dev/null ++++ b/drivers/staging/media/lirc/lirc_rpi.c +@@ -0,0 +1,667 @@ ++/* ++ * lirc_rpi.c ++ * ++ * lirc_rpi - Device driver that records pulse- and pause-lengths ++ * (space-lengths) (just like the lirc_serial driver does) ++ * between GPIO interrupt events on the Raspberry Pi. ++ * Lots of code has been taken from the lirc_serial module, ++ * so I would like say thanks to the authors. ++ * ++ * Copyright (C) 2012 Aron Robert Szabo , ++ * Michael Bishop ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define LIRC_DRIVER_NAME "lirc_rpi" ++#define RBUF_LEN 256 ++#define LIRC_TRANSMITTER_LATENCY 50 ++ ++#ifndef MAX_UDELAY_MS ++#define MAX_UDELAY_US 5000 ++#else ++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) ++#endif ++ ++#define dprintk(fmt, args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ ++ fmt, ## args); \ ++ } while (0) ++ ++/* module parameters */ ++ ++/* set the default GPIO input pin */ ++static int gpio_in_pin = 18; ++/* set the default pull behaviour for input pin */ ++static int gpio_in_pull = BCM2708_PULL_DOWN; ++/* set the default GPIO output pin */ ++static int gpio_out_pin = 17; ++/* enable debugging messages */ ++static bool debug; ++/* -1 = auto, 0 = active high, 1 = active low */ ++static int sense = -1; ++/* use softcarrier by default */ ++static bool softcarrier = 1; ++/* 0 = do not invert output, 1 = invert output */ ++static bool invert = 0; ++ ++struct gpio_chip *gpiochip; ++static int irq_num; ++ ++/* forward declarations */ ++static long send_pulse(unsigned long length); ++static void send_space(long length); ++static void lirc_rpi_exit(void); ++ ++static struct platform_device *lirc_rpi_dev; ++static struct timeval lasttv = { 0, 0 }; ++static struct lirc_buffer rbuf; ++static spinlock_t lock; ++ ++/* initialized/set in init_timing_params() */ ++static unsigned int freq = 38000; ++static unsigned int duty_cycle = 50; ++static unsigned long period; ++static unsigned long pulse_width; ++static unsigned long space_width; ++ ++static void safe_udelay(unsigned long usecs) ++{ ++ while (usecs > MAX_UDELAY_US) { ++ udelay(MAX_UDELAY_US); ++ usecs -= MAX_UDELAY_US; ++ } ++ udelay(usecs); ++} ++ ++static unsigned long read_current_us(void) ++{ ++ struct timespec now; ++ getnstimeofday(&now); ++ return (now.tv_sec * 1000000) + (now.tv_nsec/1000); ++} ++ ++static int init_timing_params(unsigned int new_duty_cycle, ++ unsigned int new_freq) ++{ ++ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ duty_cycle = new_duty_cycle; ++ freq = new_freq; ++ period = 1000 * 1000000L / freq; ++ pulse_width = period * duty_cycle / 100; ++ space_width = period - pulse_width; ++ dprintk("in init_timing_params, freq=%d pulse=%ld, " ++ "space=%ld\n", freq, pulse_width, space_width); ++ return 0; ++} ++ ++static long send_pulse_softcarrier(unsigned long length) ++{ ++ int flag; ++ unsigned long actual, target; ++ unsigned long actual_us, initial_us, target_us; ++ ++ length *= 1000; ++ ++ actual = 0; target = 0; flag = 0; ++ actual_us = read_current_us(); ++ ++ while (actual < length) { ++ if (flag) { ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ target += space_width; ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ target += pulse_width; ++ } ++ initial_us = actual_us; ++ target_us = actual_us + (target - actual) / 1000; ++ /* ++ * Note - we've checked in ioctl that the pulse/space ++ * widths are big enough so that d is > 0 ++ */ ++ if ((int)(target_us - actual_us) > 0) ++ udelay(target_us - actual_us); ++ actual_us = read_current_us(); ++ actual += (actual_us - initial_us) * 1000; ++ flag = !flag; ++ } ++ return (actual-length) / 1000; ++} ++ ++static long send_pulse(unsigned long length) ++{ ++ if (length <= 0) ++ return 0; ++ ++ if (softcarrier) { ++ return send_pulse_softcarrier(length); ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ safe_udelay(length); ++ return 0; ++ } ++} ++ ++static void send_space(long length) ++{ ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ if (length <= 0) ++ return; ++ safe_udelay(length); ++} ++ ++static void rbwrite(int l) ++{ ++ if (lirc_buffer_full(&rbuf)) { ++ /* no new signals will be accepted */ ++ dprintk("Buffer overrun\n"); ++ return; ++ } ++ lirc_buffer_write(&rbuf, (void *)&l); ++} ++ ++static void frbwrite(int l) ++{ ++ /* simple noise filter */ ++ static int pulse, space; ++ static unsigned int ptr; ++ ++ if (ptr > 0 && (l & PULSE_BIT)) { ++ pulse += l & PULSE_MASK; ++ if (pulse > 250) { ++ rbwrite(space); ++ rbwrite(pulse | PULSE_BIT); ++ ptr = 0; ++ pulse = 0; ++ } ++ return; ++ } ++ if (!(l & PULSE_BIT)) { ++ if (ptr == 0) { ++ if (l > 20000) { ++ space = l; ++ ptr++; ++ return; ++ } ++ } else { ++ if (l > 20000) { ++ space += pulse; ++ if (space > PULSE_MASK) ++ space = PULSE_MASK; ++ space += l; ++ if (space > PULSE_MASK) ++ space = PULSE_MASK; ++ pulse = 0; ++ return; ++ } ++ rbwrite(space); ++ rbwrite(pulse | PULSE_BIT); ++ ptr = 0; ++ pulse = 0; ++ } ++ } ++ rbwrite(l); ++} ++ ++static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) ++{ ++ struct timeval tv; ++ long deltv; ++ int data; ++ int signal; ++ ++ /* use the GPIO signal level */ ++ signal = gpiochip->get(gpiochip, gpio_in_pin); ++ ++ if (sense != -1) { ++ /* get current time */ ++ do_gettimeofday(&tv); ++ ++ /* calc time since last interrupt in microseconds */ ++ deltv = tv.tv_sec-lasttv.tv_sec; ++ if (tv.tv_sec < lasttv.tv_sec || ++ (tv.tv_sec == lasttv.tv_sec && ++ tv.tv_usec < lasttv.tv_usec)) { ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": AIEEEE: your clock just jumped backwards\n"); ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": %d %d %lx %lx %lx %lx\n", signal, sense, ++ tv.tv_sec, lasttv.tv_sec, ++ tv.tv_usec, lasttv.tv_usec); ++ data = PULSE_MASK; ++ } else if (deltv > 15) { ++ data = PULSE_MASK; /* really long time */ ++ if (!(signal^sense)) { ++ /* sanity check */ ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": AIEEEE: %d %d %lx %lx %lx %lx\n", ++ signal, sense, tv.tv_sec, lasttv.tv_sec, ++ tv.tv_usec, lasttv.tv_usec); ++ /* ++ * detecting pulse while this ++ * MUST be a space! ++ */ ++ sense = sense ? 0 : 1; ++ } ++ } else { ++ data = (int) (deltv*1000000 + ++ (tv.tv_usec - lasttv.tv_usec)); ++ } ++ frbwrite(signal^sense ? data : (data|PULSE_BIT)); ++ lasttv = tv; ++ wake_up_interruptible(&rbuf.wait_poll); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int is_right_chip(struct gpio_chip *chip, void *data) ++{ ++ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); ++ ++ if (strcmp(data, chip->label) == 0) ++ return 1; ++ return 0; ++} ++ ++static int init_port(void) ++{ ++ int i, nlow, nhigh, ret; ++ ++ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++ ++ if (!gpiochip) ++ return -ENODEV; ++ ++ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_out_pin); ++ ret = -ENODEV; ++ goto exit_init_port; ++ } ++ ++ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_in_pin); ++ ret = -ENODEV; ++ goto exit_gpio_free_out_pin; ++ } ++ ++ bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); ++ gpiochip->direction_input(gpiochip, gpio_in_pin); ++ gpiochip->direction_output(gpiochip, gpio_out_pin, 1); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); ++ dprintk("to_irq %d\n", irq_num); ++ ++ /* if pin is high, then this must be an active low receiver. */ ++ if (sense == -1) { ++ /* wait 1/2 sec for the power supply */ ++ msleep(500); ++ ++ /* ++ * probe 9 times every 0.04s, collect "votes" for ++ * active high/low ++ */ ++ nlow = 0; ++ nhigh = 0; ++ for (i = 0; i < 9; i++) { ++ if (gpiochip->get(gpiochip, gpio_in_pin)) ++ nlow++; ++ else ++ nhigh++; ++ msleep(40); ++ } ++ sense = (nlow >= nhigh ? 1 : 0); ++ printk(KERN_INFO LIRC_DRIVER_NAME ++ ": auto-detected active %s receiver on GPIO pin %d\n", ++ sense ? "low" : "high", gpio_in_pin); ++ } else { ++ printk(KERN_INFO LIRC_DRIVER_NAME ++ ": manually using active %s receiver on GPIO pin %d\n", ++ sense ? "low" : "high", gpio_in_pin); ++ } ++ ++ return 0; ++ ++ exit_gpio_free_out_pin: ++ gpio_free(gpio_out_pin); ++ ++ exit_init_port: ++ return ret; ++} ++ ++// called when the character device is opened ++static int set_use_inc(void *data) ++{ ++ int result; ++ ++ /* initialize timestamp */ ++ do_gettimeofday(&lasttv); ++ ++ result = request_irq(irq_num, ++ (irq_handler_t) irq_handler, ++ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, ++ LIRC_DRIVER_NAME, (void*) 0); ++ ++ switch (result) { ++ case -EBUSY: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": IRQ %d is busy\n", ++ irq_num); ++ return -EBUSY; ++ case -EINVAL: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": Bad irq number or handler\n"); ++ return -EINVAL; ++ default: ++ dprintk("Interrupt %d obtained\n", ++ irq_num); ++ break; ++ }; ++ ++ /* initialize pulse/space widths */ ++ init_timing_params(duty_cycle, freq); ++ ++ return 0; ++} ++ ++static void set_use_dec(void *data) ++{ ++ /* GPIO Pin Falling/Rising Edge Detect Disable */ ++ irq_set_irq_type(irq_num, 0); ++ disable_irq(irq_num); ++ ++ free_irq(irq_num, (void *) 0); ++ ++ dprintk(KERN_INFO LIRC_DRIVER_NAME ++ ": freed IRQ %d\n", irq_num); ++} ++ ++static ssize_t lirc_write(struct file *file, const char *buf, ++ size_t n, loff_t *ppos) ++{ ++ int i, count; ++ unsigned long flags; ++ long delta = 0; ++ int *wbuf; ++ ++ count = n / sizeof(int); ++ if (n % sizeof(int) || count % 2 == 0) ++ return -EINVAL; ++ wbuf = memdup_user(buf, n); ++ if (IS_ERR(wbuf)) ++ return PTR_ERR(wbuf); ++ spin_lock_irqsave(&lock, flags); ++ ++ for (i = 0; i < count; i++) { ++ if (i%2) ++ send_space(wbuf[i] - delta); ++ else ++ delta = send_pulse(wbuf[i]); ++ } ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ kfree(wbuf); ++ return n; ++} ++ ++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) ++{ ++ int result; ++ __u32 value; ++ ++ switch (cmd) { ++ case LIRC_GET_SEND_MODE: ++ return -ENOIOCTLCMD; ++ break; ++ ++ case LIRC_SET_SEND_MODE: ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ /* only LIRC_MODE_PULSE supported */ ++ if (value != LIRC_MODE_PULSE) ++ return -ENOSYS; ++ break; ++ ++ case LIRC_GET_LENGTH: ++ return -ENOSYS; ++ break; ++ ++ case LIRC_SET_SEND_DUTY_CYCLE: ++ dprintk("SET_SEND_DUTY_CYCLE\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value <= 0 || value > 100) ++ return -EINVAL; ++ return init_timing_params(value, freq); ++ break; ++ ++ case LIRC_SET_SEND_CARRIER: ++ dprintk("SET_SEND_CARRIER\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value > 500000 || value < 20000) ++ return -EINVAL; ++ return init_timing_params(duty_cycle, value); ++ break; ++ ++ default: ++ return lirc_dev_fop_ioctl(filep, cmd, arg); ++ } ++ return 0; ++} ++ ++static const struct file_operations lirc_fops = { ++ .owner = THIS_MODULE, ++ .write = lirc_write, ++ .unlocked_ioctl = lirc_ioctl, ++ .read = lirc_dev_fop_read, ++ .poll = lirc_dev_fop_poll, ++ .open = lirc_dev_fop_open, ++ .release = lirc_dev_fop_close, ++ .llseek = no_llseek, ++}; ++ ++static struct lirc_driver driver = { ++ .name = LIRC_DRIVER_NAME, ++ .minor = -1, ++ .code_length = 1, ++ .sample_rate = 0, ++ .data = NULL, ++ .add_to_buf = NULL, ++ .rbuf = &rbuf, ++ .set_use_inc = set_use_inc, ++ .set_use_dec = set_use_dec, ++ .fops = &lirc_fops, ++ .dev = NULL, ++ .owner = THIS_MODULE, ++}; ++ ++static struct platform_driver lirc_rpi_driver = { ++ .driver = { ++ .name = LIRC_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init lirc_rpi_init(void) ++{ ++ int result; ++ ++ /* Init read buffer. */ ++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); ++ if (result < 0) ++ return -ENOMEM; ++ ++ result = platform_driver_register(&lirc_rpi_driver); ++ if (result) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": lirc register returned %d\n", result); ++ goto exit_buffer_free; ++ } ++ ++ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); ++ if (!lirc_rpi_dev) { ++ result = -ENOMEM; ++ goto exit_driver_unregister; ++ } ++ ++ result = platform_device_add(lirc_rpi_dev); ++ if (result) ++ goto exit_device_put; ++ ++ return 0; ++ ++ exit_device_put: ++ platform_device_put(lirc_rpi_dev); ++ ++ exit_driver_unregister: ++ platform_driver_unregister(&lirc_rpi_driver); ++ ++ exit_buffer_free: ++ lirc_buffer_free(&rbuf); ++ ++ return result; ++} ++ ++static void lirc_rpi_exit(void) ++{ ++ if (!lirc_rpi_dev->dev.of_node) ++ platform_device_unregister(lirc_rpi_dev); ++ platform_driver_unregister(&lirc_rpi_driver); ++ lirc_buffer_free(&rbuf); ++} ++ ++static int __init lirc_rpi_init_module(void) ++{ ++ int result; ++ ++ result = lirc_rpi_init(); ++ if (result) ++ return result; ++ ++ if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) { ++ result = -EINVAL; ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": invalid GPIO pin(s) specified!\n"); ++ goto exit_rpi; ++ } ++ ++ result = init_port(); ++ if (result < 0) ++ goto exit_rpi; ++ ++ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | ++ LIRC_CAN_SET_SEND_CARRIER | ++ LIRC_CAN_SEND_PULSE | ++ LIRC_CAN_REC_MODE2; ++ ++ driver.dev = &lirc_rpi_dev->dev; ++ driver.minor = lirc_register_driver(&driver); ++ ++ if (driver.minor < 0) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": device registration failed with %d\n", result); ++ result = -EIO; ++ goto exit_rpi; ++ } ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); ++ ++ return 0; ++ ++ exit_rpi: ++ lirc_rpi_exit(); ++ ++ return result; ++} ++ ++static void __exit lirc_rpi_exit_module(void) ++{ ++ lirc_unregister_driver(driver.minor); ++ ++ gpio_free(gpio_out_pin); ++ gpio_free(gpio_in_pin); ++ ++ lirc_rpi_exit(); ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); ++} ++ ++module_init(lirc_rpi_init_module); ++module_exit(lirc_rpi_exit_module); ++ ++MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); ++MODULE_AUTHOR("Aron Robert Szabo "); ++MODULE_AUTHOR("Michael Bishop "); ++MODULE_LICENSE("GPL"); ++ ++module_param(gpio_out_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" ++ " processor. (default 17"); ++ ++module_param(gpio_in_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." ++ " (default 18"); ++ ++module_param(gpio_in_pull, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." ++ " (0 = off, 1 = up, 2 = down, default down)"); ++ ++module_param(sense, int, S_IRUGO); ++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" ++ " (0 = active high, 1 = active low )"); ++ ++module_param(softcarrier, bool, S_IRUGO); ++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); ++ ++module_param(invert, bool, S_IRUGO); ++MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Enable debugging messages"); + +From 336b54eb6d0d48c2a57b7eabcbf73e54ee2f1c9f Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:49:20 +0100 +Subject: [PATCH 015/104] Add cpufreq driver + +--- + arch/arm/Kconfig | 1 + + drivers/cpufreq/Kconfig.arm | 8 ++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/bcm2835-cpufreq.c | 224 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 234 insertions(+) + create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index ee1bb5e..9fcd395 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -378,6 +378,7 @@ config ARCH_BCM2708 + select NEED_MACH_GPIO_H + select NEED_MACH_MEMORY_H + select CLKDEV_LOOKUP ++ select ARCH_HAS_CPUFREQ + select GENERIC_CLOCKEVENTS + select ARM_ERRATA_411920 + select MACH_BCM2708 +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index 1b06fc4..0b90de8 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -249,6 +249,14 @@ config ARM_SPEAR_CPUFREQ + help + This adds the CPUFreq driver support for SPEAr SOCs. + ++config ARM_BCM2835_CPUFREQ ++ bool "BCM2835 Driver" ++ default y ++ help ++ This adds the CPUFreq driver for BCM2835 ++ ++ If in doubt, say N. ++ + config ARM_TEGRA_CPUFREQ + bool "TEGRA CPUFreq support" + depends on ARCH_TEGRA +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 82a1821..50b1954 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o + obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o + obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o + obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o ++obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o + obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o + obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o + +diff --git a/drivers/cpufreq/bcm2835-cpufreq.c b/drivers/cpufreq/bcm2835-cpufreq.c +new file mode 100755 +index 0000000..447ca09 +--- /dev/null ++++ b/drivers/cpufreq/bcm2835-cpufreq.c +@@ -0,0 +1,224 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++/***************************************************************************** ++* FILENAME: bcm2835-cpufreq.h ++* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM ++* processor. Messages are sent to Videocore either setting or requesting the ++* frequency of the ARM in order to match an appropiate frequency to the current ++* usage of the processor. The policy which selects the frequency to use is ++* defined in the kernel .config file, but can be changed during runtime. ++*****************************************************************************/ ++ ++/* ---------- INCLUDES ---------- */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ---------- DEFINES ---------- */ ++/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */ ++#define MODULE_NAME "bcm2835-cpufreq" ++ ++#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */ ++ ++/* debug printk macros */ ++#ifdef CPUFREQ_DEBUG_ENABLE ++#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__) ++ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the message id */ ++ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */ ++ uint32_t data_size; /* amount of data being sent or received */ ++ uint32_t dev_id; /* the ID of the clock/voltage to get or set */ ++ uint32_t val; /* the value (e.g. rate (in Hz)) to set */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++/* ---------- GLOBALS ---------- */ ++static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */ ++ ++static struct cpufreq_frequency_table bcm2835_freq_table[] = { ++ {0, 0, 0}, ++ {0, 0, 0}, ++ {0, 0, CPUFREQ_TABLE_END}, ++}; ++ ++/* ++ =============================================== ++ clk_rate either gets or sets the clock rates. ++ =============================================== ++*/ ++static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) ++{ ++ int s, actual_rate=0; ++ struct vc_msg msg; ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ msg.msg_size = sizeof msg; ++ ++ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE; ++ msg.tag.buffer_size = 8; ++ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */ ++ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; ++ msg.tag.val = arm_rate * 1000; ++ ++ /* send the message */ ++ s = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in KHz */ ++ if (s == 0 && (msg.request_code & 0x80000000)) ++ actual_rate = msg.tag.val/1000; ++ ++ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate); ++ return actual_rate; ++} ++ ++static uint32_t bcm2835_cpufreq_get_clock(int tag) ++{ ++ int s; ++ int arm_rate = 0; ++ struct vc_msg msg; ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ msg.msg_size = sizeof msg; ++ msg.tag.tag_id = tag; ++ msg.tag.buffer_size = 8; ++ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */ ++ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; ++ ++ /* send the message */ ++ s = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in KHz */ ++ if (s == 0 && (msg.request_code & 0x80000000)) ++ arm_rate = msg.tag.val/1000; ++ ++ print_debug("%s frequency = %d\n", ++ tag == VCMSG_GET_CLOCK_RATE ? "Current": ++ tag == VCMSG_GET_MIN_CLOCK ? "Min": ++ tag == VCMSG_GET_MAX_CLOCK ? "Max": ++ "Unexpected", arm_rate); ++ ++ return arm_rate; ++} ++ ++/* ++ ==================================================== ++ Module Initialisation registers the cpufreq driver ++ ==================================================== ++*/ ++static int __init bcm2835_cpufreq_module_init(void) ++{ ++ print_debug("IN\n"); ++ return cpufreq_register_driver(&bcm2835_cpufreq_driver); ++} ++ ++/* ++ ============= ++ Module exit ++ ============= ++*/ ++static void __exit bcm2835_cpufreq_module_exit(void) ++{ ++ print_debug("IN\n"); ++ cpufreq_unregister_driver(&bcm2835_cpufreq_driver); ++ return; ++} ++ ++/* ++ ============================================================== ++ Initialisation function sets up the CPU policy for first use ++ ============================================================== ++*/ ++static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ /* measured value of how long it takes to change frequency */ ++ const unsigned int transition_latency = 355000; /* ns */ ++ ++ /* now find out what the maximum and minimum frequencies are */ ++ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK); ++ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK); ++ ++ print_info("min=%d max=%d\n", bcm2835_freq_table[0].frequency, bcm2835_freq_table[1].frequency); ++ return cpufreq_generic_init(policy, bcm2835_freq_table, transition_latency); ++} ++ ++/* ++ ===================================================================== ++ Target index function chooses the requested frequency from the table ++ ===================================================================== ++*/ ++ ++static int bcm2835_cpufreq_driver_target_index(struct cpufreq_policy *policy, unsigned int state) ++{ ++ unsigned int target_freq = bcm2835_freq_table[state].frequency; ++ unsigned int cur = bcm2835_cpufreq_set_clock(policy->cur, target_freq); ++ ++ if (!cur) ++ { ++ print_err("Error occurred setting a new frequency (%d)\n", target_freq); ++ return -EINVAL; ++ } ++ print_debug("%s: %i: freq %d->%d\n", policy->governor->name, state, policy->cur, cur); ++ return 0; ++} ++ ++/* ++ ====================================================== ++ Get function returns the current frequency from table ++ ====================================================== ++*/ ++ ++static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu) ++{ ++ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE); ++ print_debug("%d: freq=%d\n", cpu, actual_rate); ++ return actual_rate <= bcm2835_freq_table[0].frequency ? bcm2835_freq_table[0].frequency : bcm2835_freq_table[1].frequency; ++} ++ ++/* the CPUFreq driver */ ++static struct cpufreq_driver bcm2835_cpufreq_driver = { ++ .name = "BCM2835 CPUFreq", ++ .init = bcm2835_cpufreq_driver_init, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = bcm2835_cpufreq_driver_target_index, ++ .get = bcm2835_cpufreq_driver_get, ++ .attr = cpufreq_generic_attr, ++}; ++ ++MODULE_AUTHOR("Dorian Peake and Dom Cobley"); ++MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip"); ++MODULE_LICENSE("GPL"); ++ ++module_init(bcm2835_cpufreq_module_init); ++module_exit(bcm2835_cpufreq_module_exit); + +From 542e09877537f6c05253e0aa86cd8842984a007d Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 19:24:24 +0000 +Subject: [PATCH 016/104] Added hwmon/thermal driver for reporting core + temperature. Thanks Dorian + +--- + arch/arm/mach-bcm2708/bcm2708.c | 11 ++ + drivers/hwmon/Kconfig | 10 ++ + drivers/hwmon/Makefile | 1 + + drivers/hwmon/bcm2835-hwmon.c | 219 ++++++++++++++++++++++++++++++++++++++ + drivers/thermal/Kconfig | 6 ++ + drivers/thermal/Makefile | 1 + + drivers/thermal/bcm2835-thermal.c | 184 ++++++++++++++++++++++++++++++++ + 7 files changed, 432 insertions(+) + create mode 100644 drivers/hwmon/bcm2835-hwmon.c + create mode 100644 drivers/thermal/bcm2835-thermal.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index f54e3e9b..bae8ba5 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -499,6 +499,14 @@ static struct platform_device bcm2708_alsa_devices[] = { + }, + }; + ++static struct platform_device bcm2835_hwmon_device = { ++ .name = "bcm2835_hwmon", ++}; ++ ++static struct platform_device bcm2835_thermal_device = { ++ .name = "bcm2835_thermal", ++}; ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -611,6 +619,9 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + ++ bcm_register_device(&bcm2835_hwmon_device); ++ bcm_register_device(&bcm2835_thermal_device); ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 110fade..8c4474d 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1703,6 +1703,16 @@ config SENSORS_ULTRA45 + This driver provides support for the Ultra45 workstation environmental + sensors. + ++config SENSORS_BCM2835 ++ depends on THERMAL_BCM2835=n ++ tristate "Broadcom BCM2835 HWMON Driver" ++ help ++ If you say yes here you get support for the hardware ++ monitoring features of the BCM2835 Chip ++ ++ This driver can also be built as a module. If so, the module ++ will be called bcm2835-hwmon. ++ + if ACPI + + comment "ACPI drivers" +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index 6c94147..e7c60cb 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o + obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o + obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o + obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ++obj-$(CONFIG_SENSORS_BCM2835) += bcm2835-hwmon.o + + obj-$(CONFIG_PMBUS) += pmbus/ + +diff --git a/drivers/hwmon/bcm2835-hwmon.c b/drivers/hwmon/bcm2835-hwmon.c +new file mode 100644 +index 0000000..5bbed45 +--- /dev/null ++++ b/drivers/hwmon/bcm2835-hwmon.c +@@ -0,0 +1,219 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MODULE_NAME "bcm2835_hwmon" ++ ++/*#define HWMON_DEBUG_ENABLE*/ ++ ++#ifdef HWMON_DEBUG_ENABLE ++#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__) ++ ++#define VC_TAG_GET_TEMP 0x00030006 ++#define VC_TAG_GET_MAX_TEMP 0x0003000A ++ ++/* --- STRUCTS --- */ ++struct bcm2835_hwmon_data { ++ struct device *hwmon_dev; ++}; ++ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the tag ID for the temperature */ ++ uint32_t buffer_size; /* size of the buffer (should be 8) */ ++ uint32_t request_code; /* identifies message as a request (should be 0) */ ++ uint32_t id; /* extra ID field (should be 0) */ ++ uint32_t val; /* returned value of the temperature */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++typedef enum { ++ TEMP, ++ MAX_TEMP, ++} temp_type; ++ ++/* --- PROTOTYPES --- */ ++static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf); ++static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf); ++ ++/* --- GLOBALS --- */ ++ ++static struct bcm2835_hwmon_data *bcm2835_data; ++static struct platform_driver bcm2835_hwmon_driver; ++ ++static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0); ++static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP); ++static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP); ++ ++static struct attribute* bcm2835_attributes[] = { ++ &sensor_dev_attr_name.dev_attr.attr, ++ &sensor_dev_attr_temp1_input.dev_attr.attr, ++ &sensor_dev_attr_temp1_max.dev_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group bcm2835_attr_group = { ++ .attrs = bcm2835_attributes, ++}; ++ ++/* --- FUNCTIONS --- */ ++ ++static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf,"bcm2835_hwmon\n"); ++} ++ ++static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct vc_msg msg; ++ int result; ++ uint temp = 0; ++ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index; ++ ++ print_debug("IN"); ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ /* determine the message type */ ++ if(index == TEMP) ++ msg.tag.tag_id = VC_TAG_GET_TEMP; ++ else if (index == MAX_TEMP) ++ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP; ++ else ++ { ++ print_debug("Unknown temperature message!"); ++ return -EINVAL; ++ } ++ ++ msg.msg_size = sizeof msg; ++ msg.tag.buffer_size = 8; ++ ++ /* send the message */ ++ result = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in milli degrees C */ ++ if (result == 0 && (msg.request_code & 0x80000000)) ++ temp = (uint)msg.tag.val; ++ #ifdef HWMON_DEBUG_ENABLE ++ else ++ print_debug("Failed to get temperature!"); ++ #endif ++ print_debug("Got temperature as %u",temp); ++ print_debug("OUT"); ++ return sprintf(buf, "%u\n", temp); ++} ++ ++ ++static int bcm2835_hwmon_probe(struct platform_device *pdev) ++{ ++ int err; ++ ++ print_debug("IN"); ++ print_debug("HWMON Driver has been probed!"); ++ ++ /* check that the device isn't null!*/ ++ if(pdev == NULL) ++ { ++ print_debug("Platform device is empty!"); ++ return -ENODEV; ++ } ++ ++ /* allocate memory for neccessary data */ ++ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL); ++ if(!bcm2835_data) ++ { ++ print_debug("Unable to allocate memory for hwmon data!"); ++ err = -ENOMEM; ++ goto kzalloc_error; ++ } ++ ++ /* create the sysfs files */ ++ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group)) ++ { ++ print_debug("Unable to create sysfs files!"); ++ err = -EFAULT; ++ goto sysfs_error; ++ } ++ ++ /* register the hwmon device */ ++ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev); ++ if (IS_ERR(bcm2835_data->hwmon_dev)) ++ { ++ err = PTR_ERR(bcm2835_data->hwmon_dev); ++ goto hwmon_error; ++ } ++ print_debug("OUT"); ++ return 0; ++ ++ /* error goto's */ ++ hwmon_error: ++ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); ++ ++ sysfs_error: ++ kfree(bcm2835_data); ++ ++ kzalloc_error: ++ ++ return err; ++ ++} ++ ++static int bcm2835_hwmon_remove(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ hwmon_device_unregister(bcm2835_data->hwmon_dev); ++ ++ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); ++ print_debug("OUT"); ++ return 0; ++} ++ ++/* Hwmon Driver */ ++static struct platform_driver bcm2835_hwmon_driver = { ++ .probe = bcm2835_hwmon_probe, ++ .remove = bcm2835_hwmon_remove, ++ .driver = { ++ .name = "bcm2835_hwmon", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dorian Peake"); ++MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip"); ++ ++module_platform_driver(bcm2835_hwmon_driver); +diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig +index af40db0..4584b1e 100644 +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -238,6 +238,12 @@ config INTEL_POWERCLAMP + enforce idle time which results in more package C-state residency. The + user interface is exposed via generic thermal framework. + ++config THERMAL_BCM2835 ++ tristate "BCM2835 Thermal Driver" ++ help ++ This will enable temperature monitoring for the Broadcom BCM2835 ++ chip. If built as a module, it will be called 'bcm2835-thermal'. ++ + config X86_PKG_TEMP_THERMAL + tristate "X86 package temperature thermal driver" + depends on X86_THERMAL_VECTOR +diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile +index fa0dc48..453d4d9 100644 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o + obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o + obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o + obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o ++obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o + obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o + obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o + obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ +diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c +new file mode 100644 +index 0000000..85fceb5 +--- /dev/null ++++ b/drivers/thermal/bcm2835-thermal.c +@@ -0,0 +1,184 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* --- DEFINITIONS --- */ ++#define MODULE_NAME "bcm2835_thermal" ++ ++/*#define THERMAL_DEBUG_ENABLE*/ ++ ++#ifdef THERMAL_DEBUG_ENABLE ++#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++ ++#define VC_TAG_GET_TEMP 0x00030006 ++#define VC_TAG_GET_MAX_TEMP 0x0003000A ++ ++typedef enum { ++ TEMP, ++ MAX_TEMP, ++} temp_type; ++ ++/* --- STRUCTS --- */ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the tag ID for the temperature */ ++ uint32_t buffer_size; /* size of the buffer (should be 8) */ ++ uint32_t request_code; /* identifies message as a request (should be 0) */ ++ uint32_t id; /* extra ID field (should be 0) */ ++ uint32_t val; /* returned value of the temperature */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++struct bcm2835_thermal_data { ++ struct thermal_zone_device *thermal_dev; ++ struct vc_msg msg; ++}; ++ ++/* --- GLOBALS --- */ ++static struct bcm2835_thermal_data bcm2835_data; ++ ++/* Thermal Device Operations */ ++static struct thermal_zone_device_ops ops; ++ ++/* --- FUNCTIONS --- */ ++ ++static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id) ++{ ++ int result = -1, retry = 3; ++ print_debug("IN"); ++ ++ *temp = 0; ++ while (result != 0 && retry-- > 0) { ++ /* wipe all previous message data */ ++ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg); ++ ++ /* prepare message */ ++ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg; ++ bcm2835_data.msg.tag.buffer_size = 8; ++ bcm2835_data.msg.tag.tag_id = tag_id; ++ ++ /* send the message */ ++ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg); ++ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code); ++ if (!(bcm2835_data.msg.request_code & 0x80000000)) ++ result = -1; ++ } ++ ++ /* check if it was all ok and return the rate in milli degrees C */ ++ if (result == 0) ++ *temp = (uint)bcm2835_data.msg.tag.val; ++ else ++ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result); ++ print_debug("OUT"); ++ return result; ++} ++ ++static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp) ++{ ++ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP); ++} ++ ++static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp) ++{ ++ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP); ++} ++ ++static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type) ++{ ++ *trip_type = THERMAL_TRIP_HOT; ++ return 0; ++} ++ ++ ++static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode) ++{ ++ *dev_mode = THERMAL_DEVICE_ENABLED; ++ return 0; ++} ++ ++ ++static int bcm2835_thermal_probe(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ print_debug("THERMAL Driver has been probed!"); ++ ++ /* check that the device isn't null!*/ ++ if(pdev == NULL) ++ { ++ print_debug("Platform device is empty!"); ++ return -ENODEV; ++ } ++ ++ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0))) ++ { ++ print_debug("Unable to register the thermal device!"); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++ ++static int bcm2835_thermal_remove(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ ++ thermal_zone_device_unregister(bcm2835_data.thermal_dev); ++ ++ print_debug("OUT"); ++ ++ return 0; ++} ++ ++static struct thermal_zone_device_ops ops = { ++ .get_temp = bcm2835_get_temp, ++ .get_trip_temp = bcm2835_get_max_temp, ++ .get_trip_type = bcm2835_get_trip_type, ++ .get_mode = bcm2835_get_mode, ++}; ++ ++/* Thermal Driver */ ++static struct platform_driver bcm2835_thermal_driver = { ++ .probe = bcm2835_thermal_probe, ++ .remove = bcm2835_thermal_remove, ++ .driver = { ++ .name = "bcm2835_thermal", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dorian Peake"); ++MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); ++ ++module_platform_driver(bcm2835_thermal_driver); + +From 1665c59e341b2005edb3da64189cced3b29cde33 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 4 Nov 2013 18:56:10 +0000 +Subject: [PATCH 017/104] Add Chris Boot's i2c and spi drivers. + +i2c-bcm2708: fixed baudrate + +Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock). +In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits. +This resulted in incorrect setting of CDIV and higher baudrate than intended. +Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz +After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz +The correct baudrate is shown in the log after the cdiv > 0xffff correction. + +Perform I2C combined transactions when possible + +Perform I2C combined transactions whenever possible, within the +restrictions of the Broadcomm Serial Controller. + +Disable DONE interrupt during TA poll + +Prevent interrupt from being triggered if poll is missed and transfer +starts and finishes. + +i2c: Make combined transactions optional and disabled by default +--- + arch/arm/mach-bcm2708/Kconfig | 7 + + arch/arm/mach-bcm2708/bcm2708.c | 104 ++++++- + drivers/i2c/busses/Kconfig | 19 ++ + drivers/i2c/busses/Makefile | 2 + + drivers/i2c/busses/i2c-bcm2708.c | 449 ++++++++++++++++++++++++++++ + drivers/spi/Kconfig | 8 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-bcm2708.c | 626 +++++++++++++++++++++++++++++++++++++++ + 8 files changed, 1214 insertions(+), 2 deletions(-) + create mode 100644 drivers/i2c/busses/i2c-bcm2708.c + create mode 100644 drivers/spi/spi-bcm2708.c + +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index 9355841..e151ed4 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -31,4 +31,11 @@ config BCM2708_NOL2CACHE + help + Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. + ++config BCM2708_SPIDEV ++ bool "Bind spidev to SPI0 master" ++ depends on MACH_BCM2708 ++ depends on SPI ++ default y ++ help ++ Binds spidev driver to the SPI0 master + endmenu +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index bae8ba5..752dd46 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -205,7 +206,6 @@ static struct clk osc_clk = { + + /* warning - the USB needs a clock > 34MHz */ + +-#ifdef CONFIG_MMC_BCM2708 + static struct clk sdhost_clk = { + #ifdef CONFIG_ARCH_BCM2708_CHIPIT + .rate = 4000000, /* 4MHz */ +@@ -213,7 +213,6 @@ static struct clk sdhost_clk = { + .rate = 250000000, /* 250MHz */ + #endif + }; +-#endif + + static struct clk_lookup lookups[] = { + { /* UART0 */ +@@ -223,6 +222,15 @@ static struct clk_lookup lookups[] = { + { /* USB */ + .dev_id = "bcm2708_usb", + .clk = &osc_clk, ++ }, { /* SPI */ ++ .dev_id = "bcm2708_spi.0", ++ .clk = &sdhost_clk, ++ }, { /* BSC0 */ ++ .dev_id = "bcm2708_i2c.0", ++ .clk = &sdhost_clk, ++ }, { /* BSC1 */ ++ .dev_id = "bcm2708_i2c.1", ++ .clk = &sdhost_clk, + } + }; + +@@ -499,6 +507,89 @@ static struct platform_device bcm2708_alsa_devices[] = { + }, + }; + ++static struct resource bcm2708_spi_resources[] = { ++ { ++ .start = SPI0_BASE, ++ .end = SPI0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SPI, ++ .end = IRQ_SPI, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++static struct platform_device bcm2708_spi_device = { ++ .name = "bcm2708_spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), ++ .resource = bcm2708_spi_resources, ++ .dev = { ++ .dma_mask = &bcm2708_spi_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, ++}; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++static struct spi_board_info bcm2708_spi_devices[] = { ++#ifdef CONFIG_SPI_SPIDEV ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = SPI_MODE_0, ++ }, { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 1, ++ .mode = SPI_MODE_0, ++ } ++#endif ++}; ++#endif ++ ++static struct resource bcm2708_bsc0_resources[] = { ++ { ++ .start = BSC0_BASE, ++ .end = BSC0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc0_device = { ++ .name = "bcm2708_i2c", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), ++ .resource = bcm2708_bsc0_resources, ++}; ++ ++ ++static struct resource bcm2708_bsc1_resources[] = { ++ { ++ .start = BSC1_BASE, ++ .end = BSC1_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc1_device = { ++ .name = "bcm2708_i2c", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), ++ .resource = bcm2708_bsc1_resources, ++}; ++ + static struct platform_device bcm2835_hwmon_device = { + .name = "bcm2835_hwmon", + }; +@@ -619,6 +710,10 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + ++ bcm_register_device(&bcm2708_spi_device); ++ bcm_register_device(&bcm2708_bsc0_device); ++ bcm_register_device(&bcm2708_bsc1_device); ++ + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); + +@@ -628,6 +723,11 @@ void __init bcm2708_init(void) + } + system_rev = boardrev; + system_serial_low = serial; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); ++#endif + } + + static void timer_set_mode(enum clock_event_mode mode, +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 22da9c2..00c7baa 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -8,6 +8,25 @@ menu "I2C Hardware Bus support" + comment "PC SMBus host controller drivers" + depends on PCI + ++config I2C_BCM2708 ++ tristate "BCM2708 BSC" ++ depends on MACH_BCM2708 ++ help ++ Enabling this option will add BSC (Broadcom Serial Controller) ++ support for the BCM2708. BSC is a Broadcom proprietary bus compatible ++ with I2C/TWI/SMBus. ++ ++config I2C_BCM2708_BAUDRATE ++ prompt "BCM2708 I2C baudrate" ++ depends on I2C_BCM2708 ++ int ++ default 100000 ++ help ++ Set the I2C baudrate. This will alter the default value. A ++ different baudrate can be set by using a module parameter as well. If ++ no parameter is provided when loading, this is the value that will be ++ used. ++ + config I2C_ALI1535 + tristate "ALI 1535" + depends on PCI +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 3638feb..4905fae 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -2,6 +2,8 @@ + # Makefile for the i2c bus drivers. + # + ++obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o ++ + # ACPI drivers + obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o + +diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c +new file mode 100644 +index 0000000..7d385a3 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -0,0 +1,449 @@ ++/* ++ * Driver for Broadcom BCM2708 BSC Controllers ++ * ++ * Copyright (C) 2012 Chris Boot & Frank Buss ++ * ++ * This driver is inspired by: ++ * i2c-ocores.c, by Peter Korsgaard ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* BSC register offsets */ ++#define BSC_C 0x00 ++#define BSC_S 0x04 ++#define BSC_DLEN 0x08 ++#define BSC_A 0x0c ++#define BSC_FIFO 0x10 ++#define BSC_DIV 0x14 ++#define BSC_DEL 0x18 ++#define BSC_CLKT 0x1c ++ ++/* Bitfields in BSC_C */ ++#define BSC_C_I2CEN 0x00008000 ++#define BSC_C_INTR 0x00000400 ++#define BSC_C_INTT 0x00000200 ++#define BSC_C_INTD 0x00000100 ++#define BSC_C_ST 0x00000080 ++#define BSC_C_CLEAR_1 0x00000020 ++#define BSC_C_CLEAR_2 0x00000010 ++#define BSC_C_READ 0x00000001 ++ ++/* Bitfields in BSC_S */ ++#define BSC_S_CLKT 0x00000200 ++#define BSC_S_ERR 0x00000100 ++#define BSC_S_RXF 0x00000080 ++#define BSC_S_TXE 0x00000040 ++#define BSC_S_RXD 0x00000020 ++#define BSC_S_TXD 0x00000010 ++#define BSC_S_RXR 0x00000008 ++#define BSC_S_TXW 0x00000004 ++#define BSC_S_DONE 0x00000002 ++#define BSC_S_TA 0x00000001 ++ ++#define I2C_TIMEOUT_MS 150 ++ ++#define DRV_NAME "bcm2708_i2c" ++ ++static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; ++module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); ++MODULE_PARM_DESC(baudrate, "The I2C baudrate"); ++ ++static bool combined = false; ++module_param(combined, bool, 0644); ++MODULE_PARM_DESC(combined, "Use combined transactions"); ++ ++struct bcm2708_i2c { ++ struct i2c_adapter adapter; ++ ++ spinlock_t lock; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ ++ struct completion done; ++ ++ struct i2c_msg *msg; ++ int pos; ++ int nmsgs; ++ bool error; ++}; ++ ++/* ++ * This function sets the ALT mode on the I2C pins so that we can use them with ++ * the BSC hardware. ++ * ++ * FIXME: This is a hack. Use pinmux / pinctrl. ++ */ ++static void bcm2708_i2c_init_pinmode(int id) ++{ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ ++ int pin; ++ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); ++ ++ BUG_ON(id != 0 && id != 1); ++ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */ ++ for (pin = id*2+0; pin <= id*2+1; pin++) { ++printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin); ++ INP_GPIO(pin); /* set mode to GPIO input first */ ++ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ ++ } ++ ++ iounmap(gpio); ++ ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg) ++{ ++ return readl(bi->base + reg); ++} ++ ++static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val) ++{ ++ writel(val, bi->base + reg); ++} ++ ++static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi) ++{ ++ bcm2708_wr(bi, BSC_C, 0); ++ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); ++} ++ ++static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi) ++{ ++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len)) ++ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); ++} ++ ++static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) ++{ ++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len)) ++ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); ++} ++ ++static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) ++{ ++ unsigned long bus_hz; ++ u32 cdiv, s; ++ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; ++ ++ bus_hz = clk_get_rate(bi->clk); ++ cdiv = bus_hz / baudrate; ++ if (cdiv > 0xffff) ++ cdiv = 0xffff; ++ ++ if (bi->msg->flags & I2C_M_RD) ++ c |= BSC_C_INTR | BSC_C_READ; ++ else ++ c |= BSC_C_INTT; ++ ++ bcm2708_wr(bi, BSC_DIV, cdiv); ++ bcm2708_wr(bi, BSC_A, bi->msg->addr); ++ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); ++ if (combined) ++ { ++ /* Do the next two messages meet combined transaction criteria? ++ - Current message is a write, next message is a read ++ - Both messages to same slave address ++ - Write message can fit inside FIFO (16 bytes or less) */ ++ if ( (bi->nmsgs > 1) && ++ !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && ++ (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { ++ /* Fill FIFO with entire write message (16 byte FIFO) */ ++ while (bi->pos < bi->msg->len) ++ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); ++ /* Start write transfer (no interrupts, don't clear FIFO) */ ++ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); ++ /* poll for transfer start bit (should only take 1-20 polls) */ ++ do { ++ s = bcm2708_rd(bi, BSC_S); ++ } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE))); ++ /* Send next read message before the write transfer finishes. */ ++ bi->nmsgs--; ++ bi->msg++; ++ bi->pos = 0; ++ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); ++ c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; ++ } ++ } ++ bcm2708_wr(bi, BSC_C, c); ++} ++ ++static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) ++{ ++ struct bcm2708_i2c *bi = dev_id; ++ bool handled = true; ++ u32 s; ++ ++ spin_lock(&bi->lock); ++ ++ /* we may see camera interrupts on the "other" I2C channel ++ Just return if we've not sent anything */ ++ if (!bi->nmsgs || !bi->msg ) ++ goto early_exit; ++ ++ s = bcm2708_rd(bi, BSC_S); ++ ++ if (s & (BSC_S_CLKT | BSC_S_ERR)) { ++ bcm2708_bsc_reset(bi); ++ bi->error = true; ++ ++ /* wake up our bh */ ++ complete(&bi->done); ++ } else if (s & BSC_S_DONE) { ++ bi->nmsgs--; ++ ++ if (bi->msg->flags & I2C_M_RD) ++ bcm2708_bsc_fifo_drain(bi); ++ ++ bcm2708_bsc_reset(bi); ++ ++ if (bi->nmsgs) { ++ /* advance to next message */ ++ bi->msg++; ++ bi->pos = 0; ++ bcm2708_bsc_setup(bi); ++ } else { ++ /* wake up our bh */ ++ complete(&bi->done); ++ } ++ } else if (s & BSC_S_TXW) { ++ bcm2708_bsc_fifo_fill(bi); ++ } else if (s & BSC_S_RXR) { ++ bcm2708_bsc_fifo_drain(bi); ++ } else { ++ handled = false; ++ } ++ ++early_exit: ++ spin_unlock(&bi->lock); ++ ++ return handled ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct bcm2708_i2c *bi = adap->algo_data; ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&bi->lock, flags); ++ ++ reinit_completion(&bi->done); ++ bi->msg = msgs; ++ bi->pos = 0; ++ bi->nmsgs = num; ++ bi->error = false; ++ ++ bcm2708_bsc_setup(bi); ++ ++ /* unlockig _after_ the setup to avoid races with the interrupt routine */ ++ spin_unlock_irqrestore(&bi->lock, flags); ++ ++ ret = wait_for_completion_timeout(&bi->done, ++ msecs_to_jiffies(I2C_TIMEOUT_MS)); ++ if (ret == 0) { ++ dev_err(&adap->dev, "transfer timed out\n"); ++ spin_lock_irqsave(&bi->lock, flags); ++ bcm2708_bsc_reset(bi); ++ spin_unlock_irqrestore(&bi->lock, flags); ++ return -ETIMEDOUT; ++ } ++ ++ return bi->error ? -EIO : num; ++} ++ ++static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm bcm2708_i2c_algorithm = { ++ .master_xfer = bcm2708_i2c_master_xfer, ++ .functionality = bcm2708_i2c_functionality, ++}; ++ ++static int bcm2708_i2c_probe(struct platform_device *pdev) ++{ ++ struct resource *regs; ++ int irq, err = -ENOMEM; ++ struct clk *clk; ++ struct bcm2708_i2c *bi; ++ struct i2c_adapter *adap; ++ unsigned long bus_hz; ++ u32 cdiv; ++ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(&pdev->dev, "could not get IO memory\n"); ++ return -ENXIO; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "could not get IRQ\n"); ++ return irq; ++ } ++ ++ clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); ++ return PTR_ERR(clk); ++ } ++ ++ bcm2708_i2c_init_pinmode(pdev->id); ++ ++ bi = kzalloc(sizeof(*bi), GFP_KERNEL); ++ if (!bi) ++ goto out_clk_put; ++ ++ platform_set_drvdata(pdev, bi); ++ ++ adap = &bi->adapter; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC; ++ adap->algo = &bcm2708_i2c_algorithm; ++ adap->algo_data = bi; ++ adap->dev.parent = &pdev->dev; ++ adap->nr = pdev->id; ++ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ ++ switch (pdev->id) { ++ case 0: ++ adap->class = I2C_CLASS_HWMON; ++ break; ++ case 1: ++ adap->class = I2C_CLASS_DDC; ++ break; ++ default: ++ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n"); ++ err = -ENXIO; ++ goto out_free_bi; ++ } ++ ++ spin_lock_init(&bi->lock); ++ init_completion(&bi->done); ++ ++ bi->base = ioremap(regs->start, resource_size(regs)); ++ if (!bi->base) { ++ dev_err(&pdev->dev, "could not remap memory\n"); ++ goto out_free_bi; ++ } ++ ++ bi->irq = irq; ++ bi->clk = clk; ++ ++ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED, ++ dev_name(&pdev->dev), bi); ++ if (err) { ++ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); ++ goto out_iounmap; ++ } ++ ++ bcm2708_bsc_reset(bi); ++ ++ err = i2c_add_numbered_adapter(adap); ++ if (err < 0) { ++ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err); ++ goto out_free_irq; ++ } ++ ++ bus_hz = clk_get_rate(bi->clk); ++ cdiv = bus_hz / baudrate; ++ if (cdiv > 0xffff) { ++ cdiv = 0xffff; ++ baudrate = bus_hz / cdiv; ++ } ++ ++ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", ++ pdev->id, (unsigned long)regs->start, irq, baudrate); ++ ++ return 0; ++ ++out_free_irq: ++ free_irq(bi->irq, bi); ++out_iounmap: ++ iounmap(bi->base); ++out_free_bi: ++ kfree(bi); ++out_clk_put: ++ clk_put(clk); ++ return err; ++} ++ ++static int bcm2708_i2c_remove(struct platform_device *pdev) ++{ ++ struct bcm2708_i2c *bi = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ i2c_del_adapter(&bi->adapter); ++ free_irq(bi->irq, bi); ++ iounmap(bi->base); ++ clk_disable(bi->clk); ++ clk_put(bi->clk); ++ kfree(bi); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_i2c_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcm2708_i2c_probe, ++ .remove = bcm2708_i2c_remove, ++}; ++ ++// module_platform_driver(bcm2708_i2c_driver); ++ ++ ++static int __init bcm2708_i2c_init(void) ++{ ++ return platform_driver_register(&bcm2708_i2c_driver); ++} ++ ++static void __exit bcm2708_i2c_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_i2c_driver); ++} ++ ++module_init(bcm2708_i2c_init); ++module_exit(bcm2708_i2c_exit); ++ ++ ++ ++MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708"); ++MODULE_AUTHOR("Chris Boot "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index ab8dfbe..9628e75 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -86,6 +86,14 @@ config SPI_BCM2835 + is for the regular SPI controller. Slave mode operation is not also + not supported. + ++config SPI_BCM2708 ++ tristate "BCM2708 SPI controller driver (SPI0)" ++ depends on MACH_BCM2708 ++ help ++ This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This ++ driver is not compatible with the "Universal SPI Master" or the SPI slave ++ device. ++ + config SPI_BFIN5XX + tristate "SPI controller driver for ADI Blackfin5xx" + depends on BLACKFIN && !BF60x +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index d8cbf65..0dcd03d 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o + obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o + obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o + obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o ++obj-$(CONFIG_SPI_BCM2708) += spi-bcm2708.o + obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o + obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o + obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +new file mode 100644 +index 0000000..b04a57d +--- /dev/null ++++ b/drivers/spi/spi-bcm2708.c +@@ -0,0 +1,626 @@ ++/* ++ * Driver for Broadcom BCM2708 SPI Controllers ++ * ++ * Copyright (C) 2012 Chris Boot ++ * ++ * This driver is inspired by: ++ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos ++ * spi-atmel.c, Copyright (C) 2006 Atmel Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SPI register offsets */ ++#define SPI_CS 0x00 ++#define SPI_FIFO 0x04 ++#define SPI_CLK 0x08 ++#define SPI_DLEN 0x0c ++#define SPI_LTOH 0x10 ++#define SPI_DC 0x14 ++ ++/* Bitfields in CS */ ++#define SPI_CS_LEN_LONG 0x02000000 ++#define SPI_CS_DMA_LEN 0x01000000 ++#define SPI_CS_CSPOL2 0x00800000 ++#define SPI_CS_CSPOL1 0x00400000 ++#define SPI_CS_CSPOL0 0x00200000 ++#define SPI_CS_RXF 0x00100000 ++#define SPI_CS_RXR 0x00080000 ++#define SPI_CS_TXD 0x00040000 ++#define SPI_CS_RXD 0x00020000 ++#define SPI_CS_DONE 0x00010000 ++#define SPI_CS_LEN 0x00002000 ++#define SPI_CS_REN 0x00001000 ++#define SPI_CS_ADCS 0x00000800 ++#define SPI_CS_INTR 0x00000400 ++#define SPI_CS_INTD 0x00000200 ++#define SPI_CS_DMAEN 0x00000100 ++#define SPI_CS_TA 0x00000080 ++#define SPI_CS_CSPOL 0x00000040 ++#define SPI_CS_CLEAR_RX 0x00000020 ++#define SPI_CS_CLEAR_TX 0x00000010 ++#define SPI_CS_CPOL 0x00000008 ++#define SPI_CS_CPHA 0x00000004 ++#define SPI_CS_CS_10 0x00000002 ++#define SPI_CS_CS_01 0x00000001 ++ ++#define SPI_TIMEOUT_MS 150 ++ ++#define DRV_NAME "bcm2708_spi" ++ ++struct bcm2708_spi { ++ spinlock_t lock; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ bool stopping; ++ ++ struct list_head queue; ++ struct workqueue_struct *workq; ++ struct work_struct work; ++ struct completion done; ++ ++ const u8 *tx_buf; ++ u8 *rx_buf; ++ int len; ++}; ++ ++struct bcm2708_spi_state { ++ u32 cs; ++ u16 cdiv; ++}; ++ ++/* ++ * This function sets the ALT mode on the SPI pins so that we can use them with ++ * the SPI hardware. ++ * ++ * FIXME: This is a hack. Use pinmux / pinctrl. ++ */ ++static void bcm2708_init_pinmode(void) ++{ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ ++ int pin; ++ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); ++ ++ /* SPI is on GPIO 7..11 */ ++ for (pin = 7; pin <= 11; pin++) { ++ INP_GPIO(pin); /* set mode to GPIO input first */ ++ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ ++ } ++ ++ iounmap(gpio); ++ ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static inline u32 bcm2708_rd(struct bcm2708_spi *bs, unsigned reg) ++{ ++ return readl(bs->base + reg); ++} ++ ++static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val) ++{ ++ writel(val, bs->base + reg); ++} ++ ++static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len) ++{ ++ u8 byte; ++ ++ while (len--) { ++ byte = bcm2708_rd(bs, SPI_FIFO); ++ if (bs->rx_buf) ++ *bs->rx_buf++ = byte; ++ } ++} ++ ++static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len) ++{ ++ u8 byte; ++ u16 val; ++ ++ if (len > bs->len) ++ len = bs->len; ++ ++ if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) { ++ /* LoSSI mode */ ++ if (unlikely(len % 2)) { ++ printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n"); ++ bs->len = 0; ++ return; ++ } ++ while (len) { ++ if (bs->tx_buf) { ++ val = *(const u16 *)bs->tx_buf; ++ bs->tx_buf += 2; ++ } else ++ val = 0; ++ bcm2708_wr(bs, SPI_FIFO, val); ++ bs->len -= 2; ++ len -= 2; ++ } ++ return; ++ } ++ ++ while (len--) { ++ byte = bs->tx_buf ? *bs->tx_buf++ : 0; ++ bcm2708_wr(bs, SPI_FIFO, byte); ++ bs->len--; ++ } ++} ++ ++static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id) ++{ ++ struct spi_master *master = dev_id; ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ u32 cs; ++ ++ spin_lock(&bs->lock); ++ ++ cs = bcm2708_rd(bs, SPI_CS); ++ ++ if (cs & SPI_CS_DONE) { ++ if (bs->len) { /* first interrupt in a transfer */ ++ /* fill the TX fifo with up to 16 bytes */ ++ bcm2708_wr_fifo(bs, 16); ++ } else { /* transfer complete */ ++ /* disable interrupts */ ++ cs &= ~(SPI_CS_INTR | SPI_CS_INTD); ++ bcm2708_wr(bs, SPI_CS, cs); ++ ++ /* drain RX FIFO */ ++ while (cs & SPI_CS_RXD) { ++ bcm2708_rd_fifo(bs, 1); ++ cs = bcm2708_rd(bs, SPI_CS); ++ } ++ ++ /* wake up our bh */ ++ complete(&bs->done); ++ } ++ } else if (cs & SPI_CS_RXR) { ++ /* read 12 bytes of data */ ++ bcm2708_rd_fifo(bs, 12); ++ ++ /* write up to 12 bytes */ ++ bcm2708_wr_fifo(bs, 12); ++ } ++ ++ spin_unlock(&bs->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcm2708_setup_state(struct spi_master *master, ++ struct device *dev, struct bcm2708_spi_state *state, ++ u32 hz, u8 csel, u8 mode, u8 bpw) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ int cdiv; ++ unsigned long bus_hz; ++ u32 cs = 0; ++ ++ bus_hz = clk_get_rate(bs->clk); ++ ++ if (hz >= bus_hz) { ++ cdiv = 2; /* bus_hz / 2 is as fast as we can go */ ++ } else if (hz) { ++ cdiv = DIV_ROUND_UP(bus_hz, hz); ++ ++ /* CDIV must be a power of 2, so round up */ ++ cdiv = roundup_pow_of_two(cdiv); ++ ++ if (cdiv > 65536) { ++ dev_dbg(dev, ++ "setup: %d Hz too slow, cdiv %u; min %ld Hz\n", ++ hz, cdiv, bus_hz / 65536); ++ return -EINVAL; ++ } else if (cdiv == 65536) { ++ cdiv = 0; ++ } else if (cdiv == 1) { ++ cdiv = 2; /* 1 gets rounded down to 0; == 65536 */ ++ } ++ } else { ++ cdiv = 0; ++ } ++ ++ switch (bpw) { ++ case 8: ++ break; ++ case 9: ++ /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */ ++ cs |= SPI_CS_LEN; ++ break; ++ default: ++ dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n", ++ bpw); ++ return -EINVAL; ++ } ++ ++ if (mode & SPI_CPOL) ++ cs |= SPI_CS_CPOL; ++ if (mode & SPI_CPHA) ++ cs |= SPI_CS_CPHA; ++ ++ if (!(mode & SPI_NO_CS)) { ++ if (mode & SPI_CS_HIGH) { ++ cs |= SPI_CS_CSPOL; ++ cs |= SPI_CS_CSPOL0 << csel; ++ } ++ ++ cs |= csel; ++ } else { ++ cs |= SPI_CS_CS_10 | SPI_CS_CS_01; ++ } ++ ++ if (state) { ++ state->cs = cs; ++ state->cdiv = cdiv; ++ dev_dbg(dev, "setup: want %d Hz; " ++ "bus_hz=%lu / cdiv=%u == %lu Hz; " ++ "mode %u: cs 0x%08X\n", ++ hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs); ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_process_transfer(struct bcm2708_spi *bs, ++ struct spi_message *msg, struct spi_transfer *xfer) ++{ ++ struct spi_device *spi = msg->spi; ++ struct bcm2708_spi_state state, *stp; ++ int ret; ++ u32 cs; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ if (xfer->bits_per_word || xfer->speed_hz) { ++ ret = bcm2708_setup_state(spi->master, &spi->dev, &state, ++ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, ++ spi->chip_select, spi->mode, ++ xfer->bits_per_word ? xfer->bits_per_word : ++ spi->bits_per_word); ++ if (ret) ++ return ret; ++ ++ stp = &state; ++ } else { ++ stp = spi->controller_state; ++ } ++ ++ reinit_completion(&bs->done); ++ bs->tx_buf = xfer->tx_buf; ++ bs->rx_buf = xfer->rx_buf; ++ bs->len = xfer->len; ++ ++ cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA; ++ ++ bcm2708_wr(bs, SPI_CLK, stp->cdiv); ++ bcm2708_wr(bs, SPI_CS, cs); ++ ++ ret = wait_for_completion_timeout(&bs->done, ++ msecs_to_jiffies(SPI_TIMEOUT_MS)); ++ if (ret == 0) { ++ dev_err(&spi->dev, "transfer timed out\n"); ++ return -ETIMEDOUT; ++ } ++ ++ if (xfer->delay_usecs) ++ udelay(xfer->delay_usecs); ++ ++ if (list_is_last(&xfer->transfer_list, &msg->transfers) || ++ xfer->cs_change) { ++ /* clear TA and interrupt flags */ ++ bcm2708_wr(bs, SPI_CS, stp->cs); ++ } ++ ++ msg->actual_length += (xfer->len - bs->len); ++ ++ return 0; ++} ++ ++static void bcm2708_work(struct work_struct *work) ++{ ++ struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work); ++ unsigned long flags; ++ struct spi_message *msg; ++ struct spi_transfer *xfer; ++ int status = 0; ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ while (!list_empty(&bs->queue)) { ++ msg = list_first_entry(&bs->queue, struct spi_message, queue); ++ list_del_init(&msg->queue); ++ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ status = bcm2708_process_transfer(bs, msg, xfer); ++ if (status) ++ break; ++ } ++ ++ msg->status = status; ++ msg->complete(msg->context); ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ } ++ spin_unlock_irqrestore(&bs->lock, flags); ++} ++ ++static int bcm2708_spi_setup(struct spi_device *spi) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); ++ struct bcm2708_spi_state *state; ++ int ret; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ if (!(spi->mode & SPI_NO_CS) && ++ (spi->chip_select > spi->master->num_chipselect)) { ++ dev_dbg(&spi->dev, ++ "setup: invalid chipselect %u (%u defined)\n", ++ spi->chip_select, spi->master->num_chipselect); ++ return -EINVAL; ++ } ++ ++ state = spi->controller_state; ++ if (!state) { ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ spi->controller_state = state; ++ } ++ ++ ret = bcm2708_setup_state(spi->master, &spi->dev, state, ++ spi->max_speed_hz, spi->chip_select, spi->mode, ++ spi->bits_per_word); ++ if (ret < 0) { ++ kfree(state); ++ spi->controller_state = NULL; ++ return ret; ++ } ++ ++ dev_dbg(&spi->dev, ++ "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n", ++ spi->chip_select, spi->max_speed_hz, spi->bits_per_word, ++ spi->mode, state->cs, state->cdiv); ++ ++ return 0; ++} ++ ++static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ int ret; ++ unsigned long flags; ++ ++ if (unlikely(list_empty(&msg->transfers))) ++ return -EINVAL; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { ++ dev_dbg(&spi->dev, "missing rx or tx buf\n"); ++ return -EINVAL; ++ } ++ ++ if (!xfer->bits_per_word || xfer->speed_hz) ++ continue; ++ ++ ret = bcm2708_setup_state(spi->master, &spi->dev, NULL, ++ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, ++ spi->chip_select, spi->mode, ++ xfer->bits_per_word ? xfer->bits_per_word : ++ spi->bits_per_word); ++ if (ret) ++ return ret; ++ } ++ ++ msg->status = -EINPROGRESS; ++ msg->actual_length = 0; ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ list_add_tail(&msg->queue, &bs->queue); ++ queue_work(bs->workq, &bs->work); ++ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ return 0; ++} ++ ++static void bcm2708_spi_cleanup(struct spi_device *spi) ++{ ++ if (spi->controller_state) { ++ kfree(spi->controller_state); ++ spi->controller_state = NULL; ++ } ++} ++ ++static int bcm2708_spi_probe(struct platform_device *pdev) ++{ ++ struct resource *regs; ++ int irq, err = -ENOMEM; ++ struct clk *clk; ++ struct spi_master *master; ++ struct bcm2708_spi *bs; ++ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(&pdev->dev, "could not get IO memory\n"); ++ return -ENXIO; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "could not get IRQ\n"); ++ return irq; ++ } ++ ++ clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); ++ return PTR_ERR(clk); ++ } ++ ++ bcm2708_init_pinmode(); ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*bs)); ++ if (!master) { ++ dev_err(&pdev->dev, "spi_alloc_master() failed\n"); ++ goto out_clk_put; ++ } ++ ++ /* the spi->mode bits understood by this driver: */ ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS; ++ ++ master->bus_num = pdev->id; ++ master->num_chipselect = 3; ++ master->setup = bcm2708_spi_setup; ++ master->transfer = bcm2708_spi_transfer; ++ master->cleanup = bcm2708_spi_cleanup; ++ platform_set_drvdata(pdev, master); ++ ++ bs = spi_master_get_devdata(master); ++ ++ spin_lock_init(&bs->lock); ++ INIT_LIST_HEAD(&bs->queue); ++ init_completion(&bs->done); ++ INIT_WORK(&bs->work, bcm2708_work); ++ ++ bs->base = ioremap(regs->start, resource_size(regs)); ++ if (!bs->base) { ++ dev_err(&pdev->dev, "could not remap memory\n"); ++ goto out_master_put; ++ } ++ ++ bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev)); ++ if (!bs->workq) { ++ dev_err(&pdev->dev, "could not create workqueue\n"); ++ goto out_iounmap; ++ } ++ ++ bs->irq = irq; ++ bs->clk = clk; ++ bs->stopping = false; ++ ++ err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev), ++ master); ++ if (err) { ++ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); ++ goto out_workqueue; ++ } ++ ++ /* initialise the hardware */ ++ clk_enable(clk); ++ bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); ++ ++ err = spi_register_master(master); ++ if (err) { ++ dev_err(&pdev->dev, "could not register SPI master: %d\n", err); ++ goto out_free_irq; ++ } ++ ++ dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n", ++ (unsigned long)regs->start, irq); ++ ++ return 0; ++ ++out_free_irq: ++ free_irq(bs->irq, master); ++out_workqueue: ++ destroy_workqueue(bs->workq); ++out_iounmap: ++ iounmap(bs->base); ++out_master_put: ++ spi_master_put(master); ++out_clk_put: ++ clk_put(clk); ++ return err; ++} ++ ++static int bcm2708_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ ++ /* reset the hardware and block queue progress */ ++ spin_lock_irq(&bs->lock); ++ bs->stopping = true; ++ bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); ++ spin_unlock_irq(&bs->lock); ++ ++ flush_work(&bs->work); ++ ++ clk_disable(bs->clk); ++ clk_put(bs->clk); ++ free_irq(bs->irq, master); ++ iounmap(bs->base); ++ ++ spi_unregister_master(master); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_spi_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcm2708_spi_probe, ++ .remove = bcm2708_spi_remove, ++}; ++ ++ ++static int __init bcm2708_spi_init(void) ++{ ++ return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe); ++} ++module_init(bcm2708_spi_init); ++ ++static void __exit bcm2708_spi_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_spi_driver); ++} ++module_exit(bcm2708_spi_exit); ++ ++ ++//module_platform_driver(bcm2708_spi_driver); ++ ++MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708"); ++MODULE_AUTHOR("Chris Boot "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); + +From 6942b6c513172931ea563999626c8c179257a7e5 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Wed, 30 Jan 2013 12:45:18 +0000 +Subject: [PATCH 018/104] bcm2835: add v4l2 camera device + +- Supports raw YUV capture, preview, JPEG and H264. +- Uses videobuf2 for data transfer, using dma_buf. +- Uses 3.6.10 timestamping +- Camera power based on use +- Uses immutable input mode on video encoder + +Signed-off-by: Daniel Stone +Signed-off-by: Luke Diamand + +V4L2: Fixes from 6by9 + +V4L2: Fix EV values. Add manual shutter speed control + +V4L2 EV values should be in units of 1/1000. Corrected. +Add support for V4L2_CID_EXPOSURE_ABSOLUTE which should +give manual shutter control. Requires manual exposure mode +to be selected first. + +Signed-off-by: Dave Stevenson + +V4L2: Correct JPEG Q-factor range + +Should be 1-100, not 0-100 + +Signed-off-by: Dave Stevenson + +V4L2: Fix issue of driver jamming if STREAMON failed. + +Fix issue where the driver was left in a partially enabled +state if STREAMON failed, and would then reject many IOCTLs +as it thought it was streaming. + +Signed-off-by: Dave Stevenson + +V4L2: Fix ISO controls. + +Driver was passing the index to the GPU, and not the desired +ISO value. + +Signed-off-by: Dave Stevenson + +V4L2: Add flicker avoidance controls + +Add support for V4L2_CID_POWER_LINE_FREQUENCY to set flicker +avoidance frequencies. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for frame rate control. + +Add support for frame rate (or time per frame as V4L2 +inverts it) control via s_parm. + +Signed-off-by: Dave Stevenson + +V4L2: Improve G_FBUF handling so we pass conformance + +Return some sane numbers for get framebuffer so that +we pass conformance. + +Signed-off-by: Dave Stevenson + +V4L2: Fix information advertised through g_vidfmt + +Width and height were being stored based on incorrect +values. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for inline H264 headers + +Add support for V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER +to control H264 inline headers. +Requires firmware fix to work correctly, otherwise format +has to be set to H264 before this parameter is set. + +Signed-off-by: Dave Stevenson + +V4L2: Fix JPEG timestamp issue + +JPEG images were coming through from the GPU with timestamp +of 0. Detect this and give current system time instead +of some invalid value. + +Signed-off-by: Dave Stevenson + +V4L2: Fix issue when switching down JPEG resolution. + +JPEG buffer size calculation is based on input resolution. +Input resolution was being configured after output port +format. Caused failures if switching from one JPEG resolution +to a smaller one. + +Signed-off-by: Dave Stevenson + +V4L2: Enable MJPEG encoding + +Requires GPU firmware update to support MJPEG encoder. + +Signed-off-by: Dave Stevenson + +V4L2: Correct flag settings for compressed formats + +Set flags field correctly on enum_fmt_vid_cap for compressed +image formats. + +Signed-off-by: Dave Stevenson + +V4L2: H264 profile & level ctrls, FPS control and auto exp pri + +Several control handling updates. +H264 profile and level controls. +Timeperframe/FPS reworked to add V4L2_CID_EXPOSURE_AUTO_PRIORITY to +select whether AE is allowed to override the framerate specified. + +Signed-off-by: Dave Stevenson + +V4L2: Correct BGR24 to RGB24 in format table + +Signed-off-by: Dave Stevenson + +V4L2: Add additional pixel formats. Correct colourspace + +Adds the other flavours of YUYV, and NV12. +Corrects the overlay advertised colourspace. + +Signed-off-by: Dave Stevenson + +V4L2: Drop logging msg from info to debug + +Signed-off-by: Dave Stevenson + +V4L2: Initial pass at scene modes. + +Only supports exposure mode and metering modes. + +Signed-off-by: Dave Stevenson + +V4L2: Add manual white balance control. + +Adds support for V4L2_CID_RED_BALANCE and +V4L2_CID_BLUE_BALANCE. Only has an effect if +V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE has +V4L2_WHITE_BALANCE_MANUAL selected. + +Signed-off-by: Dave Stevenson + +config: Enable V4L / MMAL driver + +V4L2: Increase the MMAL timeout to 3sec + +MJPEG codec flush is now taking longer and results +in a kernel panic if the driver has stopped waiting for +the result when it finally completes. +Increase the timeout value from 1 to 3secs. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for setting H264_I_PERIOD + +Adds support for the parameter V4L2_CID_MPEG_VIDEO_H264_I_PERIOD +to set the frequency with which I frames are produced. + +Signed-off-by: Dave Stevenson + +V4L2: Enable GPU function for removing padding from images. + +GPU can now support arbitrary strides, although may require +additional processing to achieve it. Enable this feature +so that the images delivered are the size requested. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for V4L2_PIX_FMT_BGR32 + +Signed-off-by: Dave Stevenson + +V4L2: Set the colourspace to avoid odd YUV-RGB conversions + +Removes the amiguity from the conversion routines and stops +them dropping back to the SD vs HD choice of coeffs. + +Signed-off-by: Dave Stevenson + +V4L2: Make video/still threshold a run-time param + +Move the define for at what resolution the driver +switches from a video mode capture to a stills mode +capture to module parameters. + +Signed-off-by: Dave Stevenson + +V4L2: Fix incorrect pool sizing + +Signed-off-by: Dave Stevenson + +V4L2: Add option to disable enum_framesizes. + +Gstreamer's handling of a driver that advertises +V4L2_FRMSIZE_TYPE_STEPWISE to define the supported +resolutions is broken. See bug +https://bugzilla.gnome.org/show_bug.cgi?id=726521 + +Optional parameter of gst_v4l2src_is_broken added. +If non-zero, the driver claims not to support that +ioctl, and gstreamer should be happy again (it +guesses a set of defaults for itself). + +Signed-off-by: Dave Stevenson + +V4L2: Add support for more image formats + +Adds YVU420 (YV12), YVU420SP (NV21), and BGR888. + +Signed-off-by: Dave Stevenson + +V4L2: Extend range for V4L2_CID_MPEG_VIDEO_H264_I_PERIOD + +Request to extend the range from the fairly arbitrary +1000 frames (33 seconds at 30fps). Extend out to the +max range supported (int32 value). +Also allow 0, which is handled by the codec as only +send an I-frame on the first frame and never again. +There may be an exception if it detects a significant +scene change, but there's no easy way around that. + +Signed-off-by: Dave Stevenson + +bcm2835-camera: stop_streaming now has a void return + +BCM2835-V4L2: Fix compliance test failures + +VIDIOC_TRY_FMT and VIDIOC_S_FMT tests were faling due +to reporting V4L2_COLORSPACE_JPEG when the colour +format wasn't V4L2_PIX_FMT_JPEG. +Now reports V4L2_COLORSPACE_SMPTE170M for YUV formats. +--- + Documentation/video4linux/bcm2835-v4l2.txt | 60 + + drivers/media/platform/Kconfig | 2 + + drivers/media/platform/Makefile | 2 + + drivers/media/platform/bcm2835/Kconfig | 25 + + drivers/media/platform/bcm2835/Makefile | 5 + + drivers/media/platform/bcm2835/bcm2835-camera.c | 1828 +++++++++++++++++++++ + drivers/media/platform/bcm2835/bcm2835-camera.h | 126 ++ + drivers/media/platform/bcm2835/controls.c | 1322 +++++++++++++++ + drivers/media/platform/bcm2835/mmal-common.h | 52 + + drivers/media/platform/bcm2835/mmal-encodings.h | 127 ++ + drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + + drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + + drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ + drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ + drivers/media/platform/bcm2835/mmal-parameters.h | 656 ++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ + 17 files changed, 6941 insertions(+) + create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt + create mode 100644 drivers/media/platform/bcm2835/Kconfig + create mode 100644 drivers/media/platform/bcm2835/Makefile + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h + create mode 100644 drivers/media/platform/bcm2835/controls.c + create mode 100644 drivers/media/platform/bcm2835/mmal-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h + create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h + +diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt +new file mode 100644 +index 0000000..c585a8f +--- /dev/null ++++ b/Documentation/video4linux/bcm2835-v4l2.txt +@@ -0,0 +1,60 @@ ++ ++BCM2835 (aka Raspberry Pi) V4L2 driver ++====================================== ++ ++1. Copyright ++============ ++ ++Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ ++2. License ++========== ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT 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. ++ ++3. Quick Start ++============== ++ ++You need a version 1.0 or later of v4l2-ctl, available from: ++ git://git.linuxtv.org/v4l-utils.git ++ ++$ sudo modprobe bcm2835-v4l2 ++ ++Turn on the overlay: ++ ++$ v4l2-ctl --overlay=1 ++ ++Turn off the overlay: ++ ++$ v4l2-ctl --overlay=0 ++ ++Set the capture format for video: ++ ++$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 ++ ++(Note: 1088 not 1080). ++ ++Capture: ++ ++$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 ++ ++Stills capture: ++ ++$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 ++$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg ++ ++List of available formats: ++ ++$ v4l2-ctl --list-formats +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index d9b872b..2da9264 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -11,6 +11,8 @@ menuconfig V4L_PLATFORM_DRIVERS + + if V4L_PLATFORM_DRIVERS + ++source "drivers/media/platform/bcm2835/Kconfig" ++ + source "drivers/media/platform/marvell-ccic/Kconfig" + + config VIDEO_VIA_CAMERA +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index 3ec1547..d8d5927 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -2,6 +2,8 @@ + # Makefile for the video capture/playback device drivers. + # + ++obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ ++ + obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o + obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o + +diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig +new file mode 100644 +index 0000000..2cb1a68 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Kconfig +@@ -0,0 +1,25 @@ ++# Broadcom VideoCore IV v4l2 camera support ++ ++config VIDEO_BCM2835 ++ bool "Broadcom BCM2835 camera interface driver" ++ depends on VIDEO_V4L2 && (ARCH_BCM2708 || ARCH_BCM2709) ++ ---help--- ++ Say Y here to enable camera host interface devices for ++ Broadcom BCM2835 SoC. This operates over the VCHIQ interface ++ to a service running on VideoCore. ++ ++ ++if VIDEO_BCM2835 ++ ++config VIDEO_BCM2835_MMAL ++ tristate "Broadcom BM2835 MMAL camera interface driver" ++ depends on BCM2708_VCHIQ ++ select VIDEOBUF2_VMALLOC ++ ---help--- ++ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2835-v4l2.o ++ ++ ++endif # VIDEO_BM2835 +diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile +new file mode 100644 +index 0000000..f17c79c +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Makefile +@@ -0,0 +1,5 @@ ++bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o ++ ++obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o ++ ++ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +new file mode 100644 +index 0000000..eb4d831 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -0,0 +1,1828 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-encodings.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++#define BM2835_MMAL_VERSION "0.0.2" ++#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" ++#define MIN_WIDTH 16 ++#define MIN_HEIGHT 16 ++#define MAX_WIDTH 2592 ++#define MAX_HEIGHT 1944 ++#define MIN_BUFFER_SIZE (80*1024) ++ ++#define MAX_VIDEO_MODE_WIDTH 1280 ++#define MAX_VIDEO_MODE_HEIGHT 720 ++ ++MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); ++MODULE_AUTHOR("Vincent Sanders"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(BM2835_MMAL_VERSION); ++ ++int bcm2835_v4l2_debug; ++module_param_named(debug, bcm2835_v4l2_debug, int, 0644); ++MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); ++ ++int max_video_width = MAX_VIDEO_MODE_WIDTH; ++int max_video_height = MAX_VIDEO_MODE_HEIGHT; ++module_param(max_video_width, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(max_video_width, "Threshold for video mode"); ++module_param(max_video_height, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(max_video_height, "Threshold for video mode"); ++ ++/* Gstreamer bug https://bugzilla.gnome.org/show_bug.cgi?id=726521 ++ * v4l2src does bad (and actually wrong) things when the vidioc_enum_framesizes ++ * function says type V4L2_FRMSIZE_TYPE_STEPWISE, which we do by default. ++ * It's happier if we just don't say anything at all, when it then ++ * sets up a load of defaults that it thinks might work. ++ * If gst_v4l2src_is_broken is non-zero, then we remove the function from ++ * our function table list (actually switch to an alternate set, but same ++ * result). ++ */ ++int gst_v4l2src_is_broken = 0; ++module_param(gst_v4l2src_is_broken, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(gst_v4l2src_is_broken, "If non-zero, enable workaround for Gstreamer"); ++ ++static struct bm2835_mmal_dev *gdev; /* global device data */ ++ ++#define FPS_MIN 1 ++#define FPS_MAX 90 ++ ++/* timeperframe: min/max and default */ ++static const struct v4l2_fract ++ tpf_min = {.numerator = 1, .denominator = FPS_MAX}, ++ tpf_max = {.numerator = 1, .denominator = FPS_MIN}, ++ tpf_default = {.numerator = 1000, .denominator = 30000}; ++ ++/* video formats */ ++static struct mmal_fmt formats[] = { ++ { ++ .name = "4:2:0, packed YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_I420, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YUYV, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (LE)", ++ .fourcc = V4L2_PIX_FMT_RGB24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGR24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "JPEG", ++ .fourcc = V4L2_PIX_FMT_JPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_JPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, ++ }, ++ { ++ .name = "H264", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_H264, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ }, ++ { ++ .name = "MJPEG", ++ .fourcc = V4L2_PIX_FMT_MJPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_MJPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ }, ++ { ++ .name = "4:2:2, packed, YVYU", ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YVYU, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, VYUY", ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_VYUY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_UYVY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed, NV12", ++ .fourcc = V4L2_PIX_FMT_NV12, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_RGB24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed YVU", ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed, NV21", ++ .fourcc = V4L2_PIX_FMT_NV21, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV21, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB32 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGRA, ++ .depth = 32, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++}; ++ ++static struct mmal_fmt *get_format(struct v4l2_format *f) ++{ ++ struct mmal_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < ARRAY_SIZE(formats); k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == ARRAY_SIZE(formats)) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++/* ------------------------------------------------------------------ ++ Videobuf queue operations ++ ------------------------------------------------------------------*/ ++ ++static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ unsigned long size; ++ ++ /* refuse queue setup if port is not configured */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port not configured\n", __func__); ++ return -EINVAL; ++ } ++ ++ size = dev->capture.port->current_buffer.size; ++ if (size == 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port buffer size is zero\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) ++ *nbuffers = (dev->capture.port->current_buffer.num + 2); ++ ++ *nplanes = 1; ++ ++ sizes[0] = size; ++ ++ /* ++ * videobuf2-vmalloc allocator is context-less so no need to set ++ * alloc_ctxs array. ++ */ ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ unsigned long size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ BUG_ON(dev->capture.port == NULL); ++ BUG_ON(dev->capture.fmt == NULL); ++ ++ size = dev->capture.stride * dev->capture.height; ++ if (vb2_plane_size(vb, 0) < size) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline bool is_capturing(struct bm2835_mmal_dev *dev) ++{ ++ return dev->capture.camera_port == ++ &dev-> ++ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; ++} ++ ++static void buffer_cb(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, ++ struct mmal_buffer *buf, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) ++{ ++ struct bm2835_mmal_dev *dev = port->cb_ctx; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", ++ __func__, status, buf, length, mmal_flags, pts); ++ ++ if (status != 0) { ++ /* error in transfer */ ++ if (buf != NULL) { ++ /* there was a buffer with the error so return it */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ } ++ return; ++ } else if (length == 0) { ++ /* stream ended */ ++ if (buf != NULL) { ++ /* this should only ever happen if the port is ++ * disabled and there are buffers still queued ++ */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ pr_debug("Empty buffer"); ++ } else if (dev->capture.frame_count) { ++ /* grab another frame */ ++ if (is_capturing(dev)) { ++ pr_debug("Grab another frame"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } else { ++ if (dev->capture.frame_count) { ++ if (dev->capture.vc_start_timestamp != -1 && ++ pts != 0) { ++ s64 runtime_us = pts - ++ dev->capture.vc_start_timestamp; ++ u32 div = 0; ++ u32 rem = 0; ++ ++ div = ++ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); ++ buf->vb.v4l2_buf.timestamp.tv_sec = ++ dev->capture.kernel_start_ts.tv_sec - 1 + ++ div; ++ buf->vb.v4l2_buf.timestamp.tv_usec = ++ dev->capture.kernel_start_ts.tv_usec + rem; ++ ++ if (buf->vb.v4l2_buf.timestamp.tv_usec >= ++ USEC_PER_SEC) { ++ buf->vb.v4l2_buf.timestamp.tv_sec++; ++ buf->vb.v4l2_buf.timestamp.tv_usec -= ++ USEC_PER_SEC; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Convert start time %d.%06d and %llu " ++ "with offset %llu to %d.%06d\n", ++ (int)dev->capture.kernel_start_ts. ++ tv_sec, ++ (int)dev->capture.kernel_start_ts. ++ tv_usec, ++ dev->capture.vc_start_timestamp, pts, ++ (int)buf->vb.v4l2_buf.timestamp.tv_sec, ++ (int)buf->vb.v4l2_buf.timestamp. ++ tv_usec); ++ } else { ++ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); ++ } ++ ++ vb2_set_plane_payload(&buf->vb, 0, length); ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); ++ ++ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && ++ is_capturing(dev)) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Grab another frame as buffer has EOS"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } ++} ++ ++static int enable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed enabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ } ++ dev->camera_use_count++; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, "enabled camera (refcount %d)\n", ++ dev->camera_use_count); ++ return 0; ++} ++ ++static int disable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ v4l2_err(&dev->v4l2_dev, ++ "Disabled the camera when already disabled\n"); ++ return -EINVAL; ++ } ++ dev->camera_use_count--; ++ if (!dev->camera_use_count) { ++ unsigned int i = 0xFFFFFFFF; ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Disabling camera\n"); ++ ret = ++ vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed disabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]->control, ++ MMAL_PARAMETER_CAMERA_NUM, &i, ++ sizeof(i)); ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Camera refcount now %d\n", dev->camera_use_count); ++ return 0; ++} ++ ++static void buffer_queue(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); ++ int ret; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: dev:%p buf:%p\n", __func__, dev, buf); ++ ++ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); ++ buf->buffer_size = vb2_plane_size(&buf->vb, 0); ++ ++ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); ++ if (ret < 0) ++ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", ++ __func__); ++} ++ ++static int start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ int ret; ++ int parameter_size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ if (enable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); ++ return -EINVAL; ++ } ++ ++ /*init_completion(&dev->capture.frame_cmplt); */ ++ ++ /* enable frame capture */ ++ dev->capture.frame_count = 1; ++ ++ /* if the preview is not already running, wait for a few frames for AGC ++ * to settle down. ++ */ ++ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) ++ msleep(300); ++ ++ /* enable the connection from camera to encoder (if applicable) */ ++ if (dev->capture.camera_port != dev->capture.port ++ && dev->capture.camera_port) { ++ ret = vchiq_mmal_port_enable(dev->instance, ++ dev->capture.camera_port, NULL); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable encode tunnel - error %d\n", ++ ret); ++ return -1; ++ } ++ } ++ ++ /* Get VC timestamp at this point in time */ ++ parameter_size = sizeof(dev->capture.vc_start_timestamp); ++ if (vchiq_mmal_port_parameter_get(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_SYSTEM_TIME, ++ &dev->capture.vc_start_timestamp, ++ ¶meter_size)) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to get VC start time - update your VC f/w\n"); ++ ++ /* Flag to indicate just to rely on kernel timestamps */ ++ dev->capture.vc_start_timestamp = -1; ++ } else ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Start time %lld size %d\n", ++ dev->capture.vc_start_timestamp, parameter_size); ++ ++ v4l2_get_timestamp(&dev->capture.kernel_start_ts); ++ ++ /* enable the camera port */ ++ dev->capture.port->cb_ctx = dev; ++ ret = ++ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable capture port - error %d. " ++ "Disabling camera port again\n", ret); ++ ++ vchiq_mmal_port_disable(dev->instance, ++ dev->capture.camera_port); ++ if (disable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); ++ return -EINVAL; ++ } ++ return -1; ++ } ++ ++ /* capture the first frame */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static void stop_streaming(struct vb2_queue *vq) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ init_completion(&dev->capture.frame_cmplt); ++ dev->capture.frame_count = 0; ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "no capture port - stream not started?\n"); ++ return; ++ } ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); ++ ++ /* stop capturing frames */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ ++ /* wait for last frame to complete */ ++ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); ++ if (ret <= 0) ++ v4l2_err(&dev->v4l2_dev, ++ "error %d waiting for frame completion\n", ret); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling connection\n"); ++ ++ /* disable the connection from camera to encoder */ ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); ++ if (!ret && dev->capture.camera_port != dev->capture.port) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling port\n"); ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); ++ } else if (dev->capture.camera_port != dev->capture.port) { ++ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", ++ ret); ++ } ++ ++ if (disable_camera(dev) < 0) ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); ++} ++ ++static void bm2835_mmal_lock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_lock(&dev->mutex); ++} ++ ++static void bm2835_mmal_unlock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_unlock(&dev->mutex); ++} ++ ++static struct vb2_ops bm2835_mmal_video_qops = { ++ .queue_setup = queue_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .start_streaming = start_streaming, ++ .stop_streaming = stop_streaming, ++ .wait_prepare = bm2835_mmal_unlock, ++ .wait_finish = bm2835_mmal_lock, ++}; ++ ++/* ------------------------------------------------------------------ ++ IOCTL operations ++ ------------------------------------------------------------------*/ ++ ++/* overlay ioctl */ ++static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ f->flags = fmt->flags; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.win = dev->overlay; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ /* Only support one format so get the current one. */ ++ vidioc_g_fmt_vid_overlay(file, priv, f); ++ ++ /* todo: allow the size and/or offset to be changed. */ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ vidioc_try_fmt_vid_overlay(file, priv, f); ++ ++ dev->overlay = f->fmt.win; ++ ++ /* todo: program the preview port parameters */ ++ return 0; ++} ++ ++static int vidioc_overlay(struct file *file, void *f, unsigned int on) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *src; ++ struct vchiq_mmal_port *dst; ++ struct mmal_parameter_displayregion prev_config = { ++ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | ++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, ++ .layer = PREVIEW_LAYER, ++ .alpha = 255, ++ .fullscreen = 0, ++ .dest_rect = { ++ .x = dev->overlay.w.left, ++ .y = dev->overlay.w.top, ++ .width = dev->overlay.w.width, ++ .height = dev->overlay.w.height, ++ }, ++ }; ++ ++ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || ++ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) ++ return 0; /* already in requested state */ ++ ++ src = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ ++ if (!on) { ++ /* disconnect preview ports and disable component */ ++ ret = vchiq_mmal_port_disable(dev->instance, src); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel(dev->instance, src, ++ NULL); ++ if (ret >= 0) ++ ret = vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ disable_camera(dev); ++ return ret; ++ } ++ ++ /* set preview port format and connect it to output */ ++ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, src); ++ if (ret < 0) ++ goto error; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, ++ MMAL_PARAMETER_DISPLAYREGION, ++ &prev_config, sizeof(prev_config)); ++ if (ret < 0) ++ goto error; ++ ++ if (enable_camera(dev) < 0) ++ goto error; ++ ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto error; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", ++ src, dst); ++ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); ++error: ++ return ret; ++} ++ ++static int vidioc_g_fbuf(struct file *file, void *fh, ++ struct v4l2_framebuffer *a) ++{ ++ /* The video overlay must stay within the framebuffer and can't be ++ positioned independently. */ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ a->flags = V4L2_FBUF_FLAG_OVERLAY; ++ a->fmt.width = preview_port->es.video.width; ++ a->fmt.height = preview_port->es.video.height; ++ a->fmt.pixelformat = V4L2_PIX_FMT_YUV420; ++ a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; ++ a->fmt.sizeimage = (preview_port->es.video.width * ++ preview_port->es.video.height * 3)>>1; ++ a->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ return 0; ++} ++ ++/* input ioctls */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ /* only a single camera input */ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ sprintf(inp->name, "Camera %u", inp->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* capture ioctls */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ u32 major; ++ u32 minor; ++ ++ vchiq_mmal_version(dev->instance, &major, &minor); ++ ++ strcpy(cap->driver, "bm2835 mmal"); ++ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", ++ major, minor); ++ ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", dev->v4l2_dev.name); ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ f->flags = fmt->flags; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.pix.width = dev->capture.width; ++ f->fmt.pix.height = dev->capture.height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; ++ f->fmt.pix.bytesperline = dev->capture.stride; ++ f->fmt.pix.sizeimage = dev->capture.buffersize; ++ ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ else if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Clipping/aligning %dx%d format %08X\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); ++ ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 1, ++ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 1, 0); ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth)>>3; ++ ++ /* Image buffer has to be padded to allow for alignment, even though ++ * we then remove that padding before delivering the buffer. ++ */ ++ f->fmt.pix.sizeimage = ((f->fmt.pix.height+15)&~15) * ++ (((f->fmt.pix.width+31)&~31) * mfmt->depth) >> 3; ++ ++ if ((mfmt->flags & V4L2_FMT_FLAG_COMPRESSED) && ++ f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) ++ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Now %dx%d format %08X\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int mmal_setup_components(struct bm2835_mmal_dev *dev, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; ++ struct vchiq_mmal_component *encode_component = NULL; ++ struct mmal_fmt *mfmt = get_format(f); ++ ++ BUG_ON(!mfmt); ++ ++ if (dev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - disconnect previous tunnel\n"); ++ ++ /* Disconnect any previous connection */ ++ vchiq_mmal_port_connect_tunnel(dev->instance, ++ dev->capture.camera_port, NULL); ++ dev->capture.camera_port = NULL; ++ ret = vchiq_mmal_component_disable(dev->instance, ++ dev->capture. ++ encode_component); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to disable encode component %d\n", ++ ret); ++ ++ dev->capture.encode_component = NULL; ++ } ++ /* format dependant port setup */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_CAMERA: ++ /* Make a further decision on port based on resolution */ ++ if (f->fmt.pix.width <= max_video_width ++ && f->fmt.pix.height <= max_video_height) ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ else ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ break; ++ default: ++ break; ++ } ++ ++ if (!port) ++ return -EINVAL; ++ ++ if (encode_component) ++ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; ++ else ++ camera_port->format.encoding = mfmt->mmal; ++ ++ camera_port->format.encoding_variant = 0; ++ camera_port->es.video.width = f->fmt.pix.width; ++ camera_port->es.video.height = f->fmt.pix.height; ++ camera_port->es.video.crop.x = 0; ++ camera_port->es.video.crop.y = 0; ++ camera_port->es.video.crop.width = f->fmt.pix.width; ++ camera_port->es.video.crop.height = f->fmt.pix.height; ++ camera_port->es.video.frame_rate.num = 0; ++ camera_port->es.video.frame_rate.den = 1; ++ camera_port->es.video.color_space = MMAL_COLOR_SPACE_JPEG_JFIF; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); ++ ++ if (!ret ++ && camera_port == ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]) { ++ bool overlay_enabled = ++ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ /* Preview and encode ports need to match on resolution */ ++ if (overlay_enabled) { ++ /* Need to disable the overlay before we can update ++ * the resolution ++ */ ++ ret = ++ vchiq_mmal_port_disable(dev->instance, ++ preview_port); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ NULL); ++ } ++ preview_port->es.video.width = f->fmt.pix.width; ++ preview_port->es.video.height = f->fmt.pix.height; ++ preview_port->es.video.crop.x = 0; ++ preview_port->es.video.crop.y = 0; ++ preview_port->es.video.crop.width = f->fmt.pix.width; ++ preview_port->es.video.crop.height = f->fmt.pix.height; ++ preview_port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ preview_port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; ++ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); ++ if (overlay_enabled) { ++ ret = vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, ++ preview_port, ++ NULL); ++ } ++ } ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format %dx%d %08X\n", __func__, ++ f->fmt.pix.width, f->fmt.pix.height, ++ f->fmt.pix.pixelformat); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ if (encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - set up encode comp\n"); ++ ++ /* configure buffering */ ++ camera_port->current_buffer.size = ++ camera_port->recommended_buffer.size; ++ camera_port->current_buffer.num = ++ camera_port->recommended_buffer.num; ++ ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ camera_port, ++ &encode_component->input[0]); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to create connection\n", ++ __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ port->es.video.width = f->fmt.pix.width; ++ port->es.video.height = f->fmt.pix.height; ++ port->es.video.crop.x = 0; ++ port->es.video.crop.y = 0; ++ port->es.video.crop.width = f->fmt.pix.width; ++ port->es.video.crop.height = f->fmt.pix.height; ++ port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; ++ ++ port->format.encoding = mfmt->mmal; ++ port->format.encoding_variant = 0; ++ /* Set any encoding specific parameters */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ port->format.bitrate = ++ dev->capture.encode_bitrate; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ /* Could set EXIF parameters here */ ++ break; ++ default: ++ break; ++ } ++ ret = vchiq_mmal_port_set_format(dev->instance, ++ port); ++ if (ret) ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to set format %dx%d fmt %08X\n", ++ __func__, ++ f->fmt.pix.width, ++ f->fmt.pix.height, ++ f->fmt.pix.pixelformat ++ ); ++ } ++ ++ if (!ret) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ encode_component); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s Failed to enable encode components\n", ++ __func__); ++ } ++ } ++ if (!ret) { ++ /* configure buffering */ ++ port->current_buffer.num = 1; ++ port->current_buffer.size = ++ f->fmt.pix.sizeimage; ++ if (port->format.encoding == ++ MMAL_ENCODING_JPEG) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "JPG - buf size now %d was %d\n", ++ f->fmt.pix.sizeimage, ++ port->current_buffer.size); ++ port->current_buffer.size = ++ (f->fmt.pix.sizeimage < ++ (100 << 10)) ++ ? (100 << 10) : f->fmt.pix. ++ sizeimage; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "vid_cap - cur_buf.size set to %d\n", ++ f->fmt.pix.sizeimage); ++ port->current_buffer.alignment = 0; ++ } ++ } else { ++ /* configure buffering */ ++ camera_port->current_buffer.num = 1; ++ camera_port->current_buffer.size = f->fmt.pix.sizeimage; ++ camera_port->current_buffer.alignment = 0; ++ } ++ ++ if (!ret) { ++ dev->capture.fmt = mfmt; ++ dev->capture.stride = f->fmt.pix.bytesperline; ++ dev->capture.width = camera_port->es.video.crop.width; ++ dev->capture.height = camera_port->es.video.crop.height; ++ dev->capture.buffersize = port->current_buffer.size; ++ ++ /* select port for capture */ ++ dev->capture.port = port; ++ dev->capture.camera_port = camera_port; ++ dev->capture.encode_component = encode_component; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "Set dev->capture.fmt %08X, %dx%d, stride %d, size %d", ++ port->format.encoding, ++ dev->capture.width, dev->capture.height, ++ dev->capture.stride, dev->capture.buffersize); ++ } ++ } ++ ++ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ ++ return ret; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ /* try the format to set valid parameters */ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); ++ return ret; ++ } ++ ++ /* if a capture is running refuse to set format */ ++ if (vb2_is_busy(&dev->capture.vb_vidq)) { ++ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ /* If the format is unsupported v4l2 says we should switch to ++ * a supported one and not return an error. */ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ ret = mmal_setup_components(dev, f); ++ if (ret != 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: failed to setup mmal components: %d\n", ++ __func__, ret); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int vidioc_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ static const struct v4l2_frmsize_stepwise sizes = { ++ MIN_WIDTH, MAX_WIDTH, 2, ++ MIN_HEIGHT, MAX_HEIGHT, 2 ++ }; ++ int i; ++ ++ if (fsize->index) ++ return -EINVAL; ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fsize->pixel_format) ++ break; ++ if (i == ARRAY_SIZE(formats)) ++ return -EINVAL; ++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; ++ fsize->stepwise = sizes; ++ return 0; ++} ++ ++/* timeperframe is arbitrary and continous */ ++static int vidioc_enum_frameintervals(struct file *file, void *priv, ++ struct v4l2_frmivalenum *fival) ++{ ++ int i; ++ ++ if (fival->index) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fival->pixel_format) ++ break; ++ if (i == ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ /* regarding width & height - we support any within range */ ++ if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH || ++ fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT) ++ return -EINVAL; ++ ++ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ ++ /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ ++ fival->stepwise.min = tpf_min; ++ fival->stepwise.max = tpf_max; ++ fival->stepwise.step = (struct v4l2_fract) {1, 1}; ++ ++ return 0; ++} ++ ++static int vidioc_g_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ parm->parm.capture.timeperframe = dev->capture.timeperframe; ++ parm->parm.capture.readbuffers = 1; ++ return 0; ++} ++ ++#define FRACT_CMP(a, OP, b) \ ++ ((u64)(a).numerator * (b).denominator OP \ ++ (u64)(b).numerator * (a).denominator) ++ ++static int vidioc_s_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct v4l2_fract tpf; ++ struct mmal_parameter_rational fps_param; ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ tpf = parm->parm.capture.timeperframe; ++ ++ /* tpf: {*, 0} resets timing; clip to [min, max]*/ ++ tpf = tpf.denominator ? tpf : tpf_default; ++ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; ++ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; ++ ++ dev->capture.timeperframe = tpf; ++ parm->parm.capture.timeperframe = tpf; ++ parm->parm.capture.readbuffers = 1; ++ ++ fps_param.num = 0; /* Select variable fps, and then use ++ * FPS_RANGE to select the actual limits. ++ */ ++ fps_param.den = 1; ++ set_framerate_params(dev); ++ ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_enum_framesizes = vidioc_enum_framesizes, ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops_gstreamer = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ /* Remove this function ptr to fix gstreamer bug ++ .vidioc_enum_framesizes = vidioc_enum_framesizes, */ ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ------------------------------------------------------------------ ++ Driver init/finalise ++ ------------------------------------------------------------------*/ ++ ++static const struct v4l2_file_operations camera0_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = vb2_fop_release, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ ++ .mmap = vb2_fop_mmap, ++}; ++ ++static struct video_device vdev_template = { ++ .name = "camera0", ++ .fops = &camera0_fops, ++ .ioctl_ops = &camera0_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++static int set_camera_parameters(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *camera) ++{ ++ int ret; ++ struct mmal_parameter_camera_config cam_config = { ++ .max_stills_w = MAX_WIDTH, ++ .max_stills_h = MAX_HEIGHT, ++ .stills_yuv422 = 1, ++ .one_shot_stills = 1, ++ .max_preview_video_w = (max_video_width > 1920) ? ++ max_video_width : 1920, ++ .max_preview_video_h = (max_video_height > 1088) ? ++ max_video_height : 1088, ++ .num_preview_video_frames = 3, ++ .stills_capture_circular_buffer_height = 0, ++ .fast_preview_resume = 0, ++ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC ++ }; ++ ++ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, ++ MMAL_PARAMETER_CAMERA_CONFIG, ++ &cam_config, sizeof(cam_config)); ++ return ret; ++} ++ ++/* MMAL instance and component init */ ++static int __init mmal_init(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ struct mmal_es_format *format; ++ u32 bool_true = 1; ++ ++ ret = vchiq_mmal_init(&dev->instance); ++ if (ret < 0) ++ return ret; ++ ++ /* get the camera component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", ++ &dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_mmal; ++ ++ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < ++ MMAL_CAMERA_PORT_COUNT) { ++ ret = -EINVAL; ++ goto unreg_camera; ++ } ++ ++ ret = set_camera_parameters(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO], ++ MMAL_PARAMETER_NO_IMAGE_PADDING, ++ &bool_true, sizeof(bool_true)); ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ ++ format->es->video.width = 2592; ++ format->es->video.height = 1944; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 2592; ++ format->es->video.crop.height = 1944; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ dev->capture.width = format->es->video.width; ++ dev->capture.height = format->es->video.height; ++ dev->capture.fmt = &formats[0]; ++ dev->capture.encode_component = NULL; ++ dev->capture.timeperframe = tpf_default; ++ dev->capture.enc_profile = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; ++ dev->capture.enc_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE], ++ MMAL_PARAMETER_NO_IMAGE_PADDING, ++ &bool_true, sizeof(bool_true)); ++ ++ /* get the preview component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.video_render", ++ &dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { ++ ret = -EINVAL; ++ pr_debug("too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); ++ goto unreg_preview; ++ } ++ ++ /* get the image encoder component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.image_encode", ++ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ if (ret < 0) ++ goto unreg_preview; ++ ++ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, ++ 1); ++ goto unreg_image_encoder; ++ } ++ ++ /* get the video encoder component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", ++ &dev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ if (ret < 0) ++ goto unreg_image_encoder; ++ ++ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, ++ 1); ++ goto unreg_vid_encoder; ++ } ++ ++ { ++ struct vchiq_mmal_port *encoder_port = ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ encoder_port->format.encoding = MMAL_ENCODING_H264; ++ ret = vchiq_mmal_port_set_format(dev->instance, ++ encoder_port); ++ } ++ ++ { ++ unsigned int enable = 1; ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ &enable, sizeof(enable)); ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ &enable, ++ sizeof(enable)); ++ } ++ ret = bm2835_mmal_set_all_camera_controls(dev); ++ if (ret < 0) ++ goto unreg_vid_encoder; ++ ++ return 0; ++ ++unreg_vid_encoder: ++ pr_err("Cleanup: Destroy video encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++unreg_image_encoder: ++ pr_err("Cleanup: Destroy image encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++unreg_preview: ++ pr_err("Cleanup: Destroy video render\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++unreg_camera: ++ pr_err("Cleanup: Destroy camera\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ ++unreg_mmal: ++ vchiq_mmal_finalise(dev->instance); ++ return ret; ++} ++ ++static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, ++ struct video_device *vfd) ++{ ++ int ret; ++ ++ *vfd = vdev_template; ++ if (gst_v4l2src_is_broken) { ++ v4l2_info(&dev->v4l2_dev, ++ "Work-around for gstreamer issue is active.\n"); ++ vfd->ioctl_ops = &camera0_ioctl_ops_gstreamer; ++ } ++ ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ ++ vfd->lock = &dev->mutex; ++ ++ vfd->queue = &dev->capture.vb_vidq; ++ ++ /* video device needs to be able to access instance data */ ++ video_set_drvdata(vfd, dev); ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_info(vfd->v4l2_dev, ++ "V4L2 device registered as %s - stills mode > %dx%d\n", ++ video_device_node_name(vfd), max_video_width, max_video_height); ++ ++ return 0; ++} ++ ++static struct v4l2_format default_v4l2_format = { ++ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, ++ .fmt.pix.width = 1024, ++ .fmt.pix.bytesperline = 1024, ++ .fmt.pix.height = 768, ++ .fmt.pix.sizeimage = 1024*768, ++}; ++ ++static int __init bm2835_mmal_init(void) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev; ++ struct vb2_queue *q; ++ ++ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ /* setup device defaults */ ++ dev->overlay.w.left = 150; ++ dev->overlay.w.top = 50; ++ dev->overlay.w.width = 1024; ++ dev->overlay.w.height = 768; ++ dev->overlay.clipcount = 0; ++ dev->overlay.field = V4L2_FIELD_NONE; ++ ++ dev->capture.fmt = &formats[3]; /* JPEG */ ++ ++ /* v4l device registration */ ++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), ++ "%s", BM2835_MMAL_MODULE_NAME); ++ ret = v4l2_device_register(NULL, &dev->v4l2_dev); ++ if (ret) ++ goto free_dev; ++ ++ /* setup v4l controls */ ++ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); ++ if (ret < 0) ++ goto unreg_dev; ++ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; ++ ++ /* mmal init */ ++ ret = mmal_init(dev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* initialize queue */ ++ q = &dev->capture.vb_vidq; ++ memset(q, 0, sizeof(*q)); ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; ++ q->drv_priv = dev; ++ q->buf_struct_size = sizeof(struct mmal_buffer); ++ q->ops = &bm2835_mmal_video_qops; ++ q->mem_ops = &vb2_vmalloc_memops; ++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ret = vb2_queue_init(q); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ ++ mutex_init(&dev->mutex); ++ ++ /* initialise video devices */ ++ ret = bm2835_mmal_init_device(dev, &dev->vdev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* Really want to call vidioc_s_fmt_vid_cap with the default ++ * format, but currently the APIs don't join up. ++ */ ++ ret = mmal_setup_components(dev, &default_v4l2_format); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: could not setup components\n", __func__); ++ goto unreg_dev; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Broadcom 2835 MMAL video capture ver %s loaded.\n", ++ BM2835_MMAL_VERSION); ++ ++ gdev = dev; ++ return 0; ++ ++unreg_dev: ++ v4l2_ctrl_handler_free(&dev->ctrl_handler); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++free_dev: ++ kfree(dev); ++ ++ v4l2_err(&dev->v4l2_dev, ++ "%s: error %d while loading driver\n", ++ BM2835_MMAL_MODULE_NAME, ret); ++ ++ return ret; ++} ++ ++static void __exit bm2835_mmal_exit(void) ++{ ++ if (!gdev) ++ return; ++ ++ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", ++ video_device_node_name(&gdev->vdev)); ++ ++ video_unregister_device(&gdev->vdev); ++ ++ if (gdev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, ++ "mmal_exit - disconnect tunnel\n"); ++ vchiq_mmal_port_connect_tunnel(gdev->instance, ++ gdev->capture.camera_port, NULL); ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->capture.encode_component); ++ } ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_finalise(gdev->instance); ++ ++ v4l2_ctrl_handler_free(&gdev->ctrl_handler); ++ ++ v4l2_device_unregister(&gdev->v4l2_dev); ++ ++ kfree(gdev); ++} ++ ++module_init(bm2835_mmal_init); ++module_exit(bm2835_mmal_exit); +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +new file mode 100644 +index 0000000..7fe9f65 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -0,0 +1,126 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * core driver device ++ */ ++ ++#define V4L2_CTRL_COUNT 28 /* number of v4l controls */ ++ ++enum { ++ MMAL_COMPONENT_CAMERA = 0, ++ MMAL_COMPONENT_PREVIEW, ++ MMAL_COMPONENT_IMAGE_ENCODE, ++ MMAL_COMPONENT_VIDEO_ENCODE, ++ MMAL_COMPONENT_COUNT ++}; ++ ++enum { ++ MMAL_CAMERA_PORT_PREVIEW = 0, ++ MMAL_CAMERA_PORT_VIDEO, ++ MMAL_CAMERA_PORT_CAPTURE, ++ MMAL_CAMERA_PORT_COUNT ++}; ++ ++#define PREVIEW_LAYER 2 ++ ++extern int bcm2835_v4l2_debug; ++ ++struct bm2835_mmal_dev { ++ /* v4l2 devices */ ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct mutex mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; ++ enum v4l2_scene_mode scene_mode; ++ struct mmal_colourfx colourfx; ++ int hflip; ++ int vflip; ++ int red_gain; ++ int blue_gain; ++ enum mmal_parameter_exposuremode exposure_mode_user; ++ enum v4l2_exposure_auto_type exposure_mode_v4l2_user; ++ /* active exposure mode may differ if selected via a scene mode */ ++ enum mmal_parameter_exposuremode exposure_mode_active; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++ unsigned int manual_shutter_speed; ++ bool exp_auto_priority; ++ ++ /* allocated mmal instance and components */ ++ struct vchiq_mmal_instance *instance; ++ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; ++ int camera_use_count; ++ ++ struct v4l2_window overlay; ++ ++ struct { ++ unsigned int width; /* width */ ++ unsigned int height; /* height */ ++ unsigned int stride; /* stride */ ++ unsigned int buffersize; /* buffer size with padding */ ++ struct mmal_fmt *fmt; ++ struct v4l2_fract timeperframe; ++ ++ /* H264 encode bitrate */ ++ int encode_bitrate; ++ /* H264 bitrate mode. CBR/VBR */ ++ int encode_bitrate_mode; ++ /* H264 profile */ ++ enum v4l2_mpeg_video_h264_profile enc_profile; ++ /* H264 level */ ++ enum v4l2_mpeg_video_h264_level enc_level; ++ /* JPEG Q-factor */ ++ int q_factor; ++ ++ struct vb2_queue vb_vidq; ++ ++ /* VC start timestamp for streaming */ ++ s64 vc_start_timestamp; ++ /* Kernel start timestamp for streaming */ ++ struct timeval kernel_start_ts; ++ ++ struct vchiq_mmal_port *port; /* port being used for capture */ ++ /* camera port being used for capture */ ++ struct vchiq_mmal_port *camera_port; ++ /* component being used for encode */ ++ struct vchiq_mmal_component *encode_component; ++ /* number of frames remaining which driver should capture */ ++ unsigned int frame_count; ++ /* last frame completion */ ++ struct completion frame_cmplt; ++ ++ } capture; ++ ++}; ++ ++int bm2835_mmal_init_controls( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl); ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); ++int set_framerate_params(struct bm2835_mmal_dev *dev); ++ ++/* Debug helpers */ ++ ++#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ ++{ \ ++ v4l2_dbg(level, debug, dev, \ ++"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ ++ desc == NULL ? "" : desc, \ ++ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ ++ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ ++ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ ++} +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +new file mode 100644 +index 0000000..3017b94 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -0,0 +1,1322 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -4.0 to +4.0. ++ * MMAL values are in 1/6th increments so the MMAL range is -24 to +24. ++ * V4L2 docs say value "is expressed in terms of EV, drivers should interpret ++ * the values as 0.001 EV units, where the value 1000 stands for +1 EV." ++ * V4L2 is limited to a max of 32 values in a menu, so count in 1/3rds from ++ * -4 to +4 ++ */ ++static const s64 ev_bias_qmenu[] = { ++ -4000, -3667, -3333, ++ -3000, -2667, -2333, ++ -2000, -1667, -1333, ++ -1000, -667, -333, ++ 0, 333, 667, ++ 1000, 1333, 1667, ++ 2000, 2333, 2667, ++ 3000, 3333, 3667, ++ 4000 ++}; ++ ++/* Supported ISO values ++ * ISOO = auto ISO ++ */ ++static const s64 iso_qmenu[] = { ++ 0, 100, 200, 400, 800, ++}; ++ ++static const s64 mains_freq_qmenu[] = { ++ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO ++}; ++ ++/* Supported video encode modes */ ++static const s64 bitrate_mode_qmenu[] = { ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ++}; ++ ++enum bm2835_mmal_ctrl_type { ++ MMAL_CONTROL_TYPE_STD, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ MMAL_CONTROL_TYPE_INT_MENU, ++ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ ++}; ++ ++struct bm2835_mmal_v4l2_ctrl; ++ ++typedef int(bm2835_mmal_v4l2_ctrl_cb)( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); ++ ++struct bm2835_mmal_v4l2_ctrl { ++ u32 id; /* v4l2 control identifier */ ++ enum bm2835_mmal_ctrl_type type; ++ /* control minimum value or ++ * mask for MMAL_CONTROL_TYPE_STD_MENU */ ++ s32 min; ++ s32 max; /* maximum value of control */ ++ s32 def; /* default value of control */ ++ s32 step; /* step size of the control */ ++ const s64 *imenu; /* integer menu array */ ++ u32 mmal_id; /* mmal parameter id */ ++ bm2835_mmal_v4l2_ctrl_cb *setter; ++ bool ignore_errors; ++}; ++ ++struct v4l2_to_mmal_effects_setting { ++ u32 v4l2_effect; ++ u32 mmal_effect; ++ s32 col_fx_enable; ++ s32 col_fx_fixed_cbcr; ++ u32 u; ++ u32 v; ++ u32 num_effect_params; ++ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; ++ ++static const struct v4l2_to_mmal_effects_setting ++ v4l2_to_mmal_effects_values[] = { ++ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, ++ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, ++ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, ++ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } ++}; ++ ++struct v4l2_mmal_scene_config { ++ enum v4l2_scene_mode v4l2_scene; ++ enum mmal_parameter_exposuremode exposure_mode; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++}; ++ ++static const struct v4l2_mmal_scene_config scene_configs[] = { ++ /* V4L2_SCENE_MODE_NONE automatically added */ ++ { ++ V4L2_SCENE_MODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE ++ }, ++ { ++ V4L2_SCENE_MODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE ++ }, ++}; ++ ++/* control handlers*/ ++ ++static int ctrl_set_rational(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct mmal_parameter_rational rational_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ rational_value.num = ctrl->val; ++ rational_value.den = 100; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &rational_value, ++ sizeof(rational_value)); ++} ++ ++static int ctrl_set_value(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_value_menu(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ if (ctrl->val > mmal_ctrl->max || ctrl->val < mmal_ctrl->min) ++ return 1; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = mmal_ctrl->imenu[ctrl->val]; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ s32 s32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ s32_value = (ctrl->val-12)*2; /* Convert from index to 1/6ths */ ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &s32_value, sizeof(s32_value)); ++} ++ ++static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ u32_value = ((ctrl->val % 360) / 90) * 90; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++} ++ ++static int ctrl_set_flip(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ if (ctrl->id == V4L2_CID_HFLIP) ++ dev->hflip = ctrl->val; ++ else ++ dev->vflip = ctrl->val; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ if (dev->hflip && dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_BOTH; ++ else if (dev->hflip) ++ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; ++ else if (dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_VERTICAL; ++ else ++ u32_value = MMAL_PARAM_MIRROR_NONE; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++ ++} ++ ++static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode_user; ++ u32 shutter_speed = 0; ++ struct vchiq_mmal_port *control; ++ int ret = 0; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (mmal_ctrl->mmal_id == MMAL_PARAMETER_SHUTTER_SPEED) { ++ /* V4L2 is in 100usec increments. ++ * MMAL is 1usec. ++ */ ++ dev->manual_shutter_speed = ctrl->val * 100; ++ } else if (mmal_ctrl->mmal_id == MMAL_PARAMETER_EXPOSURE_MODE) { ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_AUTO: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_AUTO; ++ break; ++ ++ case V4L2_EXPOSURE_MANUAL: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF; ++ break; ++ } ++ dev->exposure_mode_user = exp_mode; ++ dev->exposure_mode_v4l2_user = ctrl->val; ++ } else if (mmal_ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { ++ dev->exp_auto_priority = ctrl->val; ++ } ++ ++ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { ++ if (exp_mode == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exp_mode, ++ sizeof(u32)); ++ dev->exposure_mode_active = exp_mode; ++ } ++ /* exposure_dynamic_framerate (V4L2_CID_EXPOSURE_AUTO_PRIORITY) should ++ * always apply irrespective of scene mode. ++ */ ++ ret += set_framerate_params(dev); ++ ++ return ret; ++} ++ ++static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_METERING_AVERAGE: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_SPOT: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; ++ break; ++ ++ /* todo matrix weighting not added to Linux API till 3.9 ++ case V4L2_EXPOSURE_METERING_MATRIX: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; ++ break; ++ */ ++ ++ } ++ ++ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { ++ struct vchiq_mmal_port *control; ++ u32 u32_value = dev->metering_mode; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ } else ++ return 0; ++} ++ ++static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ u32_value = MMAL_PARAM_FLICKERAVOID_OFF; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_50HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_60HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ u32_value = MMAL_PARAM_FLICKERAVOID_AUTO; ++ break; ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_WHITE_BALANCE_MANUAL: ++ u32_value = MMAL_PARAM_AWBMODE_OFF; ++ break; ++ ++ case V4L2_WHITE_BALANCE_AUTO: ++ u32_value = MMAL_PARAM_AWBMODE_AUTO; ++ break; ++ ++ case V4L2_WHITE_BALANCE_INCANDESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT_H: ++ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; ++ break; ++ ++ case V4L2_WHITE_BALANCE_HORIZON: ++ u32_value = MMAL_PARAM_AWBMODE_HORIZON; ++ break; ++ ++ case V4L2_WHITE_BALANCE_DAYLIGHT: ++ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLASH: ++ u32_value = MMAL_PARAM_AWBMODE_FLASH; ++ break; ++ ++ case V4L2_WHITE_BALANCE_CLOUDY: ++ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; ++ break; ++ ++ case V4L2_WHITE_BALANCE_SHADE: ++ u32_value = MMAL_PARAM_AWBMODE_SHADE; ++ break; ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_gains(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_awbgains gains; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (ctrl->id == V4L2_CID_RED_BALANCE) ++ dev->red_gain = ctrl->val; ++ else if (ctrl->id == V4L2_CID_BLUE_BALANCE) ++ dev->blue_gain = ctrl->val; ++ ++ gains.r_gain.num = dev->red_gain; ++ gains.b_gain.num = dev->blue_gain; ++ gains.r_gain.den = gains.b_gain.den = 1000; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &gains, sizeof(gains)); ++} ++ ++static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ int i, j; ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_imagefx_parameters imagefx; ++ ++ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { ++ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { ++ ++ imagefx.effect = ++ v4l2_to_mmal_effects_values[i].mmal_effect; ++ imagefx.num_effect_params = ++ v4l2_to_mmal_effects_values[i].num_effect_params; ++ ++ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) ++ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; ++ ++ for (j = 0; j < imagefx.num_effect_params; j++) ++ imagefx.effect_parameter[j] = ++ v4l2_to_mmal_effects_values[i].effect_params[j]; ++ ++ dev->colourfx.enable = ++ v4l2_to_mmal_effects_values[i].col_fx_enable; ++ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { ++ dev->colourfx.u = ++ v4l2_to_mmal_effects_values[i].u; ++ dev->colourfx.v = ++ v4l2_to_mmal_effects_values[i].v; ++ } ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ &imagefx, sizeof(imagefx)); ++ if (ret) ++ goto exit; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ } ++ } ++ ++exit: ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, ++ dev->colourfx.enable ? "true" : "false", ++ dev->colourfx.u, dev->colourfx.v, ++ ret, (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; ++ dev->colourfx.enable = ctrl->val & 0xff; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", ++ __func__, mmal_ctrl, ctrl->id, ctrl->val, ret, ++ (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ struct vchiq_mmal_port *encoder_out; ++ ++ dev->capture.encode_bitrate = ctrl->val; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &ctrl->val, sizeof(ctrl->val)); ++ ret = 0; ++ return ret; ++} ++ ++static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 bitrate_mode; ++ struct vchiq_mmal_port *encoder_out; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ dev->capture.encode_bitrate_mode = ctrl->val; ++ switch (ctrl->val) { ++ default: ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; ++ break; ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; ++ break; ++ } ++ ++ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &bitrate_mode, ++ sizeof(bitrate_mode)); ++ return 0; ++} ++ ++static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *jpeg_out; ++ ++ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_video_encode_param_output(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *vid_enc_ctl; ++ ++ vid_enc_ctl = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, vid_enc_ctl, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_video_encode_profile_level(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct mmal_parameter_video_profile param; ++ int ret = 0; ++ ++ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) { ++ switch (ctrl->val) { ++ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: ++ dev->capture.enc_profile = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ } else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) { ++ switch (ctrl->val) { ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: ++ dev->capture.enc_level = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ } ++ ++ if (!ret) { ++ switch (dev->capture.enc_profile) { ++ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: ++ param.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: ++ param.profile = ++ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: ++ param.profile = MMAL_VIDEO_PROFILE_H264_MAIN; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: ++ param.profile = MMAL_VIDEO_PROFILE_H264_HIGH; ++ break; ++ default: ++ /* Should never get here */ ++ break; ++ } ++ ++ switch (dev->capture.enc_level) { ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_1; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: ++ param.level = MMAL_VIDEO_LEVEL_H264_1b; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_11; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_12; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: ++ param.level = MMAL_VIDEO_LEVEL_H264_13; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_2; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_21; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_22; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_3; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_31; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_32; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_4; ++ break; ++ default: ++ /* Should never get here */ ++ break; ++ } ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0], ++ mmal_ctrl->mmal_id, ++ ¶m, sizeof(param)); ++ } ++ return ret; ++} ++ ++static int ctrl_set_scene_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = 0; ++ int shutter_speed; ++ struct vchiq_mmal_port *control; ++ ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "scene mode selected %d, was %d\n", ctrl->val, ++ dev->scene_mode); ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (ctrl->val == dev->scene_mode) ++ return 0; ++ ++ if (ctrl->val == V4L2_SCENE_MODE_NONE) { ++ /* Restore all user selections */ ++ dev->scene_mode = V4L2_SCENE_MODE_NONE; ++ ++ if (dev->exposure_mode_user == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ else ++ shutter_speed = 0; ++ ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", ++ __func__, shutter_speed, dev->exposure_mode_user, ++ dev->metering_mode); ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &dev->exposure_mode_user, ++ sizeof(u32)); ++ dev->exposure_mode_active = dev->exposure_mode_user; ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &dev->metering_mode, ++ sizeof(u32)); ++ ret += set_framerate_params(dev); ++ } else { ++ /* Set up scene mode */ ++ int i; ++ const struct v4l2_mmal_scene_config *scene = NULL; ++ int shutter_speed; ++ enum mmal_parameter_exposuremode exposure_mode; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++ ++ for (i = 0; i < ARRAY_SIZE(scene_configs); i++) { ++ if (scene_configs[i].v4l2_scene == ++ ctrl->val) { ++ scene = &scene_configs[i]; ++ break; ++ } ++ } ++ if (i >= ARRAY_SIZE(scene_configs)) ++ return -EINVAL; ++ ++ /* Set all the values */ ++ dev->scene_mode = ctrl->val; ++ ++ if (scene->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ else ++ shutter_speed = 0; ++ exposure_mode = scene->exposure_mode; ++ metering_mode = scene->metering_mode; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", ++ __func__, shutter_speed, exposure_mode, metering_mode); ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exposure_mode, ++ sizeof(u32)); ++ dev->exposure_mode_active = exposure_mode; ++ ret += vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exposure_mode, ++ sizeof(u32)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &metering_mode, ++ sizeof(u32)); ++ ret += set_framerate_params(dev); ++ } ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: Setting scene to %d, ret=%d\n", ++ __func__, ctrl->val, ret); ++ ret = -EINVAL; ++ } ++ return 0; ++} ++ ++static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct bm2835_mmal_dev *dev = ++ container_of(ctrl->handler, struct bm2835_mmal_dev, ++ ctrl_handler); ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; ++ int ret; ++ ++ if ((mmal_ctrl == NULL) || ++ (mmal_ctrl->id != ctrl->id) || ++ (mmal_ctrl->setter == NULL)) { ++ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); ++ return -EINVAL; ++ } ++ ++ ret = mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++ if (ret) ++ pr_warn("ctrl id:%d/MMAL param %08X- returned ret %d\n", ++ ctrl->id, mmal_ctrl->mmal_id, ret); ++ if (mmal_ctrl->ignore_errors) ++ ret = 0; ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { ++ .s_ctrl = bm2835_mmal_s_ctrl, ++}; ++ ++ ++ ++static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { ++ { ++ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SATURATION, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SHARPNESS, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_CONTRAST, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, ++ 0, 100, 50, 1, NULL, ++ MMAL_PARAMETER_BRIGHTNESS, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, ++ MMAL_PARAMETER_ISO, ++ &ctrl_set_value_menu, ++ false ++ }, ++ { ++ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_STABILISATION, ++ &ctrl_set_value, ++ false ++ }, ++/* { ++ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL ++ }, */ ++ { ++ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &ctrl_set_exposure, ++ false ++ }, ++/* todo this needs mixing in with set exposure ++ { ++ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ }, ++ */ ++ { ++ V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, ++ /* Units of 100usecs */ ++ 1, 1*1000*10, 100*10, 1, NULL, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &ctrl_set_exposure, ++ false ++ }, ++ { ++ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, ++ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, ++ MMAL_PARAMETER_EXPOSURE_COMP, ++ &ctrl_set_value_ev, ++ false ++ }, ++ { ++ V4L2_CID_EXPOSURE_AUTO_PRIORITY, MMAL_CONTROL_TYPE_STD, ++ 0, 1, ++ 0, 1, NULL, ++ 0, /* Dummy MMAL ID as it gets mapped into FPS range*/ ++ &ctrl_set_exposure, ++ false ++ }, ++ { ++ V4L2_CID_EXPOSURE_METERING, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &ctrl_set_metering_mode, ++ false ++ }, ++ { ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x3ff, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, ++ MMAL_PARAMETER_AWB_MODE, ++ &ctrl_set_awb_mode, ++ false ++ }, ++ { ++ V4L2_CID_RED_BALANCE, MMAL_CONTROL_TYPE_STD, ++ 1, 7999, 1000, 1, NULL, ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ &ctrl_set_awb_gains, ++ false ++ }, ++ { ++ V4L2_CID_BLUE_BALANCE, MMAL_CONTROL_TYPE_STD, ++ 1, 7999, 1000, 1, NULL, ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ &ctrl_set_awb_gains, ++ false ++ }, ++ { ++ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, 15, V4L2_COLORFX_NONE, 0, NULL, ++ MMAL_PARAMETER_IMAGE_EFFECT, ++ &ctrl_set_image_effect, ++ false ++ }, ++ { ++ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, ++ 0, 0xffff, 0x8080, 1, NULL, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &ctrl_set_colfx, ++ false ++ }, ++ { ++ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, ++ 0, 360, 0, 90, NULL, ++ MMAL_PARAMETER_ROTATION, ++ &ctrl_set_rotate, ++ false ++ }, ++ { ++ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false ++ }, ++ { ++ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, ++ 0, 0, bitrate_mode_qmenu, ++ MMAL_PARAMETER_RATECONTROL, ++ &ctrl_set_bitrate_mode, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, ++ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ &ctrl_set_bitrate, ++ false ++ }, ++ { ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, ++ 1, 100, ++ 30, 1, NULL, ++ MMAL_PARAMETER_JPEG_Q_FACTOR, ++ &ctrl_set_image_encode_output, ++ false ++ }, ++ { ++ V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(mains_freq_qmenu) - 1, ++ 1, 1, NULL, ++ MMAL_PARAMETER_FLICKER_AVOID, ++ &ctrl_set_flicker_avoidance, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, MMAL_CONTROL_TYPE_STD, ++ 0, 1, ++ 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, ++ &ctrl_set_video_encode_param_output, ++ true /* Errors ignored as requires latest firmware to work */ ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_H264_PROFILE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~((1<ctrls[c]) && (v4l2_ctrls[c].setter)) { ++ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], ++ &v4l2_ctrls[c]); ++ if (!v4l2_ctrls[c].ignore_errors && ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Failed when setting default values for ctrl %d\n", ++ c); ++ break; ++ } ++ } ++ } ++ return ret; ++} ++ ++int set_framerate_params(struct bm2835_mmal_dev *dev) ++{ ++ struct mmal_parameter_fps_range fps_range; ++ int ret; ++ ++ if ((dev->exposure_mode_active != MMAL_PARAM_EXPOSUREMODE_OFF) && ++ (dev->exp_auto_priority)) { ++ /* Variable FPS. Define min FPS as 1fps. ++ * Max as max defined FPS. ++ */ ++ fps_range.fps_low.num = 1; ++ fps_range.fps_low.den = 1; ++ fps_range.fps_high.num = dev->capture.timeperframe.denominator; ++ fps_range.fps_high.den = dev->capture.timeperframe.numerator; ++ } else { ++ /* Fixed FPS - set min and max to be the same */ ++ fps_range.fps_low.num = fps_range.fps_high.num = ++ dev->capture.timeperframe.denominator; ++ fps_range.fps_low.den = fps_range.fps_high.den = ++ dev->capture.timeperframe.numerator; ++ } ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Set fps range to %d/%d to %d/%d\n", ++ fps_range.fps_low.num, ++ fps_range.fps_low.den, ++ fps_range.fps_high.num, ++ fps_range.fps_high.den ++ ); ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ if (ret) ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Failed to set fps ret %d\n", ++ ret); ++ ++ return ret; ++ ++} ++ ++int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl) ++{ ++ int c; ++ const struct bm2835_mmal_v4l2_ctrl *ctrl; ++ ++ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_STD: ++ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->min, ctrl->max, ctrl->step, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ { ++ int mask = ctrl->min; ++ ++ if (ctrl->id == V4L2_CID_SCENE_MODE) { ++ /* Special handling to work out the mask ++ * value based on the scene_configs array ++ * at runtime. Reduces the chance of ++ * mismatches. ++ */ ++ int i; ++ mask = 1<ctrls[c] = v4l2_ctrl_new_std_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, mask, ctrl->def); ++ break; ++ } ++ ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->def, ctrl->imenu); ++ break; ++ ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ /* skip this entry when constructing controls */ ++ continue; ++ } ++ ++ if (hdl->error) ++ break; ++ ++ dev->ctrls[c]->priv = (void *)ctrl; ++ } ++ ++ if (hdl->error) { ++ pr_err("error adding control %d/%d id 0x%x\n", c, ++ V4L2_CTRL_COUNT, ctrl->id); ++ return hdl->error; ++ } ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ v4l2_ctrl_auto_cluster(ctrl->min, ++ &dev->ctrls[c+1], ++ ctrl->max, ++ ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD: ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ break; ++ } ++ ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h +new file mode 100644 +index 0000000..35698c8 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-common.h +@@ -0,0 +1,52 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL structures ++ * ++ */ ++ ++#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) ++#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') ++ ++/** Special value signalling that time is not known */ ++#define MMAL_TIME_UNKNOWN (1LL<<63) ++ ++/* mapping between v4l and mmal video modes */ ++struct mmal_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ int flags; /* v4l2 flags field */ ++ u32 mmal; ++ int depth; ++ u32 mmal_component; /* MMAL component index to be used to encode */ ++}; ++ ++/* buffer for one video frame */ ++struct mmal_buffer { ++ /* v4l buffer data -- must be first */ ++ struct vb2_buffer vb; ++ ++ /* list of buffers available */ ++ struct list_head list; ++ ++ void *buffer; /* buffer pointer */ ++ unsigned long buffer_size; /* size of allocated buffer */ ++}; ++ ++/* */ ++struct mmal_colourfx { ++ s32 enable; ++ u32 u; ++ u32 v; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h +new file mode 100644 +index 0000000..024d620 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-encodings.h +@@ -0,0 +1,127 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++#ifndef MMAL_ENCODINGS_H ++#define MMAL_ENCODINGS_H ++ ++#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') ++#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') ++#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') ++#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') ++#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') ++#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') ++#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') ++#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') ++#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') ++#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') ++#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') ++#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') ++#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') ++#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') ++#define MMAL_ENCODING_MJPEG MMAL_FOURCC('M', 'J', 'P', 'G') ++ ++#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') ++#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') ++#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') ++#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') ++#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') ++#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') ++ ++#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') ++#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') ++#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') ++#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') ++#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') ++#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') ++#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') ++#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') ++#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') ++#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') ++#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') ++#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') ++#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') ++#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') ++#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') ++#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') ++#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') ++#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') ++#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') ++#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') ++#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') ++ ++/** SAND Video (YUVUV128) format, native format understood by VideoCore. ++ * This format is *not* opaque - if requested you will receive full frames ++ * of YUV_UV video. ++ */ ++#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') ++ ++/** VideoCore opaque image format, image handles are returned to ++ * the host but not the actual image data. ++ */ ++#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') ++ ++/** An EGL image handle ++ */ ++#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') ++ ++/* }@ */ ++ ++/** \name Pre-defined audio encodings */ ++/* @{ */ ++#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') ++#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') ++#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') ++#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') ++#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') ++#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') ++ ++/* Pre-defined H264 encoding variants */ ++ ++/** ISO 14496-10 Annex B byte stream format */ ++#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 ++/** ISO 14496-15 AVC stream format */ ++#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') ++/** Implicitly delineated NAL units without emulation prevention */ ++#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') ++ ++ ++/** \defgroup MmalColorSpace List of pre-defined video color spaces ++ * This defines a list of common color spaces. This list isn't exhaustive and ++ * is only provided as a convenience to avoid clients having to use FourCC ++ * codes directly. However components are allowed to define and use their own ++ * FourCC codes. ++ */ ++/* @{ */ ++ ++/** Unknown color space */ ++#define MMAL_COLOR_SPACE_UNKNOWN 0 ++/** ITU-R BT.601-5 [SDTV] */ ++#define MMAL_COLOR_SPACE_ITUR_BT601 MMAL_FOURCC('Y', '6', '0', '1') ++/** ITU-R BT.709-3 [HDTV] */ ++#define MMAL_COLOR_SPACE_ITUR_BT709 MMAL_FOURCC('Y', '7', '0', '9') ++/** JPEG JFIF */ ++#define MMAL_COLOR_SPACE_JPEG_JFIF MMAL_FOURCC('Y', 'J', 'F', 'I') ++/** Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */ ++#define MMAL_COLOR_SPACE_FCC MMAL_FOURCC('Y', 'F', 'C', 'C') ++/** Society of Motion Picture and Television Engineers 240M (1999) */ ++#define MMAL_COLOR_SPACE_SMPTE240M MMAL_FOURCC('Y', '2', '4', '0') ++/** ITU-R BT.470-2 System M */ ++#define MMAL_COLOR_SPACE_BT470_2_M MMAL_FOURCC('Y', '_', '_', 'M') ++/** ITU-R BT.470-2 System BG */ ++#define MMAL_COLOR_SPACE_BT470_2_BG MMAL_FOURCC('Y', '_', 'B', 'G') ++/** JPEG JFIF, but with 16..255 luma */ ++#define MMAL_COLOR_SPACE_JFIF_Y16_255 MMAL_FOURCC('Y', 'Y', '1', '6') ++/* @} MmalColorSpace List */ ++ ++#endif /* MMAL_ENCODINGS_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h +new file mode 100644 +index 0000000..66e8a6e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-common.h +@@ -0,0 +1,50 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_COMMON_H ++#define MMAL_MSG_COMMON_H ++ ++enum mmal_msg_status { ++ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ ++ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ ++ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ ++ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ ++ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ ++ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ ++ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ ++ MMAL_MSG_STATUS_EIO, /**< I/O error */ ++ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ ++ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ ++ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ ++ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ ++ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ ++ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ ++ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ ++ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ ++}; ++ ++struct mmal_rect { ++ s32 x; /**< x coordinate (from left) */ ++ s32 y; /**< y coordinate (from top) */ ++ s32 width; /**< width */ ++ s32 height; /**< height */ ++}; ++ ++struct mmal_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++#endif /* MMAL_MSG_COMMON_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h +new file mode 100644 +index 0000000..123d86e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-format.h +@@ -0,0 +1,81 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_FORMAT_H ++#define MMAL_MSG_FORMAT_H ++ ++#include "mmal-msg-common.h" ++ ++/* MMAL_ES_FORMAT_T */ ++ ++ ++struct mmal_audio_format { ++ u32 channels; /**< Number of audio channels */ ++ u32 sample_rate; /**< Sample rate */ ++ ++ u32 bits_per_sample; /**< Bits per sample */ ++ u32 block_align; /**< Size of a block of data */ ++}; ++ ++struct mmal_video_format { ++ u32 width; /**< Width of frame in pixels */ ++ u32 height; /**< Height of frame in rows of pixels */ ++ struct mmal_rect crop; /**< Visible region of the frame */ ++ struct mmal_rational frame_rate; /**< Frame rate */ ++ struct mmal_rational par; /**< Pixel aspect ratio */ ++ ++ /* FourCC specifying the color space of the video stream. See the ++ * \ref MmalColorSpace "pre-defined color spaces" for some examples. ++ */ ++ u32 color_space; ++}; ++ ++struct mmal_subpicture_format { ++ u32 x_offset; ++ u32 y_offset; ++}; ++ ++union mmal_es_specific_format { ++ struct mmal_audio_format audio; ++ struct mmal_video_format video; ++ struct mmal_subpicture_format subpicture; ++}; ++ ++/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ ++struct mmal_es_format { ++ u32 type; /* enum mmal_es_type */ ++ ++ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ ++ u32 encoding_variant; /* FourCC specifying the specific ++ * encoding variant of the elementary ++ * stream. ++ */ ++ ++ union mmal_es_specific_format *es; /* TODO: pointers in ++ * message serialisation?!? ++ */ ++ /* Type specific ++ * information for the ++ * elementary stream ++ */ ++ ++ u32 bitrate; /**< Bitrate in bits per second */ ++ u32 flags; /**< Flags describing properties of the elementary stream. */ ++ ++ u32 extradata_size; /**< Size of the codec specific data */ ++ u8 *extradata; /**< Codec specific data */ ++}; ++ ++#endif /* MMAL_MSG_FORMAT_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h +new file mode 100644 +index 0000000..a55c1ea +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-port.h +@@ -0,0 +1,107 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* MMAL_PORT_TYPE_T */ ++enum mmal_port_type { ++ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ ++ MMAL_PORT_TYPE_CONTROL, /**< Control port */ ++ MMAL_PORT_TYPE_INPUT, /**< Input port */ ++ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ ++ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ ++}; ++ ++/** The port is pass-through and doesn't need buffer headers allocated */ ++#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 ++/** The port wants to allocate the buffer payloads. ++ * This signals a preference that payload allocation should be done ++ * on this port for efficiency reasons. */ ++#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 ++/** The port supports format change events. ++ * This applies to input ports and is used to let the client know ++ * whether the port supports being reconfigured via a format ++ * change event (i.e. without having to disable the port). */ ++#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 ++ ++/* mmal port structure (MMAL_PORT_T) ++ * ++ * most elements are informational only, the pointer values for ++ * interogation messages are generally provided as additional ++ * strucures within the message. When used to set values only teh ++ * buffer_num, buffer_size and userdata parameters are writable. ++ */ ++struct mmal_port { ++ void *priv; /* Private member used by the framework */ ++ const char *name; /* Port name. Used for debugging purposes (RO) */ ++ ++ u32 type; /* Type of the port (RO) enum mmal_port_type */ ++ u16 index; /* Index of the port in its type list (RO) */ ++ u16 index_all; /* Index of the port in the list of all ports (RO) */ ++ ++ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ ++ struct mmal_es_format *format; /* Format of the elementary stream */ ++ ++ u32 buffer_num_min; /* Minimum number of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_size_min; /* Minimum size of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_alignment_min; /* Minimum alignment requirement for ++ * the buffers (RO). A value of ++ * zero means no special alignment ++ * requirements. This is set by the ++ * component. ++ */ ++ ++ u32 buffer_num_recommended; /* Number of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_size_recommended; /* Size of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_num; /* Actual number of buffers the port will use. ++ * This is set by the client. ++ */ ++ ++ u32 buffer_size; /* Actual maximum size of the buffers that ++ * will be sent to the port. This is set by ++ * the client. ++ */ ++ ++ void *component; /* Component this port belongs to (Read Only) */ ++ ++ void *userdata; /* Field reserved for use by the client */ ++ ++ u32 capabilities; /* Flags describing the capabilities of a ++ * port (RO). Bitwise combination of \ref ++ * portcapabilities "Port capabilities" ++ * values. ++ */ ++ ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h +new file mode 100644 +index 0000000..67b1076 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg.h +@@ -0,0 +1,404 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* all the data structures which serialise the MMAL protocol. note ++ * these are directly mapped onto the recived message data. ++ * ++ * BEWARE: They seem to *assume* pointers are u32 and that there is no ++ * structure padding! ++ * ++ * NOTE: this implementation uses kernel types to ensure sizes. Rather ++ * than assigning values to enums to force their size the ++ * implementation uses fixed size types and not the enums (though the ++ * comments have the actual enum type ++ */ ++ ++#define VC_MMAL_VER 15 ++#define VC_MMAL_MIN_VER 10 ++#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") ++ ++/* max total message size is 512 bytes */ ++#define MMAL_MSG_MAX_SIZE 512 ++/* with six 32bit header elements max payload is therefore 488 bytes */ ++#define MMAL_MSG_MAX_PAYLOAD 488 ++ ++#include "mmal-msg-common.h" ++#include "mmal-msg-format.h" ++#include "mmal-msg-port.h" ++ ++enum mmal_msg_type { ++ MMAL_MSG_TYPE_QUIT = 1, ++ MMAL_MSG_TYPE_SERVICE_CLOSED, ++ MMAL_MSG_TYPE_GET_VERSION, ++ MMAL_MSG_TYPE_COMPONENT_CREATE, ++ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ ++ MMAL_MSG_TYPE_COMPONENT_ENABLE, ++ MMAL_MSG_TYPE_COMPONENT_DISABLE, ++ MMAL_MSG_TYPE_PORT_INFO_GET, ++ MMAL_MSG_TYPE_PORT_INFO_SET, ++ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST, ++ MMAL_MSG_TYPE_BUFFER_TO_HOST, ++ MMAL_MSG_TYPE_GET_STATS, ++ MMAL_MSG_TYPE_PORT_PARAMETER_SET, ++ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ ++ MMAL_MSG_TYPE_EVENT_TO_HOST, ++ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, ++ MMAL_MSG_TYPE_CONSUME_MEM, ++ MMAL_MSG_TYPE_LMK, /* 20 */ ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, ++ MMAL_MSG_TYPE_DRM_GET_LHS32, ++ MMAL_MSG_TYPE_DRM_GET_TIME, ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, ++ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ ++ MMAL_MSG_TYPE_HOST_LOG, ++ MMAL_MSG_TYPE_MSG_LAST ++}; ++ ++/* port action request messages differ depending on the action type */ ++enum mmal_msg_port_action_type { ++ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ ++}; ++ ++struct mmal_msg_header { ++ u32 magic; ++ u32 type; /** enum mmal_msg_type */ ++ ++ /* Opaque handle to the control service */ ++ struct mmal_control_service *control_service; ++ ++ struct mmal_msg_context *context; /** a u32 per message context */ ++ u32 status; /** The status of the vchiq operation */ ++ u32 padding; ++}; ++ ++/* Send from VC to host to report version */ ++struct mmal_msg_version { ++ u32 flags; ++ u32 major; ++ u32 minor; ++ u32 minimum; ++}; ++ ++/* request to VC to create component */ ++struct mmal_msg_component_create { ++ void *client_component; /* component context */ ++ char name[128]; ++ u32 pid; /* For debug */ ++}; ++ ++/* reply from VC to component creation request */ ++struct mmal_msg_component_create_reply { ++ u32 status; /** enum mmal_msg_status - how does this differ to ++ * the one in the header? ++ */ ++ u32 component_handle; /* VideoCore handle for component */ ++ u32 input_num; /* Number of input ports */ ++ u32 output_num; /* Number of output ports */ ++ u32 clock_num; /* Number of clock ports */ ++}; ++ ++/* request to VC to destroy a component */ ++struct mmal_msg_component_destroy { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_destroy_reply { ++ u32 status; /** The component destruction status */ ++}; ++ ++ ++/* request and reply to VC to enable a component */ ++struct mmal_msg_component_enable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_enable_reply { ++ u32 status; /** The component enable status */ ++}; ++ ++ ++/* request and reply to VC to disable a component */ ++struct mmal_msg_component_disable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_disable_reply { ++ u32 status; /** The component disable status */ ++}; ++ ++/* request to VC to get port information */ ++struct mmal_msg_port_info_get { ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port index to query */ ++}; ++ ++/* reply from VC to get port info request */ ++struct mmal_msg_port_info_get_reply { ++ u32 status; /** enum mmal_msg_status */ ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; /* elementry stream format */ ++ union mmal_es_specific_format es; /* es type specific data */ ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ ++}; ++ ++/* request to VC to set port information */ ++struct mmal_msg_port_info_set { ++ u32 component_handle; ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++/* reply from VC to port info set request */ ++struct mmal_msg_port_info_set_reply { ++ u32 status; ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++ ++/* port action requests that take a mmal_port as a parameter */ ++struct mmal_msg_port_action_port { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ struct mmal_port port; ++}; ++ ++/* port action requests that take handles as a parameter */ ++struct mmal_msg_port_action_handle { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ u32 connect_component_handle; ++ u32 connect_port_handle; ++}; ++ ++struct mmal_msg_port_action_reply { ++ u32 status; /** The port action operation status */ ++}; ++ ++ ++ ++ ++/* MMAL buffer transfer */ ++ ++/** Size of space reserved in a buffer message for short messages. */ ++#define MMAL_VC_SHORT_DATA 128 ++ ++/** Signals that the current payload is the end of the stream of data */ ++#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) ++/** Signals that the start of the current payload starts a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) ++/** Signals that the end of the current payload ends a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) ++/** Signals that the current payload contains only complete frames (>1) */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME \ ++ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) ++/** Signals that the current payload is a keyframe (i.e. self decodable) */ ++#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) ++/** Signals a discontinuity in the stream of data (e.g. after a seek). ++ * Can be used for instance by a decoder to reset its state */ ++#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) ++/** Signals a buffer containing some kind of config data for the component ++ * (e.g. codec config data) */ ++#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) ++/** Signals an encrypted payload */ ++#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) ++/** Signals a buffer containing side information */ ++#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) ++/** Signals a buffer which is the snapshot/postview image from a stills ++ * capture ++ */ ++#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) ++/** Signals a buffer which contains data known to be corrupted */ ++#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) ++/** Signals that a buffer failed to be transmitted */ ++#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) ++ ++struct mmal_driver_buffer { ++ u32 magic; ++ u32 component_handle; ++ u32 port_handle; ++ void *client_context; ++}; ++ ++/* buffer header */ ++struct mmal_buffer_header { ++ struct mmal_buffer_header *next; /* next header */ ++ void *priv; /* framework private data */ ++ u32 cmd; ++ void *data; ++ u32 alloc_size; ++ u32 length; ++ u32 offset; ++ u32 flags; ++ s64 pts; ++ s64 dts; ++ void *type; ++ void *user_data; ++}; ++ ++struct mmal_buffer_header_type_specific { ++ union { ++ struct { ++ u32 planes; ++ u32 offset[4]; ++ u32 pitch[4]; ++ u32 flags; ++ } video; ++ } u; ++}; ++ ++struct mmal_msg_buffer_from_host { ++ /* The front 32 bytes of the buffer header are copied ++ * back to us in the reply to allow for context. This ++ * area is used to store two mmal_driver_buffer structures to ++ * allow for multiple concurrent service users. ++ */ ++ /* control data */ ++ struct mmal_driver_buffer drvbuf; ++ ++ /* referenced control data for passthrough buffer management */ ++ struct mmal_driver_buffer drvbuf_ref; ++ struct mmal_buffer_header buffer_header; /* buffer header itself */ ++ struct mmal_buffer_header_type_specific buffer_header_type_specific; ++ s32 is_zero_copy; ++ s32 has_reference; ++ ++ /** allows short data to be xfered in control message */ ++ u32 payload_in_message; ++ u8 short_data[MMAL_VC_SHORT_DATA]; ++}; ++ ++ ++/* port parameter setting */ ++ ++#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 ++ ++struct mmal_msg_port_parameter_set { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++struct mmal_msg_port_parameter_set_reply { ++ u32 status; /** enum mmal_msg_status todo: how does this ++ * differ to the one in the header? ++ */ ++}; ++ ++/* port parameter getting */ ++ ++struct mmal_msg_port_parameter_get { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++}; ++ ++struct mmal_msg_port_parameter_get_reply { ++ u32 status; /* Status of mmal_port_parameter_get call */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++/* event messages */ ++#define MMAL_WORKER_EVENT_SPACE 256 ++ ++struct mmal_msg_event_to_host { ++ void *client_component; /* component context */ ++ ++ u32 port_type; ++ u32 port_num; ++ ++ u32 cmd; ++ u32 length; ++ u8 data[MMAL_WORKER_EVENT_SPACE]; ++ struct mmal_buffer_header *delayed_buffer; ++}; ++ ++/* all mmal messages are serialised through this structure */ ++struct mmal_msg { ++ /* header */ ++ struct mmal_msg_header h; ++ /* payload */ ++ union { ++ struct mmal_msg_version version; ++ ++ struct mmal_msg_component_create component_create; ++ struct mmal_msg_component_create_reply component_create_reply; ++ ++ struct mmal_msg_component_destroy component_destroy; ++ struct mmal_msg_component_destroy_reply component_destroy_reply; ++ ++ struct mmal_msg_component_enable component_enable; ++ struct mmal_msg_component_enable_reply component_enable_reply; ++ ++ struct mmal_msg_component_disable component_disable; ++ struct mmal_msg_component_disable_reply component_disable_reply; ++ ++ struct mmal_msg_port_info_get port_info_get; ++ struct mmal_msg_port_info_get_reply port_info_get_reply; ++ ++ struct mmal_msg_port_info_set port_info_set; ++ struct mmal_msg_port_info_set_reply port_info_set_reply; ++ ++ struct mmal_msg_port_action_port port_action_port; ++ struct mmal_msg_port_action_handle port_action_handle; ++ struct mmal_msg_port_action_reply port_action_reply; ++ ++ struct mmal_msg_buffer_from_host buffer_from_host; ++ ++ struct mmal_msg_port_parameter_set port_parameter_set; ++ struct mmal_msg_port_parameter_set_reply ++ port_parameter_set_reply; ++ struct mmal_msg_port_parameter_get ++ port_parameter_get; ++ struct mmal_msg_port_parameter_get_reply ++ port_parameter_get_reply; ++ ++ struct mmal_msg_event_to_host event_to_host; ++ ++ u8 payload[MMAL_MSG_MAX_PAYLOAD]; ++ } u; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +new file mode 100644 +index 0000000..aa0fd18 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -0,0 +1,656 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* common parameters */ ++ ++/** @name Parameter groups ++ * Parameters are divided into groups, and then allocated sequentially within ++ * a group using an enum. ++ * @{ ++ */ ++ ++/** Common parameter ID group, used with many types of component. */ ++#define MMAL_PARAMETER_GROUP_COMMON (0<<16) ++/** Camera-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) ++/** Video-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) ++/** Audio-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) ++/** Clock-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) ++/** Miracast-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) ++ ++/* Common parameters */ ++enum mmal_parameter_common_type { ++ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ ++ = MMAL_PARAMETER_GROUP_COMMON, ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ ++ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ ++ ++ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ ++ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, ++ ++ /** MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_COPY, ++ ++ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ ++ MMAL_PARAMETER_BUFFER_REQUIREMENTS, ++ ++ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ ++ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ ++ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ ++ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ ++ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ ++ MMAL_PARAMETER_SYSTEM_TIME, /**< MMAL_PARAMETER_UINT64_T */ ++ MMAL_PARAMETER_NO_IMAGE_PADDING /**< MMAL_PARAMETER_BOOLEAN_T */ ++}; ++ ++/* camera parameters */ ++ ++enum mmal_parameter_camera_type { ++ /* 0 */ ++ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ ++ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION ++ = MMAL_PARAMETER_GROUP_CAMERA, ++ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ ++ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ ++ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ ++ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ ++ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ ++ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ ++ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ ++ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ ++ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ ++ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ ++ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ ++ ++ /* 0x10 */ ++ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ ++ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ ++ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ ++ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ ++ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ ++ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ ++ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ ++ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ ++ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ ++ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /* 0x20 */ ++ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ ++ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ ++ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ ++ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ ++ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ ++ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ ++ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ ++ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ ++ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ ++ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ ++ /* 0x30 */ ++ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_MIN_ISO, ++ ++ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ ++ MMAL_PARAMETER_CAMERA_USE_CASE, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_STATS_PASS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ENABLE_REGISTER_FILE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ ++ MMAL_PARAMETER_CONFIGFILE_REGISTERS, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ ++ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, ++ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ ++ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ ++ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ ++ /* 0x40 */ ++ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SHUTTER_SPEED, /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ ++}; ++ ++struct mmal_parameter_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++enum mmal_parameter_camera_config_timestamp_mode { ++ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ ++ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value ++ * for the frame timestamp ++ */ ++ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp ++ * but subtract the ++ * timestamp of the first ++ * frame sent to give a ++ * zero based timestamp. ++ */ ++}; ++ ++struct mmal_parameter_fps_range { ++ /**< Low end of the permitted framerate range */ ++ struct mmal_parameter_rational fps_low; ++ /**< High end of the permitted framerate range */ ++ struct mmal_parameter_rational fps_high; ++}; ++ ++ ++/* camera configuration parameter */ ++struct mmal_parameter_camera_config { ++ /* Parameters for setting up the image pools */ ++ u32 max_stills_w; /* Max size of stills capture */ ++ u32 max_stills_h; ++ u32 stills_yuv422; /* Allow YUV422 stills capture */ ++ u32 one_shot_stills; /* Continuous or one shot stills captures. */ ++ ++ u32 max_preview_video_w; /* Max size of the preview or video ++ * capture frames ++ */ ++ u32 max_preview_video_h; ++ u32 num_preview_video_frames; ++ ++ /** Sets the height of the circular buffer for stills capture. */ ++ u32 stills_capture_circular_buffer_height; ++ ++ /** Allows preview/encode to resume as fast as possible after the stills ++ * input frame has been received, and then processes the still frame in ++ * the background whilst preview/encode has resumed. ++ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. ++ */ ++ u32 fast_preview_resume; ++ ++ /** Selects algorithm for timestamping frames if ++ * there is no clock component connected. ++ * enum mmal_parameter_camera_config_timestamp_mode ++ */ ++ s32 use_stc_timestamp; ++}; ++ ++ ++enum mmal_parameter_exposuremode { ++ MMAL_PARAM_EXPOSUREMODE_OFF, ++ MMAL_PARAM_EXPOSUREMODE_AUTO, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, ++ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SNOW, ++ MMAL_PARAM_EXPOSUREMODE_BEACH, ++ MMAL_PARAM_EXPOSUREMODE_VERYLONG, ++ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, ++ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, ++ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, ++}; ++ ++enum mmal_parameter_exposuremeteringmode { ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, ++}; ++ ++enum mmal_parameter_awbmode { ++ MMAL_PARAM_AWBMODE_OFF, ++ MMAL_PARAM_AWBMODE_AUTO, ++ MMAL_PARAM_AWBMODE_SUNLIGHT, ++ MMAL_PARAM_AWBMODE_CLOUDY, ++ MMAL_PARAM_AWBMODE_SHADE, ++ MMAL_PARAM_AWBMODE_TUNGSTEN, ++ MMAL_PARAM_AWBMODE_FLUORESCENT, ++ MMAL_PARAM_AWBMODE_INCANDESCENT, ++ MMAL_PARAM_AWBMODE_FLASH, ++ MMAL_PARAM_AWBMODE_HORIZON, ++}; ++ ++enum mmal_parameter_imagefx { ++ MMAL_PARAM_IMAGEFX_NONE, ++ MMAL_PARAM_IMAGEFX_NEGATIVE, ++ MMAL_PARAM_IMAGEFX_SOLARIZE, ++ MMAL_PARAM_IMAGEFX_POSTERIZE, ++ MMAL_PARAM_IMAGEFX_WHITEBOARD, ++ MMAL_PARAM_IMAGEFX_BLACKBOARD, ++ MMAL_PARAM_IMAGEFX_SKETCH, ++ MMAL_PARAM_IMAGEFX_DENOISE, ++ MMAL_PARAM_IMAGEFX_EMBOSS, ++ MMAL_PARAM_IMAGEFX_OILPAINT, ++ MMAL_PARAM_IMAGEFX_HATCH, ++ MMAL_PARAM_IMAGEFX_GPEN, ++ MMAL_PARAM_IMAGEFX_PASTEL, ++ MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ MMAL_PARAM_IMAGEFX_FILM, ++ MMAL_PARAM_IMAGEFX_BLUR, ++ MMAL_PARAM_IMAGEFX_SATURATION, ++ MMAL_PARAM_IMAGEFX_COLOURSWAP, ++ MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ MMAL_PARAM_IMAGEFX_POSTERISE, ++ MMAL_PARAM_IMAGEFX_COLOURPOINT, ++ MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ MMAL_PARAM_IMAGEFX_CARTOON, ++}; ++ ++enum MMAL_PARAM_FLICKERAVOID_T { ++ MMAL_PARAM_FLICKERAVOID_OFF, ++ MMAL_PARAM_FLICKERAVOID_AUTO, ++ MMAL_PARAM_FLICKERAVOID_50HZ, ++ MMAL_PARAM_FLICKERAVOID_60HZ, ++ MMAL_PARAM_FLICKERAVOID_MAX = 0x7FFFFFFF ++}; ++ ++struct mmal_parameter_awbgains { ++ struct mmal_parameter_rational r_gain; /**< Red gain */ ++ struct mmal_parameter_rational b_gain; /**< Blue gain */ ++}; ++ ++/** Manner of video rate control */ ++enum mmal_parameter_rate_control_mode { ++ MMAL_VIDEO_RATECONTROL_DEFAULT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE, ++ MMAL_VIDEO_RATECONTROL_CONSTANT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, ++ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES ++}; ++ ++enum mmal_video_profile { ++ MMAL_VIDEO_PROFILE_H263_BASELINE, ++ MMAL_VIDEO_PROFILE_H263_H320CODING, ++ MMAL_VIDEO_PROFILE_H263_BACKWARDCOMPATIBLE, ++ MMAL_VIDEO_PROFILE_H263_ISWV2, ++ MMAL_VIDEO_PROFILE_H263_ISWV3, ++ MMAL_VIDEO_PROFILE_H263_HIGHCOMPRESSION, ++ MMAL_VIDEO_PROFILE_H263_INTERNET, ++ MMAL_VIDEO_PROFILE_H263_INTERLACE, ++ MMAL_VIDEO_PROFILE_H263_HIGHLATENCY, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLESCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_CORE, ++ MMAL_VIDEO_PROFILE_MP4V_MAIN, ++ MMAL_VIDEO_PROFILE_MP4V_NBIT, ++ MMAL_VIDEO_PROFILE_MP4V_SCALABLETEXTURE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFACE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFBA, ++ MMAL_VIDEO_PROFILE_MP4V_BASICANIMATED, ++ MMAL_VIDEO_PROFILE_MP4V_HYBRID, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDREALTIME, ++ MMAL_VIDEO_PROFILE_MP4V_CORESCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCODING, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCORE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSIMPLE, ++ MMAL_VIDEO_PROFILE_H264_BASELINE, ++ MMAL_VIDEO_PROFILE_H264_MAIN, ++ MMAL_VIDEO_PROFILE_H264_EXTENDED, ++ MMAL_VIDEO_PROFILE_H264_HIGH, ++ MMAL_VIDEO_PROFILE_H264_HIGH10, ++ MMAL_VIDEO_PROFILE_H264_HIGH422, ++ MMAL_VIDEO_PROFILE_H264_HIGH444, ++ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, ++ MMAL_VIDEO_PROFILE_DUMMY = 0x7FFFFFFF ++}; ++ ++enum mmal_video_level { ++ MMAL_VIDEO_LEVEL_H263_10, ++ MMAL_VIDEO_LEVEL_H263_20, ++ MMAL_VIDEO_LEVEL_H263_30, ++ MMAL_VIDEO_LEVEL_H263_40, ++ MMAL_VIDEO_LEVEL_H263_45, ++ MMAL_VIDEO_LEVEL_H263_50, ++ MMAL_VIDEO_LEVEL_H263_60, ++ MMAL_VIDEO_LEVEL_H263_70, ++ MMAL_VIDEO_LEVEL_MP4V_0, ++ MMAL_VIDEO_LEVEL_MP4V_0b, ++ MMAL_VIDEO_LEVEL_MP4V_1, ++ MMAL_VIDEO_LEVEL_MP4V_2, ++ MMAL_VIDEO_LEVEL_MP4V_3, ++ MMAL_VIDEO_LEVEL_MP4V_4, ++ MMAL_VIDEO_LEVEL_MP4V_4a, ++ MMAL_VIDEO_LEVEL_MP4V_5, ++ MMAL_VIDEO_LEVEL_MP4V_6, ++ MMAL_VIDEO_LEVEL_H264_1, ++ MMAL_VIDEO_LEVEL_H264_1b, ++ MMAL_VIDEO_LEVEL_H264_11, ++ MMAL_VIDEO_LEVEL_H264_12, ++ MMAL_VIDEO_LEVEL_H264_13, ++ MMAL_VIDEO_LEVEL_H264_2, ++ MMAL_VIDEO_LEVEL_H264_21, ++ MMAL_VIDEO_LEVEL_H264_22, ++ MMAL_VIDEO_LEVEL_H264_3, ++ MMAL_VIDEO_LEVEL_H264_31, ++ MMAL_VIDEO_LEVEL_H264_32, ++ MMAL_VIDEO_LEVEL_H264_4, ++ MMAL_VIDEO_LEVEL_H264_41, ++ MMAL_VIDEO_LEVEL_H264_42, ++ MMAL_VIDEO_LEVEL_H264_5, ++ MMAL_VIDEO_LEVEL_H264_51, ++ MMAL_VIDEO_LEVEL_DUMMY = 0x7FFFFFFF ++}; ++ ++struct mmal_parameter_video_profile { ++ enum mmal_video_profile profile; ++ enum mmal_video_level level; ++}; ++ ++/* video parameters */ ++ ++enum mmal_parameter_video_type { ++ /** @ref MMAL_DISPLAYREGION_T */ ++ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_SUPPORTED_PROFILES, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_PROFILE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_INTRAPERIOD, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ ++ MMAL_PARAMETER_RATECONTROL, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ ++ MMAL_PARAMETER_NALUNITFORMAT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Setting the value to zero resets to the default (one slice per frame). ++ */ ++ MMAL_PARAMETER_MB_ROWS_PER_SLICE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ ++ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ ++ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, ++ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ ++ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ ++ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, ++ ++ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_VERT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, ++ ++ /* H264 specific parameters */ ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, ++ ++ /** @ref MMAL_PARAMETER_BYTES_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_VCL_HRD_PARAMETERS, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER ++}; ++ ++/** Valid mirror modes */ ++enum mmal_parameter_mirror { ++ MMAL_PARAM_MIRROR_NONE, ++ MMAL_PARAM_MIRROR_VERTICAL, ++ MMAL_PARAM_MIRROR_HORIZONTAL, ++ MMAL_PARAM_MIRROR_BOTH, ++}; ++ ++enum mmal_parameter_displaytransform { ++ MMAL_DISPLAY_ROT0 = 0, ++ MMAL_DISPLAY_MIRROR_ROT0 = 1, ++ MMAL_DISPLAY_MIRROR_ROT180 = 2, ++ MMAL_DISPLAY_ROT180 = 3, ++ MMAL_DISPLAY_MIRROR_ROT90 = 4, ++ MMAL_DISPLAY_ROT270 = 5, ++ MMAL_DISPLAY_ROT90 = 6, ++ MMAL_DISPLAY_MIRROR_ROT270 = 7, ++}; ++ ++enum mmal_parameter_displaymode { ++ MMAL_DISPLAY_MODE_FILL = 0, ++ MMAL_DISPLAY_MODE_LETTERBOX = 1, ++}; ++ ++enum mmal_parameter_displayset { ++ MMAL_DISPLAY_SET_NONE = 0, ++ MMAL_DISPLAY_SET_NUM = 1, ++ MMAL_DISPLAY_SET_FULLSCREEN = 2, ++ MMAL_DISPLAY_SET_TRANSFORM = 4, ++ MMAL_DISPLAY_SET_DEST_RECT = 8, ++ MMAL_DISPLAY_SET_SRC_RECT = 0x10, ++ MMAL_DISPLAY_SET_MODE = 0x20, ++ MMAL_DISPLAY_SET_PIXEL = 0x40, ++ MMAL_DISPLAY_SET_NOASPECT = 0x80, ++ MMAL_DISPLAY_SET_LAYER = 0x100, ++ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, ++ MMAL_DISPLAY_SET_ALPHA = 0x400, ++}; ++ ++struct mmal_parameter_displayregion { ++ /** Bitfield that indicates which fields are set and should be ++ * used. All other fields will maintain their current value. ++ * \ref MMAL_DISPLAYSET_T defines the bits that can be ++ * combined. ++ */ ++ u32 set; ++ ++ /** Describes the display output device, with 0 typically ++ * being a directly connected LCD display. The actual values ++ * will depend on the hardware. Code using hard-wired numbers ++ * (e.g. 2) is certain to fail. ++ */ ++ ++ u32 display_num; ++ /** Indicates that we are using the full device screen area, ++ * rather than a window of the display. If zero, then ++ * dest_rect is used to specify a region of the display to ++ * use. ++ */ ++ ++ s32 fullscreen; ++ /** Indicates any rotation or flipping used to map frames onto ++ * the natural display orientation. ++ */ ++ u32 transform; /* enum mmal_parameter_displaytransform */ ++ ++ /** Where to display the frame within the screen, if ++ * fullscreen is zero. ++ */ ++ struct vchiq_mmal_rect dest_rect; ++ ++ /** Indicates which area of the frame to display. If all ++ * values are zero, the whole frame will be used. ++ */ ++ struct vchiq_mmal_rect src_rect; ++ ++ /** If set to non-zero, indicates that any display scaling ++ * should disregard the aspect ratio of the frame region being ++ * displayed. ++ */ ++ s32 noaspect; ++ ++ /** Indicates how the image should be scaled to fit the ++ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates ++ * that the image should fill the screen by potentially ++ * cropping the frames. Setting \code mode \endcode to \code ++ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the ++ * source region should be displayed and black bars added if ++ * necessary. ++ */ ++ u32 mode; /* enum mmal_parameter_displaymode */ ++ ++ /** If non-zero, defines the width of a source pixel relative ++ * to \code pixel_y \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_x; ++ ++ /** If non-zero, defines the height of a source pixel relative ++ * to \code pixel_x \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_y; ++ ++ /** Sets the relative depth of the images, with greater values ++ * being in front of smaller values. ++ */ ++ u32 layer; ++ ++ /** Set to non-zero to ensure copy protection is used on ++ * output. ++ */ ++ s32 copyprotect_required; ++ ++ /** Level of opacity of the layer, where zero is fully ++ * transparent and 255 is fully opaque. ++ */ ++ u32 alpha; ++}; ++ ++#define MMAL_MAX_IMAGEFX_PARAMETERS 5 ++ ++struct mmal_parameter_imagefx_parameters { ++ enum mmal_parameter_imagefx effect; ++ u32 num_effect_params; ++ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c +new file mode 100644 +index 0000000..76f249e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.c +@@ -0,0 +1,1916 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * V4L2 driver MMAL vchiq interface code ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++ ++#define USE_VCHIQ_ARM ++#include "interface/vchi/vchi.h" ++ ++/* maximum number of components supported */ ++#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++ ++/*#define FULL_MSG_DUMP 1*/ ++ ++#ifdef DEBUG ++static const char *const msg_type_names[] = { ++ "UNKNOWN", ++ "QUIT", ++ "SERVICE_CLOSED", ++ "GET_VERSION", ++ "COMPONENT_CREATE", ++ "COMPONENT_DESTROY", ++ "COMPONENT_ENABLE", ++ "COMPONENT_DISABLE", ++ "PORT_INFO_GET", ++ "PORT_INFO_SET", ++ "PORT_ACTION", ++ "BUFFER_FROM_HOST", ++ "BUFFER_TO_HOST", ++ "GET_STATS", ++ "PORT_PARAMETER_SET", ++ "PORT_PARAMETER_GET", ++ "EVENT_TO_HOST", ++ "GET_CORE_STATS_FOR_PORT", ++ "OPAQUE_ALLOCATOR", ++ "CONSUME_MEM", ++ "LMK", ++ "OPAQUE_ALLOCATOR_DESC", ++ "DRM_GET_LHS32", ++ "DRM_GET_TIME", ++ "BUFFER_FROM_HOST_ZEROLEN", ++ "PORT_FLUSH", ++ "HOST_LOG", ++}; ++#endif ++ ++static const char *const port_action_type_names[] = { ++ "UNKNOWN", ++ "ENABLE", ++ "DISABLE", ++ "FLUSH", ++ "CONNECT", ++ "DISCONNECT", ++ "SET_REQUIREMENTS", ++}; ++ ++#if defined(DEBUG) ++#if defined(FULL_MSG_DUMP) ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ ++ do { \ ++ pr_debug(TITLE" type:%s(%d) length:%d\n", \ ++ msg_type_names[(MSG)->h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ print_hex_dump(KERN_DEBUG, "<h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ } ++#endif ++#else ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) ++#endif ++ ++/* normal message context */ ++struct mmal_msg_context { ++ union { ++ struct { ++ /* work struct for defered callback - must come first */ ++ struct work_struct work; ++ /* mmal instance */ ++ struct vchiq_mmal_instance *instance; ++ /* mmal port */ ++ struct vchiq_mmal_port *port; ++ /* actual buffer used to store bulk reply */ ++ struct mmal_buffer *buffer; ++ /* amount of buffer used */ ++ unsigned long buffer_used; ++ /* MMAL buffer flags */ ++ u32 mmal_flags; ++ /* Presentation and Decode timestamps */ ++ s64 pts; ++ s64 dts; ++ ++ int status; /* context status */ ++ ++ } bulk; /* bulk data */ ++ ++ struct { ++ /* message handle to release */ ++ VCHI_HELD_MSG_T msg_handle; ++ /* pointer to received message */ ++ struct mmal_msg *msg; ++ /* received message length */ ++ u32 msg_len; ++ /* completion upon reply */ ++ struct completion cmplt; ++ } sync; /* synchronous response */ ++ } u; ++ ++}; ++ ++struct vchiq_mmal_instance { ++ VCHI_SERVICE_HANDLE_T handle; ++ ++ /* ensure serialised access to service */ ++ struct mutex vchiq_mutex; ++ ++ /* ensure serialised access to bulk operations */ ++ struct mutex bulk_mutex; ++ ++ /* vmalloc page to receive scratch bulk xfers into */ ++ void *bulk_scratch; ++ ++ /* component to use next */ ++ int component_idx; ++ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; ++}; ++ ++static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance ++ *instance) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ /* todo: should this be allocated from a pool to avoid kmalloc */ ++ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); ++ memset(msg_context, 0, sizeof(*msg_context)); ++ ++ return msg_context; ++} ++ ++static void release_msg_context(struct mmal_msg_context *msg_context) ++{ ++ kfree(msg_context); ++} ++ ++/* deals with receipt of event to host message */ ++static void event_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ pr_debug("unhandled event\n"); ++ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", ++ msg->u.event_to_host.client_component, ++ msg->u.event_to_host.port_type, ++ msg->u.event_to_host.port_num, ++ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++} ++ ++/* workqueue scheduled callback ++ * ++ * we do this because it is important we do not call any other vchiq ++ * sync calls from witin the message delivery thread ++ */ ++static void buffer_work_cb(struct work_struct *work) ++{ ++ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; ++ ++ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port, ++ msg_context->u.bulk.status, ++ msg_context->u.bulk.buffer, ++ msg_context->u.bulk.buffer_used, ++ msg_context->u.bulk.mmal_flags, ++ msg_context->u.bulk.dts, ++ msg_context->u.bulk.pts); ++ ++ /* release message context */ ++ release_msg_context(msg_context); ++} ++ ++/* enqueue a bulk receive for a given message context */ ++static int bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long rd_len; ++ unsigned long flags = 0; ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ rd_len = msg->u.buffer_from_host.buffer_header.length; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to submit bulk receive\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (underflow ++ * handling) and there is no obvious way to deal with ++ * this - how is the mmal servie going to react when ++ * we fail to do the xfer and reschedule a buffer when ++ * it arrives? perhaps a starved flag to indicate a ++ * waiting bulk receive? ++ */ ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ /* ensure we do not overrun the available buffer */ ++ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { ++ rd_len = msg_context->u.bulk.buffer->buffer_size; ++ pr_warn("short read as not enough receive buffer space\n"); ++ /* todo: is this the correct response, what happens to ++ * the rest of the message data? ++ */ ++ } ++ ++ /* store length */ ++ msg_context->u.bulk.buffer_used = rd_len; ++ msg_context->u.bulk.mmal_flags = ++ msg->u.buffer_from_host.buffer_header.flags; ++ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; ++ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; ++ ++ // only need to flush L1 cache here, as VCHIQ takes care of the L2 ++ // cache. ++ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ret = vchi_bulk_queue_receive(instance->handle, ++ msg_context->u.bulk.buffer->buffer, ++ /* Actual receive needs to be a multiple ++ * of 4 bytes ++ */ ++ (rd_len + 3) & ~3, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* enque a dummy bulk receive for a given message context */ ++static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ /* zero length indicates this was a dummy transfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_bulk_queue_receive(instance->handle, ++ instance->bulk_scratch, ++ 8, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* data in message, memcpy from packet into output buffer */ ++static int inline_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long flags = 0; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to receive inline\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (with ++ * underflow handling) and there is no obvious way to ++ * deal with this. Less bad than the bulk case as we ++ * can just drop this on the floor but...unhelpful ++ */ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ memcpy(msg_context->u.bulk.buffer->buffer, ++ msg->u.buffer_from_host.short_data, ++ msg->u.buffer_from_host.payload_in_message); ++ ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ ++ return 0; ++} ++ ++/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ ++static int ++buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++{ ++ struct mmal_msg_context *msg_context; ++ struct mmal_msg m; ++ int ret; ++ ++ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); ++ ++ /* bulk mutex stops other bulk operations while we ++ * have a receive in progress ++ */ ++ if (mutex_lock_interruptible(&instance->bulk_mutex)) ++ return -EINTR; ++ ++ /* get context */ ++ msg_context = get_msg_context(instance); ++ if (msg_context == NULL) ++ return -ENOMEM; ++ ++ /* store bulk message context for when data arrives */ ++ msg_context->u.bulk.instance = instance; ++ msg_context->u.bulk.port = port; ++ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* initialise work structure ready to schedule callback */ ++ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); ++ ++ /* prep the buffer from host message */ ++ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ ++ ++ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; ++ m.h.magic = MMAL_MAGIC; ++ m.h.context = msg_context; ++ m.h.status = 0; ++ ++ /* drvbuf is our private data passed back */ ++ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; ++ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; ++ m.u.buffer_from_host.drvbuf.port_handle = port->handle; ++ m.u.buffer_from_host.drvbuf.client_context = msg_context; ++ ++ /* buffer header */ ++ m.u.buffer_from_host.buffer_header.cmd = 0; ++ m.u.buffer_from_host.buffer_header.data = buf->buffer; ++ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; ++ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ ++ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ ++ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ ++ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; ++ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; ++ ++ /* clear buffer type sepecific data */ ++ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, ++ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); ++ ++ /* no payload in message */ ++ m.u.buffer_from_host.payload_in_message = 0; ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, &m, ++ sizeof(struct mmal_msg_header) + ++ sizeof(m.u.buffer_from_host), ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (ret != 0) { ++ release_msg_context(msg_context); ++ /* todo: is this correct error value? */ ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return ret; ++} ++ ++/* submit a buffer to the mmal sevice ++ * ++ * the buffer_from_host uses size data from the ports next available ++ * mmal_buffer and deals with there being no buffer available by ++ * incrementing the underflow for later ++ */ ++static int port_buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_buffer *buf; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return -EINVAL; ++ ++ /* peek buffer from queue */ ++ spin_lock_irqsave(&port->slock, flags); ++ if (list_empty(&port->buffers)) { ++ port->buffer_underflow++; ++ spin_unlock_irqrestore(&port->slock, flags); ++ return -ENOSPC; ++ } ++ ++ buf = list_entry(port->buffers.next, struct mmal_buffer, list); ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* issue buffer to mmal service */ ++ ret = buffer_from_host(instance, port, buf); ++ if (ret) { ++ pr_err("adding buffer header failed\n"); ++ /* todo: how should this be dealt with */ ++ } ++ ++ return ret; ++} ++ ++/* deals with receipt of buffer to host message */ ++static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", ++ instance, msg, msg_len); ++ ++ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { ++ msg_context = msg->u.buffer_from_host.drvbuf.client_context; ++ } else { ++ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); ++ return; ++ } ++ ++ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { ++ /* message reception had an error */ ++ pr_warn("error %d in reply\n", msg->h.status); ++ ++ msg_context->u.bulk.status = msg->h.status; ++ ++ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { ++ /* empty buffer */ ++ if (msg->u.buffer_from_host.buffer_header.flags & ++ MMAL_BUFFER_HEADER_FLAG_EOS) { ++ msg_context->u.bulk.status = ++ dummy_bulk_receive(instance, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ } else { ++ /* do callback with empty buffer - not EOS though */ ++ msg_context->u.bulk.status = 0; ++ msg_context->u.bulk.buffer_used = 0; ++ } ++ } else if (msg->u.buffer_from_host.payload_in_message == 0) { ++ /* data is not in message, queue a bulk receive */ ++ msg_context->u.bulk.status = ++ bulk_receive(instance, msg, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ ++ /* failed to submit buffer, this will end badly */ ++ pr_err("error %d on bulk submission\n", ++ msg_context->u.bulk.status); ++ ++ } else if (msg->u.buffer_from_host.payload_in_message <= ++ MMAL_VC_SHORT_DATA) { ++ /* data payload within message */ ++ msg_context->u.bulk.status = inline_receive(instance, msg, ++ msg_context); ++ } else { ++ pr_err("message with invalid short payload\n"); ++ ++ /* signal error */ ++ msg_context->u.bulk.status = -EINVAL; ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ } ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(instance, msg_context->u.bulk.port); ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_receive_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = 0; ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_abort_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); ++ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = -EINTR; ++ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++/* incoming event service callback */ ++static void service_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *bulk_ctx) ++{ ++ struct vchiq_mmal_instance *instance = param; ++ int status; ++ u32 msg_len; ++ struct mmal_msg *msg; ++ VCHI_HELD_MSG_T msg_handle; ++ ++ if (!instance) { ++ pr_err("Message callback passed NULL instance\n"); ++ return; ++ } ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ status = vchi_msg_hold(instance->handle, (void **)&msg, ++ &msg_len, VCHI_FLAGS_NONE, &msg_handle); ++ if (status) { ++ pr_err("Unable to dequeue a message (%d)\n", status); ++ break; ++ } ++ ++ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ ++ /* handling is different for buffer messages */ ++ switch (msg->h.type) { ++ ++ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ case MMAL_MSG_TYPE_EVENT_TO_HOST: ++ event_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ ++ break; ++ ++ case MMAL_MSG_TYPE_BUFFER_TO_HOST: ++ buffer_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ default: ++ /* messages dependant on header context to complete */ ++ ++ /* todo: the msg.context really ought to be sanity ++ * checked before we just use it, afaict it comes back ++ * and is used raw from the videocore. Perhaps it ++ * should be verified the address lies in the kernel ++ * address space. ++ */ ++ if (msg->h.context == NULL) { ++ pr_err("received message context was null!\n"); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ } ++ ++ /* fill in context values */ ++ msg->h.context->u.sync.msg_handle = msg_handle; ++ msg->h.context->u.sync.msg = msg; ++ msg->h.context->u.sync.msg_len = msg_len; ++ ++ /* todo: should this check (completion_done() ++ * == 1) for no one waiting? or do we need a ++ * flag to tell us the completion has been ++ * interrupted so we can free the message and ++ * its context. This probably also solves the ++ * message arriving after interruption todo ++ * below ++ */ ++ ++ /* complete message so caller knows it happened */ ++ complete(&msg->h.context->u.sync.cmplt); ++ break; ++ } ++ ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVED: ++ bulk_receive_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: ++ bulk_abort_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ /* TODO: consider if this requires action if received when ++ * driver is not explicitly closing the service ++ */ ++ break; ++ ++ default: ++ pr_err("Received unhandled message reason %d\n", reason); ++ break; ++ } ++} ++ ++static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ unsigned int payload_len, ++ struct mmal_msg **msg_out, ++ VCHI_HELD_MSG_T *msg_handle_out) ++{ ++ struct mmal_msg_context msg_context; ++ int ret; ++ ++ /* payload size must not cause message to exceed max size */ ++ if (payload_len > ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { ++ pr_err("payload length %d exceeds max:%d\n", payload_len, ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); ++ return -EINVAL; ++ } ++ ++ init_completion(&msg_context.u.sync.cmplt); ++ ++ msg->h.magic = MMAL_MAGIC; ++ msg->h.context = &msg_context; ++ msg->h.status = 0; ++ ++ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), ++ ">>> sync message"); ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, ++ msg, ++ sizeof(struct mmal_msg_header) + payload_len, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret) { ++ pr_err("error %d queuing message\n", ret); ++ return ret; ++ } ++ ++ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, 3*HZ); ++ if (ret <= 0) { ++ pr_err("error %d waiting for sync completion\n", ret); ++ if (ret == 0) ++ ret = -ETIME; ++ /* todo: what happens if the message arrives after aborting */ ++ return ret; ++ } ++ ++ *msg_out = msg_context.u.sync.msg; ++ *msg_handle_out = msg_context.u.sync.msg_handle; ++ ++ return 0; ++} ++ ++static void dump_port_info(struct vchiq_mmal_port *port) ++{ ++ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); ++ ++ pr_debug("buffer minimum num:%d size:%d align:%d\n", ++ port->minimum_buffer.num, ++ port->minimum_buffer.size, port->minimum_buffer.alignment); ++ ++ pr_debug("buffer recommended num:%d size:%d align:%d\n", ++ port->recommended_buffer.num, ++ port->recommended_buffer.size, ++ port->recommended_buffer.alignment); ++ ++ pr_debug("buffer current values num:%d size:%d align:%d\n", ++ port->current_buffer.num, ++ port->current_buffer.size, port->current_buffer.alignment); ++ ++ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", ++ port->format.type, ++ port->format.encoding, port->format.encoding_variant); ++ ++ pr_debug(" bitrate:%d flags:0x%x\n", ++ port->format.bitrate, port->format.flags); ++ ++ if (port->format.type == MMAL_ES_TYPE_VIDEO) { ++ pr_debug ++ ("es video format: width:%d height:%d colourspace:0x%x\n", ++ port->es.video.width, port->es.video.height, ++ port->es.video.color_space); ++ ++ pr_debug(" : crop xywh %d,%d,%d,%d\n", ++ port->es.video.crop.x, ++ port->es.video.crop.y, ++ port->es.video.crop.width, port->es.video.crop.height); ++ pr_debug(" : framerate %d/%d aspect %d/%d\n", ++ port->es.video.frame_rate.num, ++ port->es.video.frame_rate.den, ++ port->es.video.par.num, port->es.video.par.den); ++ } ++} ++ ++static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) ++{ ++ ++ /* todo do readonly fields need setting at all? */ ++ p->type = port->type; ++ p->index = port->index; ++ p->index_all = 0; ++ p->is_enabled = port->enabled; ++ p->buffer_num_min = port->minimum_buffer.num; ++ p->buffer_size_min = port->minimum_buffer.size; ++ p->buffer_alignment_min = port->minimum_buffer.alignment; ++ p->buffer_num_recommended = port->recommended_buffer.num; ++ p->buffer_size_recommended = port->recommended_buffer.size; ++ ++ /* only three writable fields in a port */ ++ p->buffer_num = port->current_buffer.num; ++ p->buffer_size = port->current_buffer.size; ++ p->userdata = port; ++} ++ ++static int port_info_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ pr_debug("setting port info port %p\n", port); ++ if (!port) ++ return -1; ++ dump_port_info(port); ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; ++ ++ m.u.port_info_set.component_handle = port->component->handle; ++ m.u.port_info_set.port_type = port->type; ++ m.u.port_info_set.port_index = port->index; ++ ++ port_to_mmal_msg(port, &m.u.port_info_set.port); ++ ++ /* elementry stream format setup */ ++ m.u.port_info_set.format.type = port->format.type; ++ m.u.port_info_set.format.encoding = port->format.encoding; ++ m.u.port_info_set.format.encoding_variant = ++ port->format.encoding_variant; ++ m.u.port_info_set.format.bitrate = port->format.bitrate; ++ m.u.port_info_set.format.flags = port->format.flags; ++ ++ memcpy(&m.u.port_info_set.es, &port->es, ++ sizeof(union mmal_es_specific_format)); ++ ++ m.u.port_info_set.format.extradata_size = port->format.extradata_size; ++ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, ++ port->format.extradata_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_set), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, ++ port->component->handle, port->handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++ ++} ++ ++/* use port info get message to retrive port information */ ++static int port_info_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* port info time */ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; ++ m.u.port_info_get.component_handle = port->component->handle; ++ m.u.port_info_get.port_type = port->type; ++ m.u.port_info_get.index = port->index; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) ++ port->enabled = false; ++ else ++ port->enabled = true; ++ ++ /* copy the values out of the message */ ++ port->handle = rmsg->u.port_info_get_reply.port_handle; ++ ++ /* port type and index cached to use on port info set becuase ++ * it does not use a port handle ++ */ ++ port->type = rmsg->u.port_info_get_reply.port_type; ++ port->index = rmsg->u.port_info_get_reply.port_index; ++ ++ port->minimum_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_min; ++ port->minimum_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size_min; ++ port->minimum_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ ++ port->recommended_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ port->recommended_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_recommended; ++ ++ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; ++ port->current_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size; ++ ++ /* stream format */ ++ port->format.type = rmsg->u.port_info_get_reply.format.type; ++ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; ++ port->format.encoding_variant = ++ rmsg->u.port_info_get_reply.format.encoding_variant; ++ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; ++ port->format.flags = rmsg->u.port_info_get_reply.format.flags; ++ ++ /* elementry stream format */ ++ memcpy(&port->es, ++ &rmsg->u.port_info_get_reply.es, ++ sizeof(union mmal_es_specific_format)); ++ port->format.es = &port->es; ++ ++ port->format.extradata_size = ++ rmsg->u.port_info_get_reply.format.extradata_size; ++ memcpy(port->format.extradata, ++ rmsg->u.port_info_get_reply.extradata, ++ port->format.extradata_size); ++ ++ pr_debug("received port info\n"); ++ dump_port_info(port); ++ ++release_msg: ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", ++ __func__, ret, port->component->handle, port->handle); ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* create comonent on vc */ ++static int create_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component, ++ const char *name) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* build component create message */ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; ++ m.u.component_create.client_component = component; ++ strncpy(m.u.component_create.name, name, ++ sizeof(m.u.component_create.name)); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_create), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_create_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ /* a valid component response received */ ++ component->handle = rmsg->u.component_create_reply.component_handle; ++ component->inputs = rmsg->u.component_create_reply.input_num; ++ component->outputs = rmsg->u.component_create_reply.output_num; ++ component->clocks = rmsg->u.component_create_reply.clock_num; ++ ++ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", ++ component->handle, ++ component->inputs, component->outputs, component->clocks); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* destroys a component on vc */ ++static int destroy_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; ++ m.u.component_destroy.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_destroy), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_destroy_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* enable a component on vc */ ++static int enable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; ++ m.u.component_enable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_enable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_enable_reply.status; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disable a component on vc */ ++static int disable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; ++ m.u.component_disable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_disable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_disable_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* get version of mmal implementation */ ++static int get_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_GET_VERSION; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.version), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ *major_out = rmsg->u.version.major; ++ *minor_out = rmsg->u.version.minor; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with a port as a parameter */ ++static int port_action_port(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ m.u.port_action_port.component_handle = port->component->handle; ++ m.u.port_action_port.port_handle = port->handle; ++ m.u.port_action_port.action = action_type; ++ ++ port_to_mmal_msg(port, &m.u.port_action_port.port); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_port), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], action_type); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with handles as parameters */ ++static int port_action_handle(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type, ++ u32 connect_component_handle, ++ u32 connect_port_handle) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ ++ m.u.port_action_handle.component_handle = port->component->handle; ++ m.u.port_action_handle.port_handle = port->handle; ++ m.u.port_action_handle.action = action_type; ++ ++ m.u.port_action_handle.connect_component_handle = ++ connect_component_handle; ++ m.u.port_action_handle.connect_port_handle = connect_port_handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_handle), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ ++ " connect component:0x%x connect port:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], ++ action_type, connect_component_handle, connect_port_handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; ++ ++ m.u.port_parameter_set.component_handle = port->component->handle; ++ m.u.port_parameter_set.port_handle = port->handle; ++ m.u.port_parameter_set.id = parameter_id; ++ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; ++ memcpy(&m.u.port_parameter_set.value, value, value_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ (4 * sizeof(u32)) + value_size, ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_set_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 *value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; ++ ++ m.u.port_parameter_get.component_handle = port->component->handle; ++ m.u.port_parameter_get.port_handle = port->handle; ++ m.u.port_parameter_get.id = parameter_id; ++ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(struct ++ mmal_msg_port_parameter_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { ++ /* got an unexpected message type in reply */ ++ pr_err("Incorrect reply type %d\n", rmsg->h.type); ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_get_reply.status; ++ if (ret) { ++ /* Copy only as much as we have space for ++ * but report true size of parameter ++ */ ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ *value_size); ++ *value_size = rmsg->u.port_parameter_get_reply.size; ++ } else ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ rmsg->u.port_parameter_get_reply.size); ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disables a port and drains buffers from it */ ++static int port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct list_head *q, *buf_head; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return 0; ++ ++ port->enabled = false; ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); ++ if (ret == 0) { ++ ++ /* drain all queued buffers on port */ ++ spin_lock_irqsave(&port->slock, flags); ++ ++ list_for_each_safe(buf_head, q, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ list_del(buf_head); ++ if (port->buffer_cb) ++ port->buffer_cb(instance, ++ port, 0, mmalbuf, 0, 0, ++ MMAL_TIME_UNKNOWN, ++ MMAL_TIME_UNKNOWN); ++ } ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ ret = port_info_get(instance, port); ++ } ++ ++ return ret; ++} ++ ++/* enable a port */ ++static int port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ unsigned int hdr_count; ++ struct list_head *buf_head; ++ int ret; ++ ++ if (port->enabled) ++ return 0; ++ ++ /* ensure there are enough buffers queued to cover the buffer headers */ ++ if (port->buffer_cb != NULL) { ++ hdr_count = 0; ++ list_for_each(buf_head, &port->buffers) { ++ hdr_count++; ++ } ++ if (hdr_count < port->current_buffer.num) ++ return -ENOSPC; ++ } ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); ++ if (ret) ++ goto done; ++ ++ port->enabled = true; ++ ++ if (port->buffer_cb) { ++ /* send buffer headers to videocore */ ++ hdr_count = 1; ++ list_for_each(buf_head, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ ret = buffer_from_host(instance, port, mmalbuf); ++ if (ret) ++ goto done; ++ ++ hdr_count++; ++ if (hdr_count > port->current_buffer.num) ++ break; ++ } ++ } ++ ++ ret = port_info_get(instance, port); ++ ++done: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------ ++ * Exported API ++ *------------------------------------------------------------------*/ ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_info_set(instance, port); ++ if (ret) ++ goto release_unlock; ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, port); ++ ++release_unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++ ++} ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_set(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 *value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_get(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* enable a port ++ * ++ * enables a port and queues buffers for satisfying callbacks if we ++ * provide a callback handler ++ */ ++int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* already enabled - noop */ ++ if (port->enabled) { ++ ret = 0; ++ goto unlock; ++ } ++ ++ port->buffer_cb = buffer_cb; ++ ++ ret = port_enable(instance, port); ++ ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!port->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = port_disable(instance, port); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ports will be connected in a tunneled manner so data buffers ++ * are not handled by client. ++ */ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* disconnect ports if connected */ ++ if (src->connected != NULL) { ++ ret = port_disable(instance, src); ++ if (ret) { ++ pr_err("failed disabling src port(%d)\n", ret); ++ goto release_unlock; ++ } ++ ++ /* do not need to disable the destination port as they ++ * are connected and it is done automatically ++ */ ++ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, ++ src->connected->component->handle, ++ src->connected->handle); ++ if (ret < 0) { ++ pr_err("failed disconnecting src port\n"); ++ goto release_unlock; ++ } ++ src->connected->enabled = false; ++ src->connected = NULL; ++ } ++ ++ if (dst == NULL) { ++ /* do not make new connection */ ++ ret = 0; ++ pr_debug("not making new connection\n"); ++ goto release_unlock; ++ } ++ ++ /* copy src port format to dst */ ++ dst->format.encoding = src->format.encoding; ++ dst->es.video.width = src->es.video.width; ++ dst->es.video.height = src->es.video.height; ++ dst->es.video.crop.x = src->es.video.crop.x; ++ dst->es.video.crop.y = src->es.video.crop.y; ++ dst->es.video.crop.width = src->es.video.crop.width; ++ dst->es.video.crop.height = src->es.video.crop.height; ++ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; ++ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ ++ /* set new format */ ++ ret = port_info_set(instance, dst); ++ if (ret) { ++ pr_debug("setting port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, dst); ++ if (ret) { ++ pr_debug("read back port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* connect two ports together */ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, ++ dst->component->handle, dst->handle); ++ if (ret < 0) { ++ pr_debug("connecting port %d:%d to %d:%d failed\n", ++ src->component->handle, src->handle, ++ dst->component->handle, dst->handle); ++ goto release_unlock; ++ } ++ src->connected = dst; ++ ++release_unlock: ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buffer) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->slock, flags); ++ list_add_tail(&buffer->list, &port->buffers); ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* the port previously underflowed because it was missing a ++ * mmal_buffer which has just been added, submit that buffer ++ * to the mmal service. ++ */ ++ if (port->buffer_underflow) { ++ port_buffer_from_host(instance, port); ++ port->buffer_underflow--; ++ } ++ ++ return 0; ++} ++ ++/* Initialise a mmal component and its ports ++ * ++ */ ++int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out) ++{ ++ int ret; ++ int idx; /* port index */ ++ struct vchiq_mmal_component *component; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { ++ ret = -EINVAL; /* todo is this correct error? */ ++ goto unlock; ++ } ++ ++ component = &instance->component[instance->component_idx]; ++ ++ ret = create_component(instance, component, name); ++ if (ret < 0) ++ goto unlock; ++ ++ /* ports info needs gathering */ ++ component->control.type = MMAL_PORT_TYPE_CONTROL; ++ component->control.index = 0; ++ component->control.component = component; ++ spin_lock_init(&component->control.slock); ++ INIT_LIST_HEAD(&component->control.buffers); ++ ret = port_info_get(instance, &component->control); ++ if (ret < 0) ++ goto release_component; ++ ++ for (idx = 0; idx < component->inputs; idx++) { ++ component->input[idx].type = MMAL_PORT_TYPE_INPUT; ++ component->input[idx].index = idx; ++ component->input[idx].component = component; ++ spin_lock_init(&component->input[idx].slock); ++ INIT_LIST_HEAD(&component->input[idx].buffers); ++ ret = port_info_get(instance, &component->input[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->outputs; idx++) { ++ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; ++ component->output[idx].index = idx; ++ component->output[idx].component = component; ++ spin_lock_init(&component->output[idx].slock); ++ INIT_LIST_HEAD(&component->output[idx].buffers); ++ ret = port_info_get(instance, &component->output[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->clocks; idx++) { ++ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; ++ component->clock[idx].index = idx; ++ component->clock[idx].component = component; ++ spin_lock_init(&component->clock[idx].slock); ++ INIT_LIST_HEAD(&component->clock[idx].buffers); ++ ret = port_info_get(instance, &component->clock[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ instance->component_idx++; ++ ++ *component_out = component; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return 0; ++ ++release_component: ++ destroy_component(instance, component); ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be destroyed ++ */ ++int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) ++ ret = disable_component(instance, component); ++ ++ ret = destroy_component(instance, component); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = enable_component(instance, component); ++ if (ret == 0) ++ component->enabled = true; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = disable_component(instance, component); ++ if (ret == 0) ++ component->enabled = false; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = get_version(instance, major_out, minor_out); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) ++{ ++ int status = 0; ++ ++ if (instance == NULL) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ vchi_service_use(instance->handle); ++ ++ status = vchi_service_close(instance->handle); ++ if (status != 0) ++ pr_err("mmal-vchiq: VCHIQ close failed"); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ vfree(instance->bulk_scratch); ++ ++ kfree(instance); ++ ++ return status; ++} ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++{ ++ int status; ++ struct vchiq_mmal_instance *instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ static VCHI_INSTANCE_T vchi_instance; ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), ++ VC_MMAL_SERVER_NAME, ++ vchi_connection, ++ 0, /* rx fifo size (unused) */ ++ 0, /* tx fifo size (unused) */ ++ service_callback, ++ NULL, /* service callback parameter */ ++ 1, /* unaligned bulk receives */ ++ 1, /* unaligned bulk transmits */ ++ 0 /* want crc check on bulk transfers */ ++ }; ++ ++ /* compile time checks to ensure structure size as they are ++ * directly (de)serialised from memory. ++ */ ++ ++ /* ensure the header structure has packed to the correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ ++ /* ensure message structure does not exceed maximum length */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); ++ ++ /* mmal port struct is correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); ++ ++ /* create a vchi instance */ ++ status = vchi_initialise(&vchi_instance); ++ if (status) { ++ pr_err("Failed to initialise VCHI instance (status=%d)\n", ++ status); ++ return -EIO; ++ } ++ ++ status = vchi_connect(NULL, 0, vchi_instance); ++ if (status) { ++ pr_err("Failed to connect VCHI instance (status=%d)\n", status); ++ return -EIO; ++ } ++ ++ instance = kmalloc(sizeof(*instance), GFP_KERNEL); ++ memset(instance, 0, sizeof(*instance)); ++ ++ mutex_init(&instance->vchiq_mutex); ++ mutex_init(&instance->bulk_mutex); ++ ++ instance->bulk_scratch = vmalloc(PAGE_SIZE); ++ ++ params.callback_param = instance; ++ ++ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); ++ if (status) { ++ pr_err("Failed to open VCHI service connection (status=%d)\n", ++ status); ++ goto err_close_services; ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ *out_instance = instance; ++ ++ return 0; ++ ++err_close_services: ++ ++ vchi_service_close(instance->handle); ++ vfree(instance->bulk_scratch); ++ kfree(instance); ++ return -ENODEV; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h +new file mode 100644 +index 0000000..9d1d11e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.h +@@ -0,0 +1,178 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL interface to VCHIQ message passing ++ */ ++ ++#ifndef MMAL_VCHIQ_H ++#define MMAL_VCHIQ_H ++ ++#include "mmal-msg-format.h" ++ ++#define MAX_PORT_COUNT 4 ++ ++/* Maximum size of the format extradata. */ ++#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 ++ ++struct vchiq_mmal_instance; ++ ++enum vchiq_mmal_es_type { ++ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ ++ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ ++ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ ++ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ ++ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ ++}; ++ ++/* rectangle, used lots so it gets its own struct */ ++struct vchiq_mmal_rect { ++ s32 x; ++ s32 y; ++ s32 width; ++ s32 height; ++}; ++ ++struct vchiq_mmal_port_buffer { ++ unsigned int num; /* number of buffers */ ++ u32 size; /* size of buffers */ ++ u32 alignment; /* alignment of buffers */ ++}; ++ ++struct vchiq_mmal_port; ++ ++typedef void (*vchiq_mmal_buffer_cb)( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, struct mmal_buffer *buffer, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); ++ ++struct vchiq_mmal_port { ++ bool enabled; ++ u32 handle; ++ u32 type; /* port type, cached to use on port info set */ ++ u32 index; /* port index, cached to use on port info set */ ++ ++ /* component port belongs to, allows simple deref */ ++ struct vchiq_mmal_component *component; ++ ++ struct vchiq_mmal_port *connected; /* port conencted to */ ++ ++ /* buffer info */ ++ struct vchiq_mmal_port_buffer minimum_buffer; ++ struct vchiq_mmal_port_buffer recommended_buffer; ++ struct vchiq_mmal_port_buffer current_buffer; ++ ++ /* stream format */ ++ struct mmal_es_format format; ++ /* elementry stream format */ ++ union mmal_es_specific_format es; ++ ++ /* data buffers to fill */ ++ struct list_head buffers; ++ /* lock to serialise adding and removing buffers from list */ ++ spinlock_t slock; ++ /* count of how many buffer header refils have failed because ++ * there was no buffer to satisfy them ++ */ ++ int buffer_underflow; ++ /* callback on buffer completion */ ++ vchiq_mmal_buffer_cb buffer_cb; ++ /* callback context */ ++ void *cb_ctx; ++}; ++ ++struct vchiq_mmal_component { ++ bool enabled; ++ u32 handle; /* VideoCore handle for component */ ++ u32 inputs; /* Number of input ports */ ++ u32 outputs; /* Number of output ports */ ++ u32 clocks; /* Number of clock ports */ ++ struct vchiq_mmal_port control; /* control port */ ++ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ ++ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ ++ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ ++}; ++ ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); ++ ++/* Initialise a mmal component and its ports ++* ++*/ ++int vchiq_mmal_component_init( ++ struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out); ++ ++int vchiq_mmal_component_finalise( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_disable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++ ++ ++/* enable a mmal port ++ * ++ * enables a port and if a buffer callback provided enque buffer ++ * headers as apropriate for the port. ++ */ ++int vchiq_mmal_port_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb); ++ ++/* disable a port ++ * ++ * disable a port will dequeue any pending buffers ++ */ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 value_size); ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 *value_size); ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst); ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, ++ u32 *minor_out); ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buf); ++ ++#endif /* MMAL_VCHIQ_H */ + +From a386866d41f3d5603dc84a4763284e7510b72333 Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 6 Jul 2014 12:07:25 +0200 +Subject: [PATCH 019/104] spi-bcm2708: Prepare for Common Clock Framework + migration + +As part of migrating to use the Common Clock Framework, replace clk_enable() +with clk_prepare_enable() and clk_disable() with clk_disable_unprepare(). +This does not affect behaviour under the current clock implementation. + +Also add a missing clk_disable_unprepare() in the probe error path. + +Signed-off-by: Noralf Tronnes +--- + drivers/spi/spi-bcm2708.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +index b04a57d..349d21f 100644 +--- a/drivers/spi/spi-bcm2708.c ++++ b/drivers/spi/spi-bcm2708.c +@@ -545,7 +545,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + } + + /* initialise the hardware */ +- clk_enable(clk); ++ clk_prepare_enable(clk); + bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); + + err = spi_register_master(master); +@@ -561,6 +561,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + + out_free_irq: + free_irq(bs->irq, master); ++ clk_disable_unprepare(bs->clk); + out_workqueue: + destroy_workqueue(bs->workq); + out_iounmap: +@@ -585,7 +586,7 @@ static int bcm2708_spi_remove(struct platform_device *pdev) + + flush_work(&bs->work); + +- clk_disable(bs->clk); ++ clk_disable_unprepare(bs->clk); + clk_put(bs->clk); + free_irq(bs->irq, master); + iounmap(bs->base); + +From 3d32fd6b5984a34d576228ef9f1c7f1c3d2002cf Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 6 Jul 2014 12:09:30 +0200 +Subject: [PATCH 020/104] BCM2708: Migrate to the Common Clock Framework + +As part of moving towards using Device Tree, the Common Clock Framework +has to be used instead of the BCM2708 clock implementation. + +Selecting COMMON_CLK removes the need to set CLKDEV_LOOKUP and HAVE_CLK explicitly. + +CONFIG_ARCH_BCM2708_CHIPIT #ifdef's are removed. They are no longer in use. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/Kconfig | 3 +- + arch/arm/mach-bcm2708/Makefile | 2 +- + arch/arm/mach-bcm2708/bcm2708.c | 79 +++++++++++++++++------------------------ + arch/arm/mach-bcm2708/clock.c | 61 ------------------------------- + arch/arm/mach-bcm2708/clock.h | 24 ------------- + 5 files changed, 34 insertions(+), 135 deletions(-) + delete mode 100644 arch/arm/mach-bcm2708/clock.c + delete mode 100644 arch/arm/mach-bcm2708/clock.h + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 9fcd395..c495965 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -373,11 +373,10 @@ config ARCH_BCM2708 + bool "Broadcom BCM2708 family" + select CPU_V6 + select ARM_AMBA +- select HAVE_CLK + select HAVE_SCHED_CLOCK + select NEED_MACH_GPIO_H + select NEED_MACH_MEMORY_H +- select CLKDEV_LOOKUP ++ select COMMON_CLK + select ARCH_HAS_CPUFREQ + select GENERIC_CLOCKEVENTS + select ARM_ERRATA_411920 +diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile +index a722f3f..21e3521 100644 +--- a/arch/arm/mach-bcm2708/Makefile ++++ b/arch/arm/mach-bcm2708/Makefile +@@ -2,6 +2,6 @@ + # Makefile for the linux kernel. + # + +-obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o ++obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o dma.o + obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o + obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 752dd46..080e260 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -27,6 +27,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -57,7 +59,6 @@ + + #include "bcm2708.h" + #include "armctrl.h" +-#include "clock.h" + + #ifdef CONFIG_BCM_VC_CMA + #include +@@ -78,7 +79,7 @@ + + /* command line parameters */ + static unsigned boardrev, serial; +-static unsigned uart_clock; ++static unsigned uart_clock = UART0_CLOCK; + static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; +@@ -188,51 +189,39 @@ static void __init bcm2708_clocksource_init(void) + } + } + ++struct clk __init *bcm2708_clk_register(const char *name, unsigned long fixed_rate) ++{ ++ struct clk *clk; + +-/* +- * These are fixed clocks. +- */ +-static struct clk ref24_clk = { +- .rate = UART0_CLOCK, /* The UART is clocked at 3MHz via APB_CLK */ +-}; ++ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, ++ fixed_rate); ++ if (IS_ERR(clk)) ++ pr_err("%s not registered\n", name); + +-static struct clk osc_clk = { +-#ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 27000000, +-#else +- .rate = 500000000, /* ARM clock is set from the VideoCore booter */ +-#endif +-}; ++ return clk; ++} + +-/* warning - the USB needs a clock > 34MHz */ ++void __init bcm2708_register_clkdev(struct clk *clk, const char *name) ++{ ++ int ret; + +-static struct clk sdhost_clk = { +-#ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 4000000, /* 4MHz */ +-#else +- .rate = 250000000, /* 250MHz */ +-#endif +-}; ++ ret = clk_register_clkdev(clk, NULL, name); ++ if (ret) ++ pr_err("%s alias not registered\n", name); ++} + +-static struct clk_lookup lookups[] = { +- { /* UART0 */ +- .dev_id = "dev:f1", +- .clk = &ref24_clk, +- }, +- { /* USB */ +- .dev_id = "bcm2708_usb", +- .clk = &osc_clk, +- }, { /* SPI */ +- .dev_id = "bcm2708_spi.0", +- .clk = &sdhost_clk, +- }, { /* BSC0 */ +- .dev_id = "bcm2708_i2c.0", +- .clk = &sdhost_clk, +- }, { /* BSC1 */ +- .dev_id = "bcm2708_i2c.1", +- .clk = &sdhost_clk, +- } +-}; ++void __init bcm2708_init_clocks(void) ++{ ++ struct clk *clk; ++ ++ clk = bcm2708_clk_register("uart0_clk", uart_clock); ++ bcm2708_register_clkdev(clk, "dev:f1"); ++ ++ clk = bcm2708_clk_register("sdhost_clk", 250000000); ++ bcm2708_register_clkdev(clk, "bcm2708_spi.0"); ++ bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); ++ bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); ++} + + #define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } + #define UART0_DMA { 15, 14 } +@@ -685,11 +674,7 @@ void __init bcm2708_init(void) + printk("bcm2708.uart_clock = %d\n", uart_clock); + pm_power_off = bcm2708_power_off; + +- if (uart_clock) +- lookups[0].clk->rate = uart_clock; +- +- for (i = 0; i < ARRAY_SIZE(lookups); i++) +- clkdev_add(&lookups[i]); ++ bcm2708_init_clocks(); + + bcm_register_device(&bcm2708_dmaman_device); + bcm_register_device(&bcm2708_dmaengine_device); +diff --git a/arch/arm/mach-bcm2708/clock.c b/arch/arm/mach-bcm2708/clock.c +deleted file mode 100644 +index 4fc556e..0000000 +--- a/arch/arm/mach-bcm2708/clock.c ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- * linux/arch/arm/mach-bcm2708/clock.c +- * +- * Copyright (C) 2010 Broadcom +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "clock.h" +- +-int clk_enable(struct clk *clk) +-{ +- return 0; +-} +-EXPORT_SYMBOL(clk_enable); +- +-void clk_disable(struct clk *clk) +-{ +-} +-EXPORT_SYMBOL(clk_disable); +- +-unsigned long clk_get_rate(struct clk *clk) +-{ +- return clk->rate; +-} +-EXPORT_SYMBOL(clk_get_rate); +- +-long clk_round_rate(struct clk *clk, unsigned long rate) +-{ +- return clk->rate; +-} +-EXPORT_SYMBOL(clk_round_rate); +- +-int clk_set_rate(struct clk *clk, unsigned long rate) +-{ +- return -EIO; +-} +-EXPORT_SYMBOL(clk_set_rate); +diff --git a/arch/arm/mach-bcm2708/clock.h b/arch/arm/mach-bcm2708/clock.h +deleted file mode 100644 +index 5f9d725..0000000 +--- a/arch/arm/mach-bcm2708/clock.h ++++ /dev/null +@@ -1,24 +0,0 @@ +-/* +- * linux/arch/arm/mach-bcm2708/clock.h +- * +- * Copyright (C) 2010 Broadcom +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +-struct module; +- +-struct clk { +- unsigned long rate; +-}; + +From fc0495fc9d5db39fc59407514d6fb2ca1ec060ef Mon Sep 17 00:00:00 2001 +From: notro +Date: Wed, 9 Jul 2014 14:46:08 +0200 +Subject: [PATCH 021/104] BCM2708: Add core Device Tree support + +Add the bare minimum needed to boot BCM2708 from a Device Tree. + +Signed-off-by: Noralf Tronnes + +BCM2708: DT: change 'axi' nodename to 'soc' + +Change DT node named 'axi' to 'soc' so it matches ARCH_BCM2835. +The VC4 bootloader fills in certain properties in the 'axi' subtree, +but since this is part of an upstreaming effort, the name is changed. + +Signed-off-by: Noralf Tronnes notro@tronnes.org + +BCM2708_DT: Correct length of the peripheral space +--- + arch/arm/boot/dts/Makefile | 21 ++++ + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 81 +++++++++++++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 81 +++++++++++++++ + arch/arm/boot/dts/bcm2708.dtsi | 86 ++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 165 +++++++++++++++++++++++++++++++ + arch/arm/mach-bcm2708/Kconfig | 8 ++ + arch/arm/mach-bcm2708/bcm2708.c | 45 ++++++++- + 7 files changed, 484 insertions(+), 3 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-plus.dts + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b.dts + create mode 100644 arch/arm/boot/dts/bcm2708.dtsi + create mode 100644 arch/arm/boot/dts/bcm2709.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index a1c776b..c86833d 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -1,5 +1,17 @@ + ifeq ($(CONFIG_OF),y) + ++dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb ++dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb ++dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb ++ ++# Raspberry Pi ++ifeq ($(CONFIG_BCM2708_DT),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_BCM2709_DT),y) ++ RPI_DT_OVERLAYS=y ++endif ++ + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb + # Keep at91 dtb files sorted alphabetically for each SoC +@@ -645,7 +657,16 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ + mt6592-evb.dtb \ + mt8127-moose.dtb \ + mt8135-evbp1.dtb ++ ++targets += dtbs dtbs_install ++targets += $(dtb-y) ++ + endif + + always := $(dtb-y) + clean-files := *.dtb ++ ++# Enable fixups to support overlays on BCM2708 platforms ++ifeq ($(RPI_DT_OVERLAYS),y) ++ DTC_FLAGS ?= -@ ++endif +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +new file mode 100644 +index 0000000..983c23f +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++ ++/include/ "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B+"; ++ ++ aliases { ++ spi0 = &spi0; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2s = &i2s; ++ gpio = &gpio; ++ sound = &sound; ++ }; ++ ++ sound: sound { ++ }; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <7 8 9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; ++ }; ++ ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { ++ brcm,pins = <18 19 20 21>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ ++ spidev@0{ ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++ ++ spidev@1{ ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++}; ++ ++&i2c0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +new file mode 100644 +index 0000000..d8c6d15 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++ ++/include/ "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B"; ++ ++ aliases { ++ spi0 = &spi0; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2s = &i2s; ++ gpio = &gpio; ++ sound = &sound; ++ }; ++ ++ sound: sound { ++ }; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <7 8 9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; ++ }; ++ ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { ++ brcm,pins = <28 29 30 31>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ ++ spidev@0{ ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++ ++ spidev@1{ ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++}; ++ ++&i2c0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +new file mode 100644 +index 0000000..96b7311 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -0,0 +1,86 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "BCM2708"; ++ ++ interrupt-parent = <&intc>; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x7e000000 0x20000000 0x01000000>; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +new file mode 100644 +index 0000000..15c7e65 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -0,0 +1,165 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2709"; ++ model = "BCM2709"; ++ ++ interrupt-parent = <&intc>; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc: soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x7e000000 0x3f000000 0x01000000>; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ leds: leds { ++ compatible = "gpio-leds"; ++ ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ }; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupts = <3 9>; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ clock-frequency = <19200000>; ++ interrupts = <3 0>, // PHYS_SECURE_PPI ++ <3 1>, // PHYS_NONSECURE_PPI ++ <3 3>, // VIRT_PPI ++ <3 2>; // HYP_PPI ++ always-on; ++ }; ++ ++ cpus: cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ v7_cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf00>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf01>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf02>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf03>; ++ clock-frequency = <800000000>; ++ }; ++ }; ++ ++ __overrides__ { ++ arm_freq = <&v7_cpu0>, "clock-frequency:0", ++ <&v7_cpu1>, "clock-frequency:0", ++ <&v7_cpu2>, "clock-frequency:0", ++ <&v7_cpu3>, "clock-frequency:0"; ++ }; ++}; +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index e151ed4..182e7ba 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -9,6 +9,14 @@ config MACH_BCM2708 + help + Include support for the Broadcom(R) BCM2708 platform. + ++config BCM2708_DT ++ bool "BCM2708 Device Tree support" ++ depends on MACH_BCM2708 ++ default n ++ select USE_OF ++ help ++ Enable Device Tree support for BCM2708 ++ + config BCM2708_GPIO + bool "BCM2708 gpio support" + depends on MACH_BCM2708 +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 080e260..496cbcd 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -33,7 +33,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include + #include +@@ -84,6 +86,8 @@ static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; + ++static unsigned use_dt = 0; ++ + static void __init bcm2708_init_led(void); + + void __init bcm2708_init_irq(void) +@@ -599,6 +603,16 @@ int __init bcm_register_device(struct platform_device *pdev) + return ret; + } + ++/* ++ * Use these macros for platform and i2c devices that are present in the ++ * Device Tree. This way the devices are only added on non-DT systems. ++ */ ++#define bcm_register_device_dt(pdev) \ ++ if (!use_dt) bcm_register_device(pdev) ++ ++#define i2c_register_board_info_dt(busnum, info, n) \ ++ if (!use_dt) i2c_register_board_info(busnum, info, n) ++ + int calc_rsts(int partition) + { + return PM_PASSWORD | +@@ -664,6 +678,24 @@ static void bcm2708_power_off(void) + } + } + ++#ifdef CONFIG_OF ++static void __init bcm2708_dt_init(void) ++{ ++ int ret; ++ ++ of_clk_init(NULL); ++ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++ if (ret) { ++ pr_err("of_platform_populate failed: %d\n", ret); ++ /* Proceed as if CONFIG_OF was not defined */ ++ } else { ++ use_dt = 1; ++ } ++} ++#else ++static void __init bcm2708_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ + void __init bcm2708_init(void) + { + int i; +@@ -675,6 +707,7 @@ void __init bcm2708_init(void) + pm_power_off = bcm2708_power_off; + + bcm2708_init_clocks(); ++ bcm2708_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); + bcm_register_device(&bcm2708_dmaengine_device); +@@ -842,9 +875,9 @@ static struct platform_device bcm2708_led_device = { + + static void __init bcm2708_init_led(void) + { +- bcm2708_leds[0].gpio = disk_led_gpio; +- bcm2708_leds[0].active_low = disk_led_active_low; +- platform_device_register(&bcm2708_led_device); ++ bcm2708_leds[0].gpio = disk_led_gpio; ++ bcm2708_leds[0].active_low = disk_led_active_low; ++ bcm_register_device_dt(&bcm2708_led_device); + } + #else + static inline void bcm2708_init_led(void) +@@ -869,6 +902,11 @@ static void __init board_reserve(void) + #endif + } + ++static const char * const bcm2708_compat[] = { ++ "brcm,bcm2708", ++ NULL ++}; ++ + MACHINE_START(BCM2708, "BCM2708") + /* Maintainer: Broadcom Europe Ltd. */ + .map_io = bcm2708_map_io, +@@ -878,6 +916,7 @@ MACHINE_START(BCM2708, "BCM2708") + .init_early = bcm2708_init_early, + .reserve = board_reserve, + .restart = bcm2708_restart, ++ .dt_compat = bcm2708_compat, + MACHINE_END + + module_param(boardrev, uint, 0644); + +From 3899231316f50d34a0843fc025ef541ebf5b1c1c Mon Sep 17 00:00:00 2001 +From: Siarhei Siamashka +Date: Mon, 17 Jun 2013 13:32:11 +0300 +Subject: [PATCH 022/104] fbdev: add FBIOCOPYAREA ioctl + +Based on the patch authored by Ali Gholami Rudi at + https://lkml.org/lkml/2009/7/13/153 + +Provide an ioctl for userspace applications, but only if this operation +is hardware accelerated (otherwide it does not make any sense). + +Signed-off-by: Siarhei Siamashka +--- + drivers/video/fbdev/core/fbmem.c | 30 ++++++++++++++++++++++++++++++ + include/uapi/linux/fb.h | 5 +++++ + 2 files changed, 35 insertions(+) + +diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c +index 0705d88..771992a 100644 +--- a/drivers/video/fbdev/core/fbmem.c ++++ b/drivers/video/fbdev/core/fbmem.c +@@ -1084,6 +1084,25 @@ fb_blank(struct fb_info *info, int blank) + } + EXPORT_SYMBOL(fb_blank); + ++static int fb_copyarea_user(struct fb_info *info, ++ struct fb_copyarea *copy) ++{ ++ int ret = 0; ++ if (!lock_fb_info(info)) ++ return -ENODEV; ++ if (copy->dx + copy->width > info->var.xres || ++ copy->sx + copy->width > info->var.xres || ++ copy->dy + copy->height > info->var.yres || ++ copy->sy + copy->height > info->var.yres) { ++ ret = -EINVAL; ++ goto out; ++ } ++ info->fbops->fb_copyarea(info, copy); ++out: ++ unlock_fb_info(info); ++ return ret; ++} ++ + static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) + { +@@ -1094,6 +1113,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + struct fb_event event; ++ struct fb_copyarea copy; + void __user *argp = (void __user *)arg; + long ret = 0; + +@@ -1211,6 +1231,15 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unlock_fb_info(info); + console_unlock(); + break; ++ case FBIOCOPYAREA: ++ if (info->flags & FBINFO_HWACCEL_COPYAREA) { ++ /* only provide this ioctl if it is accelerated */ ++ if (copy_from_user(©, argp, sizeof(copy))) ++ return -EFAULT; ++ ret = fb_copyarea_user(info, ©); ++ break; ++ } ++ /* fall through */ + default: + if (!lock_fb_info(info)) + return -ENODEV; +@@ -1365,6 +1394,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, + case FBIOPAN_DISPLAY: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: ++ case FBIOCOPYAREA: + arg = (unsigned long) compat_ptr(arg); + case FBIOBLANK: + ret = do_fb_ioctl(info, cmd, arg); +diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h +index fb795c3..fa72af0 100644 +--- a/include/uapi/linux/fb.h ++++ b/include/uapi/linux/fb.h +@@ -34,6 +34,11 @@ + #define FBIOPUT_MODEINFO 0x4617 + #define FBIOGET_DISPINFO 0x4618 + #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) ++/* ++ * HACK: use 'z' in order not to clash with any other ioctl numbers which might ++ * be concurrently added to the mainline kernel ++ */ ++#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea) + + #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ + #define FB_TYPE_PLANES 1 /* Non interleaved planes */ + +From 33cab037eadd63b83b445c4036f4feda8cfeaf59 Mon Sep 17 00:00:00 2001 +From: Harm Hanemaaijer +Date: Thu, 20 Jun 2013 20:21:39 +0200 +Subject: [PATCH 025/104] Speed up console framebuffer imageblit function + +Especially on platforms with a slower CPU but a relatively high +framebuffer fill bandwidth, like current ARM devices, the existing +console monochrome imageblit function used to draw console text is +suboptimal for common pixel depths such as 16bpp and 32bpp. The existing +code is quite general and can deal with several pixel depths. By creating +special case functions for 16bpp and 32bpp, by far the most common pixel +formats used on modern systems, a significant speed-up is attained +which can be readily felt on ARM-based devices like the Raspberry Pi +and the Allwinner platform, but should help any platform using the +fb layer. + +The special case functions allow constant folding, eliminating a number +of instructions including divide operations, and allow the use of an +unrolled loop, eliminating instructions with a variable shift size, +reducing source memory access instructions, and eliminating excessive +branching. These unrolled loops also allow much better code optimization +by the C compiler. The code that selects which optimized variant is used +is also simplified, eliminating integer divide instructions. + +The speed-up, measured by timing 'cat file.txt' in the console, varies +between 40% and 70%, when testing on the Raspberry Pi and Allwinner +ARM-based platforms, depending on font size and the pixel depth, with +the greater benefit for 32bpp. + +Signed-off-by: Harm Hanemaaijer +--- + drivers/video/fbdev/core/cfbimgblt.c | 152 +++++++++++++++++++++++++++++++++-- + 1 file changed, 147 insertions(+), 5 deletions(-) + +diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c +index a2bb276..436494f 100644 +--- a/drivers/video/fbdev/core/cfbimgblt.c ++++ b/drivers/video/fbdev/core/cfbimgblt.c +@@ -28,6 +28,11 @@ + * + * Also need to add code to deal with cards endians that are different than + * the native cpu endians. I also need to deal with MSB position in the word. ++ * Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013: ++ * - Provide optimized versions of fast_imageblit for 16 and 32bpp that are ++ * significantly faster than the previous implementation. ++ * - Simplify the fast/slow_imageblit selection code, avoiding integer ++ * divides. + */ + #include + #include +@@ -262,6 +267,133 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * + } + } + ++/* ++ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded ++ * into the code, main loop unrolled. ++ */ ++ ++static inline void fast_imageblit16(const struct fb_image *image, ++ struct fb_info *p, u8 __iomem * dst1, ++ u32 fgcolor, u32 bgcolor) ++{ ++ u32 fgx = fgcolor, bgx = bgcolor; ++ u32 spitch = (image->width + 7) / 8; ++ u32 end_mask, eorx; ++ const char *s = image->data, *src; ++ u32 __iomem *dst; ++ const u32 *tab = NULL; ++ int i, j, k; ++ ++ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; ++ ++ fgx <<= 16; ++ bgx <<= 16; ++ fgx |= fgcolor; ++ bgx |= bgcolor; ++ ++ eorx = fgx ^ bgx; ++ k = image->width / 2; ++ ++ for (i = image->height; i--;) { ++ dst = (u32 __iomem *) dst1; ++ src = s; ++ ++ j = k; ++ while (j >= 4) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 6) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 4) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 2) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[bits & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ src++; ++ j -= 4; ++ } ++ if (j != 0) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 6) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ if (j >= 2) { ++ end_mask = tab[(bits >> 4) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ if (j == 3) { ++ end_mask = tab[(bits >> 2) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst); ++ } ++ } ++ } ++ dst1 += p->fix.line_length; ++ s += spitch; ++ } ++} ++ ++/* ++ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded ++ * into the code, main loop unrolled. ++ */ ++ ++static inline void fast_imageblit32(const struct fb_image *image, ++ struct fb_info *p, u8 __iomem * dst1, ++ u32 fgcolor, u32 bgcolor) ++{ ++ u32 fgx = fgcolor, bgx = bgcolor; ++ u32 spitch = (image->width + 7) / 8; ++ u32 end_mask, eorx; ++ const char *s = image->data, *src; ++ u32 __iomem *dst; ++ const u32 *tab = NULL; ++ int i, j, k; ++ ++ tab = cfb_tab32; ++ ++ eorx = fgx ^ bgx; ++ k = image->width; ++ ++ for (i = image->height; i--;) { ++ dst = (u32 __iomem *) dst1; ++ src = s; ++ ++ j = k; ++ while (j >= 8) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 6) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 5) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 4) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 3) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 2) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 1) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[bits & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ src++; ++ j -= 8; ++ } ++ if (j != 0) { ++ u32 bits = (u32) * src; ++ while (j > 1) { ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ bits <<= 1; ++ j--; ++ } ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst); ++ } ++ dst1 += p->fix.line_length; ++ s += spitch; ++ } ++} ++ + void cfb_imageblit(struct fb_info *p, const struct fb_image *image) + { + u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; +@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) + bgcolor = image->bg_color; + } + +- if (32 % bpp == 0 && !start_index && !pitch_index && +- ((width & (32/bpp-1)) == 0) && +- bpp >= 8 && bpp <= 32) +- fast_imageblit(image, p, dst1, fgcolor, bgcolor); +- else ++ if (!start_index && !pitch_index) { ++ if (bpp == 32) ++ fast_imageblit32(image, p, dst1, fgcolor, ++ bgcolor); ++ else if (bpp == 16 && (width & 1) == 0) ++ fast_imageblit16(image, p, dst1, fgcolor, ++ bgcolor); ++ else if (bpp == 8 && (width & 3) == 0) ++ fast_imageblit(image, p, dst1, fgcolor, ++ bgcolor); ++ else ++ slow_imageblit(image, p, dst1, fgcolor, ++ bgcolor, ++ start_index, pitch_index); ++ } else + slow_imageblit(image, p, dst1, fgcolor, bgcolor, + start_index, pitch_index); + } else + +From 4791febd5ba2ec59bdfc92fa0dc3a73119853871 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 17:26:38 +0000 +Subject: [PATCH 026/104] Allow mac address to be set in smsc95xx + +Signed-off-by: popcornmix +--- + drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 56 insertions(+) + +diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c +index 26423ad..e29a323 100644 +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -59,6 +59,7 @@ + #define SUSPEND_SUSPEND3 (0x08) + #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ + SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) ++#define MAC_ADDR_LEN (6) + + struct smsc95xx_priv { + u32 mac_cr; +@@ -74,6 +75,10 @@ static bool turbo_mode = true; + module_param(turbo_mode, bool, 0644); + MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); + ++static char *macaddr = ":"; ++module_param(macaddr, charp, 0); ++MODULE_PARM_DESC(macaddr, "MAC address"); ++ + static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, + u32 *data, int in_pm) + { +@@ -763,8 +768,59 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + } + ++/* Check the macaddr module parameter for a MAC address */ ++static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac) ++{ ++ int i, j, got_num, num; ++ u8 mtbl[MAC_ADDR_LEN]; ++ ++ if (macaddr[0] == ':') ++ return 0; ++ ++ i = 0; ++ j = 0; ++ num = 0; ++ got_num = 0; ++ while (j < MAC_ADDR_LEN) { ++ if (macaddr[i] && macaddr[i] != ':') { ++ got_num++; ++ if ('0' <= macaddr[i] && macaddr[i] <= '9') ++ num = num * 16 + macaddr[i] - '0'; ++ else if ('A' <= macaddr[i] && macaddr[i] <= 'F') ++ num = num * 16 + 10 + macaddr[i] - 'A'; ++ else if ('a' <= macaddr[i] && macaddr[i] <= 'f') ++ num = num * 16 + 10 + macaddr[i] - 'a'; ++ else ++ break; ++ i++; ++ } else if (got_num == 2) { ++ mtbl[j++] = (u8) num; ++ num = 0; ++ got_num = 0; ++ i++; ++ } else { ++ break; ++ } ++ } ++ ++ if (j == MAC_ADDR_LEN) { ++ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: " ++ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2], ++ mtbl[3], mtbl[4], mtbl[5]); ++ for (i = 0; i < MAC_ADDR_LEN; i++) ++ dev_mac[i] = mtbl[i]; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ + static void smsc95xx_init_mac_address(struct usbnet *dev) + { ++ /* Check module parameters */ ++ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) ++ return; ++ + /* try reading mac address from EEPROM */ + if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, + dev->net->dev_addr) == 0) { + +From 7958a3ae050a3d5f88132f2ae0430ecebddfff57 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 8 May 2013 11:46:50 +0100 +Subject: [PATCH 027/104] enabling the realtime clock 1-wire chip DS1307 and + 1-wire on GPIO4 (as a module) + +1-wire: Add support for configuring pin for w1-gpio kernel module +See: https://github.com/raspberrypi/linux/pull/457 + +Add bitbanging pullups, use them for w1-gpio + +Allows parasite power to work, uses module option pullup=1 + +bcm2708: Ensure 1-wire pullup is disabled by default, and expose as module parameter + +Signed-off-by: Alex J Lennon + +w1-gpio: Add gpiopin module parameter and correctly free up gpio pull-up pin, if set + +Signed-off-by: Alex J Lennon +--- + arch/arm/mach-bcm2708/bcm2708.c | 29 ++++++++++++++++++++++ + drivers/w1/masters/w1-gpio.c | 55 +++++++++++++++++++++++++++++++++++++---- + drivers/w1/w1.h | 6 +++++ + drivers/w1/w1_int.c | 14 +++++++++++ + drivers/w1/w1_io.c | 18 +++++++++++--- + 5 files changed, 114 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 496cbcd..5873f8b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -79,12 +80,19 @@ + */ + #define DMA_MASK_BITS_COMMON 32 + ++// use GPIO 4 for the one-wire GPIO pin, if enabled ++#define W1_GPIO 4 ++// ensure one-wire GPIO pullup is disabled by default ++#define W1_PULLUP -1 ++ + /* command line parameters */ + static unsigned boardrev, serial; + static unsigned uart_clock = UART0_CLOCK; + static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; ++static unsigned w1_gpio_pin = W1_GPIO; ++static unsigned w1_gpio_pullup = W1_PULLUP; + + static unsigned use_dt = 0; + +@@ -256,6 +264,20 @@ static struct platform_device bcm2708_dmaengine_device = { + .id = -1, + }; + ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++static struct w1_gpio_platform_data w1_gpio_pdata = { ++ .pin = W1_GPIO, ++ .ext_pullup_enable_pin = W1_PULLUP, ++ .is_open_drain = 0, ++}; ++ ++static struct platform_device w1_device = { ++ .name = "w1-gpio", ++ .id = -1, ++ .dev.platform_data = &w1_gpio_pdata, ++}; ++#endif ++ + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -715,6 +737,11 @@ void __init bcm2708_init(void) + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device(&bcm2708_gpio_device); + #endif ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++ w1_gpio_pdata.pin = w1_gpio_pin; ++ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; ++ bcm_register_device_dt(&w1_device); ++#endif + bcm_register_device(&bcm2708_systemtimer_device); + bcm_register_device(&bcm2708_fb_device); + bcm_register_device(&bcm2708_usb_device); +@@ -925,3 +952,5 @@ module_param(uart_clock, uint, 0644); + module_param(disk_led_gpio, uint, 0644); + module_param(disk_led_active_low, uint, 0644); + module_param(reboot_part, uint, 0644); ++module_param(w1_gpio_pin, uint, 0644); ++module_param(w1_gpio_pullup, uint, 0644); +diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c +index b99a932..3ad15cc 100644 +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -23,6 +23,15 @@ + #include "../w1.h" + #include "../w1_int.h" + ++static int w1_gpio_pullup = -1; ++static int w1_gpio_pullup_orig = -1; ++module_param_named(pullup, w1_gpio_pullup, int, 0); ++MODULE_PARM_DESC(pullup, "GPIO pin pullup number"); ++static int w1_gpio_pin = -1; ++static int w1_gpio_pin_orig = -1; ++module_param_named(gpiopin, w1_gpio_pin, int, 0); ++MODULE_PARM_DESC(gpiopin, "GPIO pin number"); ++ + static u8 w1_gpio_set_pullup(void *data, int delay) + { + struct w1_gpio_platform_data *pdata = data; +@@ -67,6 +76,16 @@ static u8 w1_gpio_read_bit(void *data) + return gpio_get_value(pdata->pin) ? 1 : 0; + } + ++static void w1_gpio_bitbang_pullup(void *data, u8 on) ++{ ++ struct w1_gpio_platform_data *pdata = data; ++ ++ if (on) ++ gpio_direction_output(pdata->pin, 1); ++ else ++ gpio_direction_input(pdata->pin); ++} ++ + #if defined(CONFIG_OF) + static struct of_device_id w1_gpio_dt_ids[] = { + { .compatible = "w1-gpio" }, +@@ -113,13 +132,15 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) + static int w1_gpio_probe(struct platform_device *pdev) + { + struct w1_bus_master *master; +- struct w1_gpio_platform_data *pdata; ++ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + int err; + +- if (of_have_populated_dt()) { +- err = w1_gpio_probe_dt(pdev); +- if (err < 0) +- return err; ++ if(pdata == NULL) { ++ if (of_have_populated_dt()) { ++ err = w1_gpio_probe_dt(pdev); ++ if (err < 0) ++ return err; ++ } + } + + pdata = dev_get_platdata(&pdev->dev); +@@ -136,6 +157,19 @@ static int w1_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ w1_gpio_pin_orig = pdata->pin; ++ w1_gpio_pullup_orig = pdata->ext_pullup_enable_pin; ++ ++ if(gpio_is_valid(w1_gpio_pin)) { ++ pdata->pin = w1_gpio_pin; ++ pdata->ext_pullup_enable_pin = -1; ++ } ++ if(gpio_is_valid(w1_gpio_pullup)) { ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup; ++ } ++ ++ dev_info(&pdev->dev, "gpio pin %d, gpio pullup pin %d\n", pdata->pin, pdata->ext_pullup_enable_pin); ++ + err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); + if (err) { + dev_err(&pdev->dev, "gpio_request (pin) failed\n"); +@@ -165,6 +199,14 @@ static int w1_gpio_probe(struct platform_device *pdev) + master->set_pullup = w1_gpio_set_pullup; + } + ++ if (gpio_is_valid(w1_gpio_pullup)) { ++ if (pdata->is_open_drain) ++ printk(KERN_ERR "w1-gpio 'pullup' option " ++ "doesn't work with open drain GPIO\n"); ++ else ++ master->bitbang_pullup = w1_gpio_bitbang_pullup; ++ } ++ + err = w1_add_master_device(master); + if (err) { + dev_err(&pdev->dev, "w1_add_master device failed\n"); +@@ -195,6 +237,9 @@ static int w1_gpio_remove(struct platform_device *pdev) + + w1_remove_master_device(master); + ++ pdata->pin = w1_gpio_pin_orig; ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup_orig; ++ + return 0; + } + +diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h +index 56a49ba..881d728 100644 +--- a/drivers/w1/w1.h ++++ b/drivers/w1/w1.h +@@ -171,6 +171,12 @@ struct w1_bus_master + + u8 (*set_pullup)(void *, int); + ++ /** ++ * Turns the pullup on/off in bitbanging mode, takes an on/off argument. ++ * @return -1=Error, 0=completed ++ */ ++ void (*bitbang_pullup) (void *, u8); ++ + void (*search)(void *, struct w1_master *, + u8, w1_slave_found_callback); + }; +diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c +index 47249a3..a4b4a8d 100644 +--- a/drivers/w1/w1_int.c ++++ b/drivers/w1/w1_int.c +@@ -123,6 +123,20 @@ int w1_add_master_device(struct w1_bus_master *master) + return(-EINVAL); + } + ++ /* bitbanging hardware uses bitbang_pullup, other hardware uses set_pullup ++ * and takes care of timing itself */ ++ if (!master->write_byte && !master->touch_bit && master->set_pullup) { ++ printk(KERN_ERR "w1_add_master_device: set_pullup requires " ++ "write_byte or touch_bit, disabling\n"); ++ master->set_pullup = NULL; ++ } ++ ++ if (master->set_pullup && master->bitbang_pullup) { ++ printk(KERN_ERR "w1_add_master_device: set_pullup should not " ++ "be set when bitbang_pullup is used, disabling\n"); ++ master->set_pullup = NULL; ++ } ++ + /* Lock until the device is added (or not) to w1_masters. */ + mutex_lock(&w1_mlock); + /* Search for the first available id (starting at 1). */ +diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c +index 2820924..fd0550f 100644 +--- a/drivers/w1/w1_io.c ++++ b/drivers/w1/w1_io.c +@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_master *dev) + static void w1_post_write(struct w1_master *dev) + { + if (dev->pullup_duration) { +- if (dev->enable_pullup && dev->bus_master->set_pullup) +- dev->bus_master->set_pullup(dev->bus_master->data, 0); +- else ++ if (dev->enable_pullup) { ++ if (dev->bus_master->set_pullup) { ++ dev->bus_master->set_pullup(dev-> ++ bus_master->data, ++ 0); ++ } else if (dev->bus_master->bitbang_pullup) { ++ dev->bus_master-> ++ bitbang_pullup(dev->bus_master->data, 1); ++ msleep(dev->pullup_duration); ++ dev->bus_master-> ++ bitbang_pullup(dev->bus_master->data, 0); ++ } ++ } else { + msleep(dev->pullup_duration); ++ } ++ + dev->pullup_duration = 0; + } + } + +From 198cbde7112abbdccf89a1a75e8386c8cba42de9 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Dec 2013 22:16:19 +0000 +Subject: [PATCH 029/104] config: Enable CONFIG_MEMCG, but leave it disabled + (due to memory cost). Enable with cgroup_enable=memory. + +--- + kernel/cgroup.c | 23 +++++++++++++++++++++++ + mm/memcontrol.c | 1 + + 2 files changed, 24 insertions(+) + +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index 29a7b2c..54fd2f0 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -5393,6 +5393,29 @@ static int __init cgroup_disable(char *str) + } + __setup("cgroup_disable=", cgroup_disable); + ++static int __init cgroup_enable(char *str) ++{ ++ struct cgroup_subsys *ss; ++ char *token; ++ int i; ++ ++ while ((token = strsep(&str, ",")) != NULL) { ++ if (!*token) ++ continue; ++ ++ for_each_subsys(ss, i) { ++ if (!strcmp(token, ss->name)) { ++ ss->disabled = 0; ++ printk(KERN_INFO "Enabling %s control group" ++ " subsystem\n", ss->name); ++ break; ++ } ++ } ++ } ++ return 1; ++} ++__setup("cgroup_enable=", cgroup_enable); ++ + static int __init cgroup_set_legacy_files_on_dfl(char *str) + { + printk("cgroup: using legacy files on the default hierarchy\n"); +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index b34ef4a..c2aa348 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -5391,6 +5391,7 @@ struct cgroup_subsys memory_cgrp_subsys = { + .dfl_cftypes = memory_files, + .legacy_cftypes = mem_cgroup_legacy_files, + .early_init = 0, ++ .disabled = 1, + }; + + /** + +From 121bddc28d392117231ba805dbe27e63440f90ac Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:33:38 +0100 +Subject: [PATCH 030/104] ASoC: Add support for BCM2708 + +This driver adds support for digital audio (I2S) +for the BCM2708 SoC that is used by the +Raspberry Pi. External audio codecs can be +connected to the Raspberry Pi via P5 header. + +It relies on cyclic DMA engine support for BCM2708. + +Signed-off-by: Florian Meier + +ASoC: BCM2708: Add 24 bit support + +This adds 24 bit support to the I2S driver of the BCM2708. +Besides enabling the 24 bit flags, it includes two bug fixes: + +MMAP is not supported. Claiming this leads to strange issues +when the format of driver and file do not match. + +The datasheet states that the width extension bit should be set +for widths greater than 24, but greater or equal would be correct. +This follows from the definition of the width field. + +Signed-off-by: Florian Meier + +bcm2708-i2s: Update bclk_ratio to more correct values + +Move GPIO setup to hw_params. + +This is used to stop the I2S driver from breaking +the GPIO setup for other uses of the PCM interface + +Configure GPIOs for I2S based on revision/card settings + +With RPi model B+, assignment of the I2S GPIO pins has changed. +This patch uses the board revision to auto-detect the GPIOs used +for I2S. It also allows sound card drivers to set the GPIOs that +should be used. This is especially important with the Compute +Module. + +bcm2708-i2s: Avoid leak from iomap when accessing gpio + +bcm2708: Eliminate i2s debugfs directory error + +Qualify the two regmap ranges uses by bcm2708-i2s ('-i2s' and '-clk') +to avoid the name clash when registering debugfs entries. +--- + sound/soc/bcm/Kconfig | 11 + + sound/soc/bcm/Makefile | 4 + + sound/soc/bcm/bcm2708-i2s.c | 1009 +++++++++++++++++++++++++++++++++++++++++++ + sound/soc/bcm/bcm2708-i2s.h | 35 ++ + 4 files changed, 1059 insertions(+) + create mode 100644 sound/soc/bcm/bcm2708-i2s.c + create mode 100644 sound/soc/bcm/bcm2708-i2s.h + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 6a834e1..8642296 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -7,3 +7,14 @@ config SND_BCM2835_SOC_I2S + Say Y or M if you want to add support for codecs attached to + the BCM2835 I2S interface. You will also need + to select the audio interfaces to support below. ++ ++config SND_BCM2708_SOC_I2S ++ tristate "SoC Audio support for the Broadcom BCM2708 I2S module" ++ depends on MACH_BCM2708 || MACH_BCM2709 ++ select REGMAP_MMIO ++ select SND_SOC_DMAENGINE_PCM ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ help ++ Say Y or M if you want to add support for codecs attached to ++ the BCM2708 I2S interface. You will also need ++ to select the audio interfaces to support below. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index bc816b7..f8bbe1f 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o + + obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o + ++# BCM2708 Platform Support ++snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o +diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +new file mode 100644 +index 0000000..a3b65dc +--- /dev/null ++++ b/sound/soc/bcm/bcm2708-i2s.c +@@ -0,0 +1,1009 @@ ++/* ++ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * Based on ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor ++ * Vladimir Barinov, ++ * Copyright (C) 2007 MontaVista Software, Inc., ++ * ++ * OMAP ALSA SoC DAI driver using McBSP port ++ * Copyright (C) 2008 Nokia Corporation ++ * Contact: Jarkko Nikula ++ * Peter Ujfalusi ++ * ++ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver ++ * Author: Timur Tabi ++ * Copyright 2007-2010 Freescale Semiconductor, Inc. ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 "bcm2708-i2s.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Clock registers */ ++#define BCM2708_CLK_PCMCTL_REG 0x00 ++#define BCM2708_CLK_PCMDIV_REG 0x04 ++ ++/* Clock register settings */ ++#define BCM2708_CLK_PASSWD (0x5a000000) ++#define BCM2708_CLK_PASSWD_MASK (0xff000000) ++#define BCM2708_CLK_MASH(v) ((v) << 9) ++#define BCM2708_CLK_FLIP BIT(8) ++#define BCM2708_CLK_BUSY BIT(7) ++#define BCM2708_CLK_KILL BIT(5) ++#define BCM2708_CLK_ENAB BIT(4) ++#define BCM2708_CLK_SRC(v) (v) ++ ++#define BCM2708_CLK_SHIFT (12) ++#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) ++#define BCM2708_CLK_DIVF(v) (v) ++#define BCM2708_CLK_DIVF_MASK (0xFFF) ++ ++enum { ++ BCM2708_CLK_MASH_0 = 0, ++ BCM2708_CLK_MASH_1, ++ BCM2708_CLK_MASH_2, ++ BCM2708_CLK_MASH_3, ++}; ++ ++enum { ++ BCM2708_CLK_SRC_GND = 0, ++ BCM2708_CLK_SRC_OSC, ++ BCM2708_CLK_SRC_DBG0, ++ BCM2708_CLK_SRC_DBG1, ++ BCM2708_CLK_SRC_PLLA, ++ BCM2708_CLK_SRC_PLLC, ++ BCM2708_CLK_SRC_PLLD, ++ BCM2708_CLK_SRC_HDMI, ++}; ++ ++/* Most clocks are not useable (freq = 0) */ ++static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { ++ [BCM2708_CLK_SRC_GND] = 0, ++ [BCM2708_CLK_SRC_OSC] = 19200000, ++ [BCM2708_CLK_SRC_DBG0] = 0, ++ [BCM2708_CLK_SRC_DBG1] = 0, ++ [BCM2708_CLK_SRC_PLLA] = 0, ++ [BCM2708_CLK_SRC_PLLC] = 0, ++ [BCM2708_CLK_SRC_PLLD] = 500000000, ++ [BCM2708_CLK_SRC_HDMI] = 0, ++}; ++ ++/* I2S registers */ ++#define BCM2708_I2S_CS_A_REG 0x00 ++#define BCM2708_I2S_FIFO_A_REG 0x04 ++#define BCM2708_I2S_MODE_A_REG 0x08 ++#define BCM2708_I2S_RXC_A_REG 0x0c ++#define BCM2708_I2S_TXC_A_REG 0x10 ++#define BCM2708_I2S_DREQ_A_REG 0x14 ++#define BCM2708_I2S_INTEN_A_REG 0x18 ++#define BCM2708_I2S_INTSTC_A_REG 0x1c ++#define BCM2708_I2S_GRAY_REG 0x20 ++ ++/* I2S register settings */ ++#define BCM2708_I2S_STBY BIT(25) ++#define BCM2708_I2S_SYNC BIT(24) ++#define BCM2708_I2S_RXSEX BIT(23) ++#define BCM2708_I2S_RXF BIT(22) ++#define BCM2708_I2S_TXE BIT(21) ++#define BCM2708_I2S_RXD BIT(20) ++#define BCM2708_I2S_TXD BIT(19) ++#define BCM2708_I2S_RXR BIT(18) ++#define BCM2708_I2S_TXW BIT(17) ++#define BCM2708_I2S_CS_RXERR BIT(16) ++#define BCM2708_I2S_CS_TXERR BIT(15) ++#define BCM2708_I2S_RXSYNC BIT(14) ++#define BCM2708_I2S_TXSYNC BIT(13) ++#define BCM2708_I2S_DMAEN BIT(9) ++#define BCM2708_I2S_RXTHR(v) ((v) << 7) ++#define BCM2708_I2S_TXTHR(v) ((v) << 5) ++#define BCM2708_I2S_RXCLR BIT(4) ++#define BCM2708_I2S_TXCLR BIT(3) ++#define BCM2708_I2S_TXON BIT(2) ++#define BCM2708_I2S_RXON BIT(1) ++#define BCM2708_I2S_EN (1) ++ ++#define BCM2708_I2S_CLKDIS BIT(28) ++#define BCM2708_I2S_PDMN BIT(27) ++#define BCM2708_I2S_PDME BIT(26) ++#define BCM2708_I2S_FRXP BIT(25) ++#define BCM2708_I2S_FTXP BIT(24) ++#define BCM2708_I2S_CLKM BIT(23) ++#define BCM2708_I2S_CLKI BIT(22) ++#define BCM2708_I2S_FSM BIT(21) ++#define BCM2708_I2S_FSI BIT(20) ++#define BCM2708_I2S_FLEN(v) ((v) << 10) ++#define BCM2708_I2S_FSLEN(v) (v) ++ ++#define BCM2708_I2S_CHWEX BIT(15) ++#define BCM2708_I2S_CHEN BIT(14) ++#define BCM2708_I2S_CHPOS(v) ((v) << 4) ++#define BCM2708_I2S_CHWID(v) (v) ++#define BCM2708_I2S_CH1(v) ((v) << 16) ++#define BCM2708_I2S_CH2(v) (v) ++ ++#define BCM2708_I2S_TX_PANIC(v) ((v) << 24) ++#define BCM2708_I2S_RX_PANIC(v) ((v) << 16) ++#define BCM2708_I2S_TX(v) ((v) << 8) ++#define BCM2708_I2S_RX(v) (v) ++ ++#define BCM2708_I2S_INT_RXERR BIT(3) ++#define BCM2708_I2S_INT_TXERR BIT(2) ++#define BCM2708_I2S_INT_RXR BIT(1) ++#define BCM2708_I2S_INT_TXW BIT(0) ++ ++/* I2S DMA interface */ ++#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004 ++#define BCM2708_DMA_DREQ_PCM_TX 2 ++#define BCM2708_DMA_DREQ_PCM_RX 3 ++ ++/* I2S pin configuration */ ++static int bcm2708_i2s_gpio=BCM2708_I2S_GPIO_AUTO; ++ ++/* General device struct */ ++struct bcm2708_i2s_dev { ++ struct device *dev; ++ struct snd_dmaengine_dai_dma_data dma_data[2]; ++ unsigned int fmt; ++ unsigned int bclk_ratio; ++ ++ struct regmap *i2s_regmap; ++ struct regmap *clk_regmap; ++}; ++ ++void bcm2708_i2s_set_gpio(int gpio) { ++ bcm2708_i2s_gpio=gpio; ++} ++EXPORT_SYMBOL(bcm2708_i2s_set_gpio); ++ ++ ++static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev) ++{ ++ /* Start the clock if in master mode */ ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ ++ switch (master) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ case SND_SOC_DAIFMT_CBS_CFM: ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev) ++{ ++ uint32_t clkreg; ++ int timeout = 1000; ++ ++ /* Stop clock */ ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD); ++ ++ /* Wait for the BUSY flag going down */ ++ while (--timeout) { ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ if (!(clkreg & BCM2708_CLK_BUSY)) ++ break; ++ } ++ ++ if (!timeout) { ++ /* KILL the clock */ ++ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); ++ } ++} ++ ++static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev, ++ bool tx, bool rx) ++{ ++ int timeout = 1000; ++ uint32_t syncval; ++ uint32_t csreg; ++ uint32_t i2s_active_state; ++ uint32_t clkreg; ++ uint32_t clk_active_state; ++ uint32_t off; ++ uint32_t clr; ++ ++ off = tx ? BCM2708_I2S_TXON : 0; ++ off |= rx ? BCM2708_I2S_RXON : 0; ++ ++ clr = tx ? BCM2708_I2S_TXCLR : 0; ++ clr |= rx ? BCM2708_I2S_RXCLR : 0; ++ ++ /* Backup the current state */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON); ++ ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ clk_active_state = clkreg & BCM2708_CLK_ENAB; ++ ++ /* Start clock if not running */ ++ if (!clk_active_state) { ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ } ++ ++ /* Stop I2S module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0); ++ ++ /* ++ * Clear the FIFOs ++ * Requires at least 2 PCM clock cycles to take effect ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr); ++ ++ /* Wait for 2 PCM clock cycles */ ++ ++ /* ++ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back ++ * FIXME: This does not seem to work for slave mode! ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval); ++ syncval &= BCM2708_I2S_SYNC; ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_SYNC, ~syncval); ++ ++ /* Wait for the SYNC flag changing it's state */ ++ while (--timeout) { ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ if ((csreg & BCM2708_I2S_SYNC) != syncval) ++ break; ++ } ++ ++ if (!timeout) ++ dev_err(dev->dev, "I2S SYNC error!\n"); ++ ++ /* Stop clock if it was not running before */ ++ if (!clk_active_state) ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Restore I2S state */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state); ++} ++ ++static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai, ++ unsigned int fmt) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->fmt = fmt; ++ return 0; ++} ++ ++static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, ++ unsigned int ratio) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->bclk_ratio = ratio; ++ return 0; ++} ++ ++ ++static int bcm2708_i2s_set_function(unsigned offset, int function) ++{ ++ #define GPIOFSEL(x) (0x00+(x)*4) ++ void __iomem *gpio = __io_address(GPIO_BASE); ++ unsigned alt = function <= 3 ? function + 4: function == 4 ? 3 : 2; ++ unsigned gpiodir; ++ unsigned gpio_bank = offset / 10; ++ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ gpiodir = readl(gpio + GPIOFSEL(gpio_bank)); ++ gpiodir &= ~(7 << gpio_field_offset); ++ gpiodir |= alt << gpio_field_offset; ++ writel(gpiodir, gpio + GPIOFSEL(gpio_bank)); ++ return 0; ++} ++ ++static void bcm2708_i2s_setup_gpio(void) ++{ ++ /* ++ * This is the common way to handle the GPIO pins for ++ * the Raspberry Pi. ++ * TODO Better way would be to handle ++ * this in the device tree! ++ */ ++ int pin,pinconfig,startpin,alt; ++ ++ /* SPI is on different GPIOs on different boards */ ++ /* for Raspberry Pi B+, this is pin GPIO18-21, for original on 28-31 */ ++ if (bcm2708_i2s_gpio==BCM2708_I2S_GPIO_AUTO) { ++ if ((system_rev & 0xffffff) >= 0x10) { ++ /* Model B+ */ ++ pinconfig=BCM2708_I2S_GPIO_PIN18; ++ } else { ++ /* original */ ++ pinconfig=BCM2708_I2S_GPIO_PIN28; ++ } ++ } else { ++ pinconfig=bcm2708_i2s_gpio; ++ } ++ ++ if (pinconfig==BCM2708_I2S_GPIO_PIN18) { ++ startpin=18; ++ alt=BCM2708_I2S_GPIO_PIN18_ALT; ++ } else if (pinconfig==BCM2708_I2S_GPIO_PIN28) { ++ startpin=28; ++ alt=BCM2708_I2S_GPIO_PIN28_ALT; ++ } else { ++ printk(KERN_INFO "Can't configure I2S GPIOs, unknown pin mode for I2S: %i\n",pinconfig); ++ return; ++ } ++ ++ /* configure I2S pins to correct ALT mode */ ++ for (pin = startpin; pin <= startpin+3; pin++) { ++ bcm2708_i2s_set_function(pin, alt); ++ } ++} ++ ++static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ unsigned int sampling_rate = params_rate(params); ++ unsigned int data_length, data_delay, bclk_ratio; ++ unsigned int ch1pos, ch2pos, mode, format; ++ unsigned int mash = BCM2708_CLK_MASH_1; ++ unsigned int divi, divf, target_frequency; ++ int clk_src = -1; ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBS_CFM); ++ ++ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBM_CFS); ++ uint32_t csreg; ++ ++ /* ++ * If a stream is already enabled, ++ * the registers are already set properly. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ ++ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON)) ++ return 0; ++ ++ ++ bcm2708_i2s_setup_gpio(); ++ ++ /* ++ * Adjust the data length according to the format. ++ * We prefill the half frame length with an integer ++ * divider of 2400 as explained at the clock settings. ++ * Maybe it is overwritten there, if the Integer mode ++ * does not apply. ++ */ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ data_length = 16; ++ bclk_ratio = 50; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ data_length = 24; ++ bclk_ratio = 50; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ data_length = 32; ++ bclk_ratio = 100; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* If bclk_ratio already set, use that one. */ ++ if (dev->bclk_ratio) ++ bclk_ratio = dev->bclk_ratio; ++ ++ /* ++ * Clock Settings ++ * ++ * The target frequency of the bit clock is ++ * sampling rate * frame length ++ * ++ * Integer mode: ++ * Sampling rates that are multiples of 8000 kHz ++ * can be driven by the oscillator of 19.2 MHz ++ * with an integer divider as long as the frame length ++ * is an integer divider of 19200000/8000=2400 as set up above. ++ * This is no longer possible if the sampling rate ++ * is too high (e.g. 192 kHz), because the oscillator is too slow. ++ * ++ * MASH mode: ++ * For all other sampling rates, it is not possible to ++ * have an integer divider. Approximate the clock ++ * with the MASH module that induces a slight frequency ++ * variance. To minimize that it is best to have the fastest ++ * clock here. That is PLLD with 500 MHz. ++ */ ++ target_frequency = sampling_rate * bclk_ratio; ++ clk_src = BCM2708_CLK_SRC_OSC; ++ mash = BCM2708_CLK_MASH_0; ++ ++ if (bcm2708_clk_freq[clk_src] % target_frequency == 0 ++ && bit_master && frame_master) { ++ divi = bcm2708_clk_freq[clk_src] / target_frequency; ++ divf = 0; ++ } else { ++ uint64_t dividend; ++ ++ if (!dev->bclk_ratio) { ++ /* ++ * Overwrite bclk_ratio, because the ++ * above trick is not needed or can ++ * not be used. ++ */ ++ bclk_ratio = 2 * data_length; ++ } ++ ++ target_frequency = sampling_rate * bclk_ratio; ++ ++ clk_src = BCM2708_CLK_SRC_PLLD; ++ mash = BCM2708_CLK_MASH_1; ++ ++ dividend = bcm2708_clk_freq[clk_src]; ++ dividend <<= BCM2708_CLK_SHIFT; ++ do_div(dividend, target_frequency); ++ divi = dividend >> BCM2708_CLK_SHIFT; ++ divf = dividend & BCM2708_CLK_DIVF_MASK; ++ } ++ ++ /* Clock should only be set up here if CPU is clock master */ ++ if (((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS) || ++ ((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFM)) { ++ /* Set clock divider */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_DIVI(divi) ++ | BCM2708_CLK_DIVF(divf)); ++ ++ /* Setup clock, but don't start it yet */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_MASH(mash) ++ | BCM2708_CLK_SRC(clk_src)); ++ } ++ ++ /* Setup the frame format */ ++ format = BCM2708_I2S_CHEN; ++ ++ if (data_length >= 24) ++ format |= BCM2708_I2S_CHWEX; ++ ++ format |= BCM2708_I2S_CHWID((data_length-8)&0xf); ++ ++ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ data_delay = 1; ++ break; ++ default: ++ /* ++ * TODO ++ * Others are possible but are not implemented at the moment. ++ */ ++ dev_err(dev->dev, "%s:bad format\n", __func__); ++ return -EINVAL; ++ } ++ ++ ch1pos = data_delay; ++ ch2pos = bclk_ratio / 2 + data_delay; ++ ++ switch (params_channels(params)) { ++ case 2: ++ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format); ++ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos)); ++ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * Set format for both streams. ++ * We cannot set another frame length ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format); ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format); ++ ++ /* Setup the I2S mode */ ++ mode = 0; ++ ++ if (data_length <= 16) { ++ /* ++ * Use frame packed mode (2 channels per 32 bit word) ++ * We cannot set another frame length in the second stream ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP; ++ } ++ ++ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1); ++ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2); ++ ++ /* Master or slave? */ ++ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* CPU is master */ ++ break; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ /* ++ * CODEC is bit clock master ++ * CPU is frame master ++ */ ++ mode |= BCM2708_I2S_CLKM; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFM: ++ /* ++ * CODEC is frame master ++ * CPU is bit clock master ++ */ ++ mode |= BCM2708_I2S_FSM; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ /* CODEC is master */ ++ mode |= BCM2708_I2S_CLKM; ++ mode |= BCM2708_I2S_FSM; ++ break; ++ default: ++ dev_err(dev->dev, "%s:bad master\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Invert clocks? ++ * ++ * The BCM approach seems to be inverted to the classical I2S approach. ++ */ ++ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ /* None. Therefore, both for BCM */ ++ mode |= BCM2708_I2S_CLKI; ++ mode |= BCM2708_I2S_FSI; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ /* Both. Therefore, none for BCM */ ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ /* ++ * Invert only frame sync. Therefore, ++ * invert only bit clock for BCM ++ */ ++ mode |= BCM2708_I2S_CLKI; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ /* ++ * Invert only bit clock. Therefore, ++ * invert only frame sync for BCM ++ */ ++ mode |= BCM2708_I2S_FSI; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode); ++ ++ /* Setup the DMA parameters */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXTHR(1) ++ | BCM2708_I2S_TXTHR(1) ++ | BCM2708_I2S_DMAEN, 0xffffffff); ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG, ++ BCM2708_I2S_TX_PANIC(0x10) ++ | BCM2708_I2S_RX_PANIC(0x30) ++ | BCM2708_I2S_TX(0x30) ++ | BCM2708_I2S_RX(0x20), 0xffffffff); ++ ++ /* Clear FIFOs */ ++ bcm2708_i2s_clear_fifos(dev, true, true); ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t cs_reg; ++ ++ bcm2708_i2s_start_clock(dev); ++ ++ /* ++ * Clear both FIFOs if the one that should be started ++ * is not empty at the moment. This should only happen ++ * after overrun. Otherwise, hw_params would have cleared ++ * the FIFO. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ++ && !(cs_reg & BCM2708_I2S_TXE)) ++ bcm2708_i2s_clear_fifos(dev, true, false); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE ++ && (cs_reg & BCM2708_I2S_RXD)) ++ bcm2708_i2s_clear_fifos(dev, false, true); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev, ++ struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ uint32_t mask; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, 0); ++ ++ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ ++ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT)) ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t mask; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ bcm2708_i2s_start_clock(dev); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, mask); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ bcm2708_i2s_stop(dev, substream, dai); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ if (dai->active) ++ return 0; ++ ++ /* Should this still be running stop it */ ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Enable PCM block */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, BCM2708_I2S_EN); ++ ++ /* ++ * Disable STBY. ++ * Requires at least 4 PCM clock cycles to take effect. ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_STBY, BCM2708_I2S_STBY); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ bcm2708_i2s_stop(dev, substream, dai); ++ ++ /* If both streams are stopped, disable module and clock */ ++ if (dai->active) ++ return; ++ ++ /* Disable the module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, 0); ++ ++ /* ++ * Stopping clock is necessary, because stop does ++ * not stop the clock when SND_SOC_DAIFMT_CONT ++ */ ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = { ++ .startup = bcm2708_i2s_startup, ++ .shutdown = bcm2708_i2s_shutdown, ++ .prepare = bcm2708_i2s_prepare, ++ .trigger = bcm2708_i2s_trigger, ++ .hw_params = bcm2708_i2s_hw_params, ++ .set_fmt = bcm2708_i2s_set_dai_fmt, ++ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio ++}; ++ ++static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; ++ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ return 0; ++} ++ ++static struct snd_soc_dai_driver bcm2708_i2s_dai = { ++ .name = "bcm2708-i2s", ++ .probe = bcm2708_i2s_dai_probe, ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .capture = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .ops = &bcm2708_i2s_dai_ops, ++ .symmetric_rates = 1 ++}; ++ ++static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_CS_A_REG: ++ case BCM2708_I2S_FIFO_A_REG: ++ case BCM2708_I2S_INTSTC_A_REG: ++ case BCM2708_I2S_GRAY_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_FIFO_A_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_CLK_PCMCTL_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static const struct regmap_config bcm2708_regmap_config[] = { ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_I2S_GRAY_REG, ++ .precious_reg = bcm2708_i2s_precious_reg, ++ .volatile_reg = bcm2708_i2s_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ .name = "i2s", ++ }, ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_CLK_PCMDIV_REG, ++ .volatile_reg = bcm2708_clk_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ .name = "clk", ++ }, ++}; ++ ++static const struct snd_soc_component_driver bcm2708_i2s_component = { ++ .name = "bcm2708-i2s-comp", ++}; ++ ++static const struct snd_pcm_hardware bcm2708_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_JOINT_DUPLEX, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .period_bytes_min = 32, ++ .period_bytes_max = 64 * PAGE_SIZE, ++ .periods_min = 2, ++ .periods_max = 255, ++ .buffer_bytes_max = 128 * PAGE_SIZE, ++}; ++ ++static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &bcm2708_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, ++}; ++ ++ ++static int bcm2708_i2s_probe(struct platform_device *pdev) ++{ ++ struct bcm2708_i2s_dev *dev; ++ int i; ++ int ret; ++ struct regmap *regmap[2]; ++ struct resource *mem[2]; ++ ++ /* Request both ioareas */ ++ for (i = 0; i <= 1; i++) { ++ void __iomem *base; ++ ++ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ base = devm_ioremap_resource(&pdev->dev, mem[i]); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, ++ &bcm2708_regmap_config[i]); ++ if (IS_ERR(regmap[i])) { ++ dev_err(&pdev->dev, "I2S probe: regmap init failed\n"); ++ return PTR_ERR(regmap[i]); ++ } ++ } ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), ++ GFP_KERNEL); ++ if (IS_ERR(dev)) ++ return PTR_ERR(dev); ++ ++ dev->i2s_regmap = regmap[0]; ++ dev->clk_regmap = regmap[1]; ++ ++ /* Set the DMA address */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ /* Set the DREQ */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id = ++ BCM2708_DMA_DREQ_PCM_TX; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id = ++ BCM2708_DMA_DREQ_PCM_RX; ++ ++ /* Set the bus width */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ ++ /* Set burst */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; ++ ++ /* BCLK ratio - use default */ ++ dev->bclk_ratio = 0; ++ ++ /* Store the pdev */ ++ dev->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, dev); ++ ++ ret = snd_soc_register_component(&pdev->dev, ++ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = snd_dmaengine_pcm_register(&pdev->dev, ++ &bcm2708_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); ++ snd_soc_unregister_component(&pdev->dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_remove(struct platform_device *pdev) ++{ ++ snd_dmaengine_pcm_unregister(&pdev->dev); ++ snd_soc_unregister_component(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id bcm2708_i2s_of_match[] = { ++ { .compatible = "brcm,bcm2708-i2s", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_i2s_of_match); ++ ++static struct platform_driver bcm2708_i2s_driver = { ++ .probe = bcm2708_i2s_probe, ++ .remove = bcm2708_i2s_remove, ++ .driver = { ++ .name = "bcm2708-i2s", ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2708_i2s_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2708_i2s_driver); ++ ++MODULE_ALIAS("platform:bcm2708-i2s"); ++MODULE_DESCRIPTION("BCM2708 I2S interface"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/bcm/bcm2708-i2s.h b/sound/soc/bcm/bcm2708-i2s.h +new file mode 100644 +index 0000000..6fdcbc1 +--- /dev/null ++++ b/sound/soc/bcm/bcm2708-i2s.h +@@ -0,0 +1,35 @@ ++/* ++ * I2S configuration for sound cards. ++ * ++ * Copyright (c) 2014 Daniel Matuschek ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef BCM2708_I2S_H ++#define BCM2708_I2S_H ++ ++/* I2S pin assignment */ ++#define BCM2708_I2S_GPIO_AUTO 0 ++#define BCM2708_I2S_GPIO_PIN18 1 ++#define BCM2708_I2S_GPIO_PIN28 2 ++ ++/* Alt mode to enable I2S */ ++#define BCM2708_I2S_GPIO_PIN18_ALT 0 ++#define BCM2708_I2S_GPIO_PIN28_ALT 2 ++ ++extern void bcm2708_i2s_set_gpio(int gpio); ++ ++#endif + +From 1f20015a049141afa162c6ce9d6349c17ca71f02 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:59:51 +0100 +Subject: [PATCH 031/104] ASoC: Add support for PCM5102A codec + +Some definitions to support the PCM5102A codec +by Texas Instruments. + +Signed-off-by: Florian Meier +--- + sound/soc/codecs/Kconfig | 4 +++ + sound/soc/codecs/Makefile | 2 ++ + sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 69 insertions(+) + create mode 100644 sound/soc/codecs/pcm5102a.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index ea9f0e3..bc39bbc 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM512x_I2C if I2C + select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C ++ select SND_SOC_PCM5102A if I2C + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C + select SND_SOC_RT5645 if I2C +@@ -506,6 +507,9 @@ config SND_SOC_RT286 + tristate + depends on I2C + ++config SND_SOC_PCM5102A ++ tristate ++ + config SND_SOC_RT5631 + tristate "Realtek ALC5631/RT5631 CODEC" + depends on I2C +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 69b8666..905ef6f 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -77,6 +77,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o + snd-soc-rl6231-objs := rl6231.o + snd-soc-rt286-objs := rt286.o ++snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o + snd-soc-rt5645-objs := rt5645.o +@@ -259,6 +260,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o ++obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o + obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o +diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c +new file mode 100644 +index 0000000..126f1e9 +--- /dev/null ++++ b/sound/soc/codecs/pcm5102a.c +@@ -0,0 +1,63 @@ ++/* ++ * Driver for the PCM5102A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static struct snd_soc_dai_driver pcm5102a_dai = { ++ .name = "pcm5102a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; ++ ++static int pcm5102a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, ++ &pcm5102a_dai, 1); ++} ++ ++static int pcm5102a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm5102a_codec_driver = { ++ .probe = pcm5102a_probe, ++ .remove = pcm5102a_remove, ++ .driver = { ++ .name = "pcm5102a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm5102a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); + +From 3b3a18623f4eb40c0cf34d738edecb8ee5f0d9f0 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:04:54 +0100 +Subject: [PATCH 032/104] BCM2708: Add I2S support to board file + +Adds the required initializations for I2S +to the board file of mach-bcm2708. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 5873f8b..7f4ebf4 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -613,6 +613,28 @@ static struct platform_device bcm2835_thermal_device = { + .name = "bcm2835_thermal", + }; + ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++static struct resource bcm2708_i2s_resources[] = { ++ { ++ .start = I2S_BASE, ++ .end = I2S_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PCM_CLOCK_BASE, ++ .end = PCM_CLOCK_BASE + 0x02, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_i2s_device = { ++ .name = "bcm2708-i2s", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), ++ .resource = bcm2708_i2s_resources, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -762,6 +784,10 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); + ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++ bcm_register_device_dt(&bcm2708_i2s_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + +From e4249088ec01bb083925564bcaf624c64af34e9a Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:19:08 +0100 +Subject: [PATCH 033/104] ASoC: Add support for HifiBerry DAC + +This adds a machine driver for the HifiBerry DAC. +It is a sound card that can +be stacked onto the Raspberry Pi. + +Signed-off-by: Florian Meier +--- + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 5 +++ + sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 112 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_dac.c + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 8642296..a92adb2 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -18,3 +18,10 @@ config SND_BCM2708_SOC_I2S + Say Y or M if you want to add support for codecs attached to + the BCM2708 I2S interface. You will also need + to select the audio interfaces to support below. ++ ++config SND_BCM2708_SOC_HIFIBERRY_DAC ++ tristate "Support for HifiBerry DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM5102A ++ help ++ Say Y or M if you want to add support for HifiBerry DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index f8bbe1f..be90a49cb 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -7,3 +7,8 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o + snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o + + obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o ++ ++# BCM2708 Machine Support ++snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o +diff --git a/sound/soc/bcm/hifiberry_dac.c b/sound/soc/bcm/hifiberry_dac.c +new file mode 100644 +index 0000000..4b70b45 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_dac.c +@@ -0,0 +1,100 @@ ++/* ++ * ASoC Driver for HifiBerry DAC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ unsigned int sample_bits = ++ snd_pcm_format_physical_width(params_format(params)); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = { ++ .hw_params = snd_rpi_hifiberry_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = { ++{ ++ .name = "HifiBerry DAC", ++ .stream_name = "HifiBerry DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm5102a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm5102a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_hifiberry_dac_ops, ++ .init = snd_rpi_hifiberry_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_dac = { ++ .name = "snd_rpi_hifiberry_dac", ++ .dai_link = snd_rpi_hifiberry_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), ++}; ++ ++static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac); ++} ++ ++static struct platform_driver snd_rpi_hifiberry_dac_driver = { ++ .driver = { ++ .name = "snd-hifiberry-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_hifiberry_dac_probe, ++ .remove = snd_rpi_hifiberry_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC"); ++MODULE_LICENSE("GPL v2"); + +From 160300e5f0194502f368d076e937d7d586ddaadd Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:21:34 +0100 +Subject: [PATCH 034/104] BCM2708: Add HifiBerry DAC to board file + +This adds the initalization of the HifiBerry DAC +to the mach-bcm2708 board file. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 7f4ebf4..94ce8fc 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -635,6 +635,20 @@ static struct platform_device bcm2708_i2s_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++static struct platform_device snd_hifiberry_dac_device = { ++ .name = "snd-hifiberry-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm5102a_codec_device = { ++ .name = "pcm5102a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -788,6 +802,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&bcm2708_i2s_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_dac_device); ++ bcm_register_device_dt(&snd_pcm5102a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + +From e6aa7036b6165cd7bd01501948ebdf8d5ad480be Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 6 Dec 2013 20:50:28 +0100 +Subject: [PATCH 035/104] ASoC: BCM2708: Add support for RPi-DAC + +This adds a machine driver for the RPi-DAC. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++ + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/Kconfig | 4 ++ + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/pcm1794a.c | 62 ++++++++++++++++++++++++++ + 7 files changed, 193 insertions(+) + create mode 100644 sound/soc/bcm/rpi-dac.c + create mode 100644 sound/soc/codecs/pcm1794a.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 94ce8fc..fb955755 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -649,6 +649,20 @@ static struct platform_device snd_pcm5102a_codec_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++static struct platform_device snd_rpi_dac_device = { ++ .name = "snd-rpi-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm1794a_codec_device = { ++ .name = "pcm1794a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -807,6 +821,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_dac_device); ++ bcm_register_device_dt(&snd_pcm1794a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index a92adb2..64c61ac 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -25,3 +25,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC + select SND_SOC_PCM5102A + help + Say Y or M if you want to add support for HifiBerry DAC. ++ ++config SND_BCM2708_SOC_RPI_DAC ++ tristate "Support for RPi-DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM1794A ++ help ++ Say Y or M if you want to add support for RPi-DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index be90a49cb..ccc9809 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -10,5 +10,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-rpi-dac-objs := rpi-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o +diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c +new file mode 100644 +index 0000000..6d6e0ba +--- /dev/null ++++ b/sound/soc/bcm/rpi-dac.c +@@ -0,0 +1,97 @@ ++/* ++ * ASoC Driver for RPi-DAC. ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_rpi_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_rpi_dac_ops = { ++ .hw_params = snd_rpi_rpi_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = { ++{ ++ .name = "RPi-DAC", ++ .stream_name = "RPi-DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm1794a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm1794a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_rpi_dac_ops, ++ .init = snd_rpi_rpi_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_rpi_dac = { ++ .name = "snd_rpi_rpi_dac", ++ .dai_link = snd_rpi_rpi_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), ++}; ++ ++static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_rpi_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_rpi_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_rpi_dac); ++} ++ ++static struct platform_driver snd_rpi_rpi_dac_driver = { ++ .driver = { ++ .name = "snd-rpi-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_rpi_dac_probe, ++ .remove = snd_rpi_rpi_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_rpi_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index bc39bbc..8682bcf 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -83,6 +83,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C + select SND_SOC_PCM5102A if I2C ++ select SND_SOC_PCM1794A if I2C + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C + select SND_SOC_RT5645 if I2C +@@ -507,6 +508,9 @@ config SND_SOC_RT286 + tristate + depends on I2C + ++config SND_SOC_PCM1794A ++ tristate ++ + config SND_SOC_PCM5102A + tristate + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 905ef6f..c9bceba 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -77,6 +77,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o + snd-soc-rl6231-objs := rl6231.o + snd-soc-rt286-objs := rt286.o ++snd-soc-pcm1794a-objs := pcm1794a.o + snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o +@@ -260,6 +261,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o ++obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o + obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c +new file mode 100644 +index 0000000..b4eaa44 +--- /dev/null ++++ b/sound/soc/codecs/pcm1794a.c +@@ -0,0 +1,62 @@ ++/* ++ * Driver for the PCM1794A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static struct snd_soc_dai_driver pcm1794a_dai = { ++ .name = "pcm1794a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm1794a; ++ ++static int pcm1794a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a, ++ &pcm1794a_dai, 1); ++} ++ ++static int pcm1794a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm1794a_codec_driver = { ++ .probe = pcm1794a_probe, ++ .remove = pcm1794a_remove, ++ .driver = { ++ .name = "pcm1794a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm1794a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); + +From d9a3158a7ddd3fdd03e61ca26ed228da5a7090d7 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:41:23 +0100 +Subject: [PATCH 036/104] ASoC: wm8804: Implement MCLK configuration options, + add 32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs + for most sample rates. At 192kHz only 128xfs is supported. The existing + driver selects 128xfs automatically for some lower samples rates. By using an + additional mclk_div divider, it is now possible to control the behaviour. + This allows using 256xfs PLL frequency on all sample rates up to 96kHz. It + should allow lower jitter and better signal quality. The behavior has to be + controlled by the sound card driver, because some sample frequency share the + same setting. e.g. 192kHz and 96kHz use 24.576MHz master clock. The only + difference is the MCLK divider. + +This also added support for 32bit data. + +Signed-off-by: Daniel Matuschek +--- + sound/soc/codecs/wm8804.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c +index b2b0e68..ba2a0f8 100644 +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -278,6 +278,7 @@ static int wm8804_hw_params(struct snd_pcm_substream *substream, + blen = 0x1; + break; + case 24: ++ case 32: + blen = 0x2; + break; + default: +@@ -621,7 +622,7 @@ static const struct snd_soc_dai_ops wm8804_dai_ops = { + }; + + #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ +- SNDRV_PCM_FMTBIT_S24_LE) ++ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + + #define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + +From 6bc408185c9f768523ad5eb0c4119149e4e31ef4 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:42:08 +0100 +Subject: [PATCH 037/104] ASoC: BCM:Add support for HiFiBerry Digi. Driver is + based on the patched WM8804 driver. + +Signed-off-by: Daniel Matuschek +--- + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_digi.c | 153 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 162 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_digi.c + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 64c61ac..d4a00fc 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC + help + Say Y or M if you want to add support for HifiBerry DAC. + ++config SND_BCM2708_SOC_HIFIBERRY_DIGI ++ tristate "Support for HifiBerry Digi" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_WM8804 ++ help ++ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. ++ + config SND_BCM2708_SOC_RPI_DAC + tristate "Support for RPi-DAC" + depends on SND_BCM2708_SOC_I2S +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index ccc9809..826df7d 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -10,7 +10,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-rpi-dac-objs := rpi-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o +diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c +new file mode 100644 +index 0000000..446ecdd +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_digi.c +@@ -0,0 +1,153 @@ ++/* ++ * ASoC Driver for HifiBerry Digi ++ * ++ * Author: Daniel Matuschek ++ * based on the HifiBerry DAC driver by Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++#include "../codecs/wm8804.h" ++ ++static int samplerate=44100; ++ ++static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_codec *codec = rtd->codec; ++ ++ /* enable TX output */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); ++ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *codec_dai = rtd->codec_dai; ++ struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ int sysclk = 27000000; /* This is fixed on this board */ ++ ++ long mclk_freq=0; ++ int mclk_div=1; ++ ++ int ret; ++ ++ samplerate = params_rate(params); ++ ++ switch (samplerate) { ++ case 44100: ++ case 48000: ++ case 88200: ++ case 96000: ++ mclk_freq=samplerate*256; ++ mclk_div=WM8804_MCLKDIV_256FS; ++ break; ++ case 176400: ++ case 192000: ++ mclk_freq=samplerate*128; ++ mclk_div=WM8804_MCLKDIV_128FS; ++ break; ++ default: ++ dev_err(codec->dev, ++ "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); ++ } ++ ++ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); ++ snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); ++ ++ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, ++ sysclk, SND_SOC_CLOCK_OUT); ++ if (ret < 0) { ++ dev_err(codec->dev, ++ "Failed to set WM8804 SYSCLK: %d\n", ret); ++ return ret; ++ } ++ ++ /* Enable TX output */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); ++ ++ /* Power on */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai,64); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { ++ .hw_params = snd_rpi_hifiberry_digi_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { ++{ ++ .name = "HifiBerry Digi", ++ .stream_name = "HifiBerry Digi HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "wm8804-spdif", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "wm8804.1-003b", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM, ++ .ops = &snd_rpi_hifiberry_digi_ops, ++ .init = snd_rpi_hifiberry_digi_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_digi = { ++ .name = "snd_rpi_hifiberry_digi", ++ .dai_link = snd_rpi_hifiberry_digi_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), ++}; ++ ++static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_digi.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_hifiberry_digi); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_digi); ++} ++ ++static struct platform_driver snd_rpi_hifiberry_digi_driver = { ++ .driver = { ++ .name = "snd-hifiberry-digi", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_hifiberry_digi_probe, ++ .remove = snd_rpi_hifiberry_digi_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_digi_driver); ++ ++MODULE_AUTHOR("Daniel Matuschek "); ++MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi"); ++MODULE_LICENSE("GPL v2"); + +From 17fa062cde886c00b02d56b8500a7972123d7975 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Thu, 16 Jan 2014 07:26:08 +0100 +Subject: [PATCH 038/104] BCM2708: Added support for HiFiBerry Digi board Board + initalization by I2C + +Signed-off-by: Daniel Matuschek +--- + arch/arm/mach-bcm2708/bcm2708.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index fb955755..0ad5343 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -649,6 +649,21 @@ static struct platform_device snd_pcm5102a_codec_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++static struct platform_device snd_hifiberry_digi_device = { ++ .name = "snd-hifiberry-digi", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("wm8804", 0x3b) ++ }, ++}; ++ ++#endif ++ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) + static struct platform_device snd_rpi_dac_device = { + .name = "snd-rpi-dac", +@@ -821,6 +836,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_digi_device); ++ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); ++#endif ++ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) + bcm_register_device_dt(&snd_rpi_dac_device); + bcm_register_device_dt(&snd_pcm1794a_codec_device); + +From 0817352c2cfeab1a7e8b29be57010e2ebfa868bc Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Thu, 16 Jan 2014 07:36:35 +0100 +Subject: [PATCH 039/104] ASoC: wm8804: Set idle_bias_off to false Idle bias + has been change to remove warning on driver startup + +Signed-off-by: Daniel Matuschek +--- + sound/soc/codecs/wm8804.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c +index ba2a0f8..91974f9 100644 +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -653,7 +653,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { + .probe = wm8804_probe, + .remove = wm8804_remove, + .set_bias_level = wm8804_set_bias_level, +- .idle_bias_off = true, ++ .idle_bias_off = false, + + .controls = wm8804_snd_controls, + .num_controls = ARRAY_SIZE(wm8804_snd_controls), + +From 146fd03395540ee2be74c216063d44375adf750a Mon Sep 17 00:00:00 2001 +From: Gordon Garrity +Date: Sat, 8 Mar 2014 16:56:57 +0000 +Subject: [PATCH 040/104] Add IQaudIO Sound Card support for Raspberry Pi + +--- + arch/arm/mach-bcm2708/bcm2708.c | 22 ++++++++ + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/iqaudio-dac.c | 111 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 142 insertions(+) + create mode 100644 sound/soc/bcm/iqaudio-dac.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 0ad5343..a812b51 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -678,6 +678,22 @@ static struct platform_device snd_pcm1794a_codec_device = { + }; + #endif + ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++static struct platform_device snd_rpi_iqaudio_dac_device = { ++ .name = "snd-rpi-iqaudio-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++// Use the actual device name rather than generic driver name ++static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4c) ++ }, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -846,6 +862,12 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm1794a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); ++#endif ++ ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index d4a00fc..faa89ef 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -39,3 +39,10 @@ config SND_BCM2708_SOC_RPI_DAC + select SND_SOC_PCM1794A + help + Say Y or M if you want to add support for RPi-DAC. ++ ++config SND_BCM2708_SOC_IQAUDIO_DAC ++ tristate "Support for IQaudIO-DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM512x_I2C ++ help ++ Say Y or M if you want to add support for IQaudIO-DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 826df7d..d597fb0 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -12,7 +12,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + snd-soc-hifiberry-dac-objs := hifiberry_dac.o + snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-rpi-dac-objs := rpi-dac.o ++snd-soc-iqaudio-dac-objs := iqaudio-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o +diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c +new file mode 100644 +index 0000000..8d0e2ae +--- /dev/null ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -0,0 +1,111 @@ ++/* ++ * ASoC Driver for IQaudIO DAC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ ++ return 0; ++} ++ ++static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai; ++// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ unsigned int sample_bits = ++ snd_pcm_format_physical_width(params_format(params)); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = { ++ .hw_params = snd_rpi_iqaudio_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = { ++{ ++ .name = "IQaudIO DAC", ++ .stream_name = "IQaudIO DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm512x-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm512x.1-004c", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_iqaudio_dac_ops, ++ .init = snd_rpi_iqaudio_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_iqaudio_dac = { ++ .name = "IQaudIODAC", ++ .dai_link = snd_rpi_iqaudio_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), ++}; ++ ++static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_iqaudio_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); ++ if (ret) ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac); ++} ++ ++static const struct of_device_id iqaudio_of_match[] = { ++ { .compatible = "iqaudio,iqaudio-dac", }, ++ {}, ++}; ++ ++static struct platform_driver snd_rpi_iqaudio_dac_driver = { ++ .driver = { ++ .name = "snd-rpi-iqaudio-dac", ++ .owner = THIS_MODULE, ++ .of_match_table = iqaudio_of_match, ++ }, ++ .probe = snd_rpi_iqaudio_dac_probe, ++ .remove = snd_rpi_iqaudio_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_iqaudio_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC"); ++MODULE_LICENSE("GPL v2"); + +From 27d378a18a7cbf89d874b933d7c37005f5f4ff60 Mon Sep 17 00:00:00 2001 +From: Howard Mitchell +Date: Mon, 2 Mar 2015 17:28:02 +0000 +Subject: [PATCH 041/104] Set a limit of 0dB on Digital Volume Control + +The main volume control in the PCM512x DAC has a range up to ++24dB. This is dangerously loud and can potentially cause massive +clipping in the output stages. Therefore this sets a sensible +limit of 0dB for this control. +--- + sound/soc/bcm/iqaudio-dac.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c +index 8d0e2ae..aff7377 100644 +--- a/sound/soc/bcm/iqaudio-dac.c ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -25,7 +25,13 @@ + + static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) + { +-// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ struct snd_soc_codec *codec = rtd->codec; ++ ++ ret = snd_soc_limit_volume(codec, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); + + return 0; + } + +From d49a92454b9672d0e36893634f9b3fdb6c5defc3 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Jun 2014 13:42:01 +0100 +Subject: [PATCH 042/104] vmstat: Workaround for issue where dirty page count + goes negative + +See: +https://github.com/raspberrypi/linux/issues/617 +http://www.spinics.net/lists/linux-mm/msg72236.html +--- + include/linux/vmstat.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h +index 82e7db7..f87d16d 100644 +--- a/include/linux/vmstat.h ++++ b/include/linux/vmstat.h +@@ -241,7 +241,11 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item) + static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) + { + atomic_long_dec(&zone->vm_stat[item]); ++ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&zone->vm_stat[item]) < 0)) ++ atomic_long_set(&zone->vm_stat[item], 0); + atomic_long_dec(&vm_stat[item]); ++ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&vm_stat[item]) < 0)) ++ atomic_long_set(&vm_stat[item], 0); + } + + static inline void __inc_zone_page_state(struct page *page, + +From 458e226338d78fbbbd88c1fe25c29d8bb3ad31b5 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 18:55:53 +0100 +Subject: [PATCH 043/104] BCM2708: armctrl: Add IRQ Device Tree support + +Add Device Tree IRQ support for BCM2708. +Usage is the same as for irq-bcm2835. +See binding document: brcm,bcm2835-armctrl-ic.txt + +A bank 3 is added to handle GPIO interrupts. This is done because +armctrl also handles GPIO interrupts. + +Signed-off-by: Noralf Tronnes + +BCM2708: armctrl: remove irq bank 3 + +irq bank 3 was needed by the pinctrl-bcm2708 and bcm2708_gpio +combination. It is no longer required. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/mach-bcm2708/armctrl.c | 96 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c +index 96fa9b9..74bacb3 100644 +--- a/arch/arm/mach-bcm2708/armctrl.c ++++ b/arch/arm/mach-bcm2708/armctrl.c +@@ -23,6 +23,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -79,6 +81,99 @@ static void armctrl_unmask_irq(struct irq_data *d) + } + } + ++#ifdef CONFIG_OF ++ ++#define NR_IRQS_BANK0 21 ++#define NR_BANKS 3 ++#define IRQS_PER_BANK 32 ++ ++/* from drivers/irqchip/irq-bcm2835.c */ ++static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, ++ const u32 *intspec, unsigned int intsize, ++ unsigned long *out_hwirq, unsigned int *out_type) ++{ ++ if (WARN_ON(intsize != 2)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] >= NR_BANKS)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) ++ return -EINVAL; ++ ++ if (intspec[0] == 0) ++ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; ++ else if (intspec[0] == 1) ++ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; ++ else ++ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; ++ ++ /* reverse remap_irqs[] */ ++ switch (*out_hwirq) { ++ case INTERRUPT_VC_JPEG: ++ *out_hwirq = INTERRUPT_JPEG; ++ break; ++ case INTERRUPT_VC_USB: ++ *out_hwirq = INTERRUPT_USB; ++ break; ++ case INTERRUPT_VC_3D: ++ *out_hwirq = INTERRUPT_3D; ++ break; ++ case INTERRUPT_VC_DMA2: ++ *out_hwirq = INTERRUPT_DMA2; ++ break; ++ case INTERRUPT_VC_DMA3: ++ *out_hwirq = INTERRUPT_DMA3; ++ break; ++ case INTERRUPT_VC_I2C: ++ *out_hwirq = INTERRUPT_I2C; ++ break; ++ case INTERRUPT_VC_SPI: ++ *out_hwirq = INTERRUPT_SPI; ++ break; ++ case INTERRUPT_VC_I2SPCM: ++ *out_hwirq = INTERRUPT_I2SPCM; ++ break; ++ case INTERRUPT_VC_SDIO: ++ *out_hwirq = INTERRUPT_SDIO; ++ break; ++ case INTERRUPT_VC_UART: ++ *out_hwirq = INTERRUPT_UART; ++ break; ++ case INTERRUPT_VC_ARASANSDIO: ++ *out_hwirq = INTERRUPT_ARASANSDIO; ++ break; ++ } ++ ++ *out_type = IRQ_TYPE_NONE; ++ return 0; ++} ++ ++static struct irq_domain_ops armctrl_ops = { ++ .xlate = armctrl_xlate ++}; ++ ++void __init armctrl_dt_init(void) ++{ ++ struct device_node *np; ++ struct irq_domain *domain; ++ ++ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); ++ if (!np) ++ return; ++ ++ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, ++ IRQ_ARMCTRL_START, 0, ++ &armctrl_ops, NULL); ++ WARN_ON(!domain); ++} ++#else ++void __init armctrl_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ + #if defined(CONFIG_PM) + + /* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ +@@ -215,5 +310,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, + + armctrl_pm_register(base, irq_start, resume_sources); + init_FIQ(FIQ_START); ++ armctrl_dt_init(); + return 0; + } + +From af606245311eeb7640ccfa4f9c596b2b05f6c3aa Mon Sep 17 00:00:00 2001 +From: notro +Date: Thu, 10 Jul 2014 13:59:47 +0200 +Subject: [PATCH 044/104] BCM2708: use pinctrl-bcm2835 + +Use pinctrl-bcm2835 instead of the pinctrl-bcm2708 and bcm2708_gpio +combination. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/mach-bcm2708/Kconfig | 3 +++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + drivers/pinctrl/pinctrl-bcm2835.c | 2 +- + 3 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index 182e7ba..4cfae55 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -14,6 +14,9 @@ config BCM2708_DT + depends on MACH_BCM2708 + default n + select USE_OF ++ select ARCH_REQUIRE_GPIOLIB ++ select PINCTRL ++ select PINCTRL_BCM2835 + help + Enable Device Tree support for BCM2708 + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a812b51..a207ad8 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -816,7 +816,7 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO +- bcm_register_device(&bcm2708_gpio_device); ++ bcm_register_device_dt(&bcm2708_gpio_device); + #endif + #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) + w1_gpio_pdata.pin = w1_gpio_pin; +diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c +index 9aa8a3f..9d1149e 100644 +--- a/drivers/pinctrl/pinctrl-bcm2835.c ++++ b/drivers/pinctrl/pinctrl-bcm2835.c +@@ -382,7 +382,7 @@ static struct gpio_chip bcm2835_gpio_chip = { + .get = bcm2835_gpio_get, + .set = bcm2835_gpio_set, + .to_irq = bcm2835_gpio_to_irq, +- .base = -1, ++ .base = 0, + .ngpio = BCM2835_NUM_GPIOS, + .can_sleep = false, + }; + +From a51a99b3b595628233b62f8f4c3a88ed9f4e75b6 Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 27 Jul 2014 20:12:58 +0200 +Subject: [PATCH 045/104] spi: bcm2708: add device tree support + +Add DT support to driver and add to .dtsi file. +Setup pins and spidev in .dts file. +SPI is disabled by default. + +Signed-off-by: Noralf Tronnes + +BCM2708: don't register SPI controller when using DT + +The device for the SPI controller is in the Device Tree. +Only register the device when not using DT. + +Signed-off-by: Noralf Tronnes + +spi: bcm2835: make driver available on ARCH_BCM2708 + +Make this driver available on ARCH_BCM2708 + +Signed-off-by: Noralf Tronnes + +bcm2708: Remove the prohibition on mixing SPIDEV and DT +--- + arch/arm/boot/dts/bcm2708.dtsi | 8 ++++++++ + arch/arm/mach-bcm2708/bcm2708.c | 7 ++++--- + drivers/spi/Kconfig | 4 ++-- + drivers/spi/spi-bcm2708.c | 8 ++++++++ + 4 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 96b7311..93f009a 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -82,5 +82,13 @@ + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; + }; + }; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a207ad8..e366bb4 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -836,7 +836,7 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + +- bcm_register_device(&bcm2708_spi_device); ++ bcm_register_device_dt(&bcm2708_spi_device); + bcm_register_device(&bcm2708_bsc0_device); + bcm_register_device(&bcm2708_bsc1_device); + +@@ -876,8 +876,9 @@ void __init bcm2708_init(void) + system_serial_low = serial; + + #ifdef CONFIG_BCM2708_SPIDEV +- spi_register_board_info(bcm2708_spi_devices, +- ARRAY_SIZE(bcm2708_spi_devices)); ++ if (!use_dt) ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); + #endif + } + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 9628e75..113bf64 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -77,7 +77,7 @@ config SPI_ATMEL + + config SPI_BCM2835 + tristate "BCM2835 SPI controller" +- depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST + help + This selects a driver for the Broadcom BCM2835 SPI master. + +@@ -88,7 +88,7 @@ config SPI_BCM2835 + + config SPI_BCM2708 + tristate "BCM2708 SPI controller driver (SPI0)" +- depends on MACH_BCM2708 ++ depends on MACH_BCM2708 || MACH_BCM2709 + help + This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This + driver is not compatible with the "Universal SPI Master" or the SPI slave +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +index 349d21f..041b5e2 100644 +--- a/drivers/spi/spi-bcm2708.c ++++ b/drivers/spi/spi-bcm2708.c +@@ -512,6 +512,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + master->setup = bcm2708_spi_setup; + master->transfer = bcm2708_spi_transfer; + master->cleanup = bcm2708_spi_cleanup; ++ master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, master); + + bs = spi_master_get_devdata(master); +@@ -596,10 +597,17 @@ static int bcm2708_spi_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id bcm2708_spi_match[] = { ++ { .compatible = "brcm,bcm2708-spi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_spi_match); ++ + static struct platform_driver bcm2708_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, ++ .of_match_table = bcm2708_spi_match, + }, + .probe = bcm2708_spi_probe, + .remove = bcm2708_spi_remove, + +From f84c5185e615c2ea21bde78393d819b3a891aea5 Mon Sep 17 00:00:00 2001 +From: notro +Date: Tue, 29 Jul 2014 11:04:49 +0200 +Subject: [PATCH 046/104] i2c: bcm2708: add device tree support + +Add DT support to driver and add to .dtsi file. +Setup pins in .dts file. +i2c is disabled by default. + +Signed-off-by: Noralf Tronnes + +bcm2708: don't register i2c controllers when using DT + +The devices for the i2c controllers are in the Device Tree. +Only register devices when not using DT. + +Signed-off-by: Noralf Tronnes + +i2c: bcm2835: make driver available on ARCH_BCM2708 + +Make this driver available on ARCH_BCM2708 + +Signed-off-by: Noralf Tronnes +--- + arch/arm/boot/dts/bcm2708.dtsi | 7 +++++++ + arch/arm/mach-bcm2708/bcm2708.c | 4 ++-- + drivers/i2c/busses/Kconfig | 4 ++-- + drivers/i2c/busses/i2c-bcm2708.c | 24 ++++++++++++++++++++++++ + 4 files changed, 35 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 93f009a..0a916a4 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -83,6 +83,13 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ + clk_spi: clock@2 { + compatible = "fixed-clock"; + reg = <2>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index e366bb4..a81200e 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -837,8 +837,8 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_alsa_devices[i]); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device(&bcm2708_bsc0_device); +- bcm_register_device(&bcm2708_bsc1_device); ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); + + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 00c7baa..92009dc 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -10,7 +10,7 @@ comment "PC SMBus host controller drivers" + + config I2C_BCM2708 + tristate "BCM2708 BSC" +- depends on MACH_BCM2708 ++ depends on MACH_BCM2708 || MACH_BCM2709 + help + Enabling this option will add BSC (Broadcom Serial Controller) + support for the BCM2708. BSC is a Broadcom proprietary bus compatible +@@ -381,7 +381,7 @@ config I2C_AXXIA + + config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" +- depends on ARCH_BCM2835 ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 + help + If you say yes to this option, support will be included for the + BCM2835 I2C controller. +diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c +index 7d385a3..526129b 100644 +--- a/drivers/i2c/busses/i2c-bcm2708.c ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -303,6 +304,21 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) + unsigned long bus_hz; + u32 cdiv; + ++ if (pdev->dev.of_node) { ++ u32 bus_clk_rate; ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); ++ if (pdev->id < 0) { ++ dev_err(&pdev->dev, "alias is missing\n"); ++ return -EINVAL; ++ } ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "clock-frequency", &bus_clk_rate)) ++ baudrate = bus_clk_rate; ++ else ++ dev_warn(&pdev->dev, ++ "Could not read clock-frequency property\n"); ++ } ++ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "could not get IO memory\n"); +@@ -336,6 +352,7 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ adap->dev.of_node = pdev->dev.of_node; + + switch (pdev->id) { + case 0: +@@ -416,10 +433,17 @@ static int bcm2708_i2c_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id bcm2708_i2c_of_match[] = { ++ { .compatible = "brcm,bcm2708-i2c" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); ++ + static struct platform_driver bcm2708_i2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, ++ .of_match_table = bcm2708_i2c_of_match, + }, + .probe = bcm2708_i2c_probe, + .remove = bcm2708_i2c_remove, + +From 6b6c91cab7046b83d671fa4087f3d987beed1831 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 14 Jul 2014 22:02:09 +0100 +Subject: [PATCH 047/104] hid: Reduce default mouse polling interval to 60Hz + +Reduces overhead when using X +--- + drivers/hid/usbhid/hid-core.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c +index bfbe1be..a738b25 100644 +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -49,7 +49,7 @@ + * Module parameters. + */ + +-static unsigned int hid_mousepoll_interval; ++static unsigned int hid_mousepoll_interval = ~0; + module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); + MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); + +@@ -1090,8 +1090,12 @@ static int usbhid_start(struct hid_device *hid) + } + + /* Change the polling interval of mice. */ +- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) +- interval = hid_mousepoll_interval; ++ if (hid->collection->usage == HID_GD_MOUSE) { ++ if (hid_mousepoll_interval == ~0 && interval < 16) ++ interval = 16; ++ else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0) ++ interval = hid_mousepoll_interval; ++ } + + ret = -ENOMEM; + if (usb_endpoint_dir_in(endpoint)) { + +From 0ef1043b14270c32125c90eb57bb47d5ba0617a2 Mon Sep 17 00:00:00 2001 +From: P33M +Date: Thu, 24 Jul 2014 21:24:03 +0100 +Subject: [PATCH 048/104] usb: core: make overcurrent messages more prominent + +Hub overcurrent messages are more serious than "debug". Increase loglevel. +--- + drivers/usb/core/hub.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index d7c3d5a..6ca08fa 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4922,7 +4922,7 @@ static void port_event(struct usb_hub *hub, int port1) + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + u16 status = 0, unused; + +- dev_dbg(&port_dev->dev, "over-current change\n"); ++ dev_notice(&port_dev->dev, "over-current change\n"); + usb_clear_port_feature(hdev, port1, + USB_PORT_FEAT_C_OVER_CURRENT); + msleep(100); /* Cool down */ + +From e4a1dd5d714904a221745386afe77cf3e5a3c623 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Thu, 7 Aug 2014 02:03:50 +0100 -Subject: [PATCH 058/172] Revert "ARM: dma: Use dma_pfn_offset for dma address +Subject: [PATCH 049/104] Revert "ARM: dma: Use dma_pfn_offset for dma address translation" This reverts commit 6ce0d20016925d031f1e24d64302e4c976d7cec6. @@ -114947,1774 +117169,26 @@ index b52101d..f5572d9 100644 } -From 431ffd837c0694e865b7aa72e284eb15fc122d7c Mon Sep 17 00:00:00 2001 -From: gellert -Date: Fri, 15 Aug 2014 16:35:06 +0100 -Subject: [PATCH 059/172] MMC: added alternative MMC driver - ---- - arch/arm/configs/bcmrpi_defconfig | 2 + - arch/arm/mach-bcm2708/bcm2708.c | 31 + - drivers/mmc/host/Kconfig | 40 +- - drivers/mmc/host/Makefile | 1 + - drivers/mmc/host/bcm2835-mmc.c | 1553 +++++++++++++++++++++++++++++++++++++ - 5 files changed, 1616 insertions(+), 11 deletions(-) - create mode 100644 drivers/mmc/host/bcm2835-mmc.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 7ba662a..0db8119 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -938,6 +938,8 @@ CONFIG_MMC=y - CONFIG_MMC_BLOCK_MINORS=32 - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y - CONFIG_MMC_SPI=m - CONFIG_LEDS_CLASS=y - CONFIG_LEDS_GPIO=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index b45f327..5ff1299 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -414,6 +414,34 @@ static struct platform_device bcm2708_systemtimer_device = { - }, - }; - -+#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ -+static struct resource bcm2835_emmc_resources[] = { -+ [0] = { -+ .start = EMMC_BASE, -+ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ -+ /* the memory map actually makes SZ_4K available */ -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_ARASANSDIO, -+ .end = IRQ_ARASANSDIO, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+static u64 bcm2835_emmc_dmamask = 0xffffffffUL; -+ -+struct platform_device bcm2835_emmc_device = { -+ .name = "mmc-bcm2835", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), -+ .resource = bcm2835_emmc_resources, -+ .dev = { -+ .dma_mask = &bcm2835_emmc_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+#endif /* CONFIG_MMC_BCM2835 */ -+ - static struct resource bcm2708_powerman_resources[] = { - [0] = { - .start = PM_BASE, -@@ -794,6 +822,9 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); - -+#ifdef CONFIG_MMC_BCM2835 -+ bcm_register_device(&bcm2835_emmc_device); -+#endif - bcm2708_init_led(); - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index 2d6fbdd..b6077b5 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -281,17 +281,6 @@ config MMC_SDHCI_BCM_KONA - - If you have a controller with this interface, say Y or M here. - --config MMC_SDHCI_BCM2835 -- tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" -- depends on ARCH_BCM2835 -- depends on MMC_SDHCI_PLTFM -- select MMC_SDHCI_IO_ACCESSORS -- help -- This selects the BCM2835 SD/MMC controller. If you have a BCM2835 -- platform with SD or MMC devices, say Y or M here. -- -- If unsure, say N. -- - config MMC_MOXART - tristate "MOXART SD/MMC Host Controller support" - depends on ARCH_MOXART && MMC -@@ -313,6 +302,35 @@ config MMC_SDHCI_ST - If you have a controller with this interface, say Y or M here. - If unsure, say N. - -+config MMC_BCM2835 -+ tristate "MMC support on BCM2835" -+ depends on MACH_BCM2708 -+ help -+ This selects the MMC Interface on BCM2835. -+ -+ If you have a controller with this interface, say Y or M here. -+ -+ If unsure, say N. -+ -+config MMC_BCM2835_DMA -+ bool "DMA support on BCM2835 Arasan controller" -+ depends on MMC_BCM2835 -+ help -+ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 -+ based chips. -+ -+ If unsure, say N. -+ -+config MMC_BCM2835_PIO_DMA_BARRIER -+ int "Block count limit for PIO transfers" -+ depends on MMC_BCM2835 && MMC_BCM2835_DMA -+ range 0 256 -+ default 2 -+ help -+ The inclusive limit in bytes under which PIO will be used instead of DMA -+ -+ If unsure, say 2 here. -+ - config MMC_OMAP - tristate "TI OMAP Multimedia Card Interface support" - depends on ARCH_OMAP -diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile -index f7b0a77..378aed9 100644 ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o - obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o - obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o - obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o -+obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o - obj-$(CONFIG_MMC_WBSD) += wbsd.o - obj-$(CONFIG_MMC_AU1X) += au1xmmc.o - obj-$(CONFIG_MMC_OMAP) += omap.o -diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c -new file mode 100644 -index 0000000..92febc3 ---- /dev/null -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -0,0 +1,1553 @@ -+/* -+ * BCM2835 MMC host driver. -+ * -+ * Author: Gellert Weisz -+ * Copyright 2014 -+ * -+ * Based on -+ * sdhci-bcm2708.c by Broadcom -+ * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko -+ * sdhci.c and sdhci-pci.c by Pierre Ossman -+ * -+ * 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 it will be useful, but WITHOUT -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sdhci.h" -+ -+ -+#ifndef CONFIG_OF -+ #define BCM2835_CLOCK_FREQ 250000000 -+#endif -+ -+#define DRIVER_NAME "mmc-bcm2835" -+ -+#define DBG(f, x...) \ -+pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) -+ -+#ifndef CONFIG_MMC_BCM2835_DMA -+ #define FORCE_PIO -+#endif -+ -+ -+/* the inclusive limit in bytes under which PIO will be used instead of DMA */ -+#ifdef CONFIG_MMC_BCM2835_PIO_DMA_BARRIER -+#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_PIO_DMA_BARRIER -+#else -+#define PIO_DMA_BARRIER 00 -+#endif -+ -+#define MIN_FREQ 400000 -+#define TIMEOUT_VAL 0xE -+#define BCM2835_SDHCI_WRITE_DELAY(f) (((2 * 1000000) / f) + 1) -+ -+#ifndef BCM2708_PERI_BASE -+ #define BCM2708_PERI_BASE 0x20000000 -+#endif -+ -+/* FIXME: Needs IOMMU support */ -+#define BCM2835_VCMMU_SHIFT (0x7E000000 - BCM2708_PERI_BASE) -+ -+ -+struct bcm2835_host { -+ spinlock_t lock; -+ -+ void __iomem *ioaddr; -+ u32 phys_addr; -+ -+ struct mmc_host *mmc; -+ -+ u32 timeout; -+ -+ int clock; /* Current clock speed */ -+ u8 pwr; /* Current voltage */ -+ -+ unsigned int max_clk; /* Max possible freq */ -+ unsigned int timeout_clk; /* Timeout freq (KHz) */ -+ unsigned int clk_mul; /* Clock Muliplier value */ -+ -+ struct tasklet_struct finish_tasklet; /* Tasklet structures */ -+ -+ struct timer_list timer; /* Timer for timeouts */ -+ -+ struct sg_mapping_iter sg_miter; /* SG state for PIO */ -+ unsigned int blocks; /* remaining PIO blocks */ -+ -+ int irq; /* Device IRQ */ -+ -+ -+ u32 ier; /* cached registers */ -+ -+ struct mmc_request *mrq; /* Current request */ -+ struct mmc_command *cmd; /* Current command */ -+ struct mmc_data *data; /* Current data request */ -+ unsigned int data_early:1; /* Data finished before cmd */ -+ -+ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ -+ -+ u32 thread_isr; -+ -+ u32 shadow; -+ -+ /*DMA part*/ -+ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ -+ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ -+ struct dma_async_tx_descriptor *tx_desc; /* descriptor */ -+ -+ bool have_dma; -+ bool use_dma; -+ /*end of DMA part*/ -+ -+ int max_delay; /* maximum length of time spent waiting */ -+ -+ int flags; /* Host attributes */ -+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ -+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ -+#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ -+#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ -+#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ -+}; -+ -+ -+static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg) -+{ -+ writel(val, host->ioaddr + reg); -+ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); -+} -+ -+static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg) -+{ -+ writel(val, host->ioaddr + reg); -+} -+ -+static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) -+{ -+ return readl(host->ioaddr + reg); -+} -+ -+static inline void bcm2835_mmc_writew(struct bcm2835_host *host, u16 val, int reg) -+{ -+ u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow : -+ bcm2835_mmc_readl(host, reg & ~3); -+ u32 word_num = (reg >> 1) & 1; -+ u32 word_shift = word_num * 16; -+ u32 mask = 0xffff << word_shift; -+ u32 newval = (oldval & ~mask) | (val << word_shift); -+ -+ if (reg == SDHCI_TRANSFER_MODE) -+ host->shadow = newval; -+ else -+ bcm2835_mmc_writel(host, newval, reg & ~3); -+ -+} -+ -+static inline void bcm2835_mmc_writeb(struct bcm2835_host *host, u8 val, int reg) -+{ -+ u32 oldval = bcm2835_mmc_readl(host, reg & ~3); -+ u32 byte_num = reg & 3; -+ u32 byte_shift = byte_num * 8; -+ u32 mask = 0xff << byte_shift; -+ u32 newval = (oldval & ~mask) | (val << byte_shift); -+ -+ bcm2835_mmc_writel(host, newval, reg & ~3); -+} -+ -+ -+static inline u16 bcm2835_mmc_readw(struct bcm2835_host *host, int reg) -+{ -+ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); -+ u32 word_num = (reg >> 1) & 1; -+ u32 word_shift = word_num * 16; -+ u32 word = (val >> word_shift) & 0xffff; -+ -+ return word; -+} -+ -+static inline u8 bcm2835_mmc_readb(struct bcm2835_host *host, int reg) -+{ -+ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); -+ u32 byte_num = reg & 3; -+ u32 byte_shift = byte_num * 8; -+ u32 byte = (val >> byte_shift) & 0xff; -+ -+ return byte; -+} -+ -+static void bcm2835_mmc_unsignal_irqs(struct bcm2835_host *host, u32 clear) -+{ -+ u32 ier; -+ -+ ier = bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE); -+ ier &= ~clear; -+ /* change which requests generate IRQs - makes no difference to -+ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ -+ bcm2835_mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+ -+static void bcm2835_mmc_dumpregs(struct bcm2835_host *host) -+{ -+ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", -+ mmc_hostname(host->mmc)); -+ -+ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_DMA_ADDRESS), -+ bcm2835_mmc_readw(host, SDHCI_HOST_VERSION)); -+ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_BLOCK_SIZE), -+ bcm2835_mmc_readw(host, SDHCI_BLOCK_COUNT)); -+ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_ARGUMENT), -+ bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE)); -+ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE), -+ bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL)); -+ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_POWER_CONTROL), -+ bcm2835_mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL)); -+ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_WAKE_UP_CONTROL), -+ bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)); -+ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_TIMEOUT_CONTROL), -+ bcm2835_mmc_readl(host, SDHCI_INT_STATUS)); -+ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_INT_ENABLE), -+ bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE)); -+ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_ACMD12_ERR), -+ bcm2835_mmc_readw(host, SDHCI_SLOT_INT_STATUS)); -+ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES), -+ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES_1)); -+ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_COMMAND), -+ bcm2835_mmc_readl(host, SDHCI_MAX_CURRENT)); -+ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2)); -+ -+ pr_debug(DRIVER_NAME ": ===========================================\n"); -+} -+ -+ -+static void bcm2835_mmc_reset(struct bcm2835_host *host, u8 mask) -+{ -+ unsigned long timeout; -+ -+ bcm2835_mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET); -+ -+ if (mask & SDHCI_RESET_ALL) -+ host->clock = 0; -+ -+ /* Wait max 100 ms */ -+ timeout = 100; -+ -+ /* hw clears the bit when it's done */ -+ while (bcm2835_mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) { -+ if (timeout == 0) { -+ pr_err("%s: Reset 0x%x never completed.\n", -+ mmc_hostname(host->mmc), (int)mask); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } -+ -+ if (100-timeout > 10 && 100-timeout > host->max_delay) { -+ host->max_delay = 100-timeout; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+} -+ -+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); -+ -+static void bcm2835_mmc_init(struct bcm2835_host *host, int soft) -+{ -+ if (soft) -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); -+ else -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | -+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | -+ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | -+ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | -+ SDHCI_INT_RESPONSE; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+ -+ if (soft) { -+ /* force clock reconfiguration */ -+ host->clock = 0; -+ bcm2835_mmc_set_ios(host->mmc, &host->mmc->ios); -+ } -+} -+ -+ -+ -+static void bcm2835_mmc_finish_data(struct bcm2835_host *host); -+ -+static void bcm2835_mmc_dma_complete(void *param) -+{ -+ struct bcm2835_host *host = param; -+ struct dma_chan *dma_chan; -+ unsigned long flags; -+ u32 dir_data; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { -+ /* otherwise handled in SDHCI IRQ */ -+ dma_chan = host->dma_chan_rx; -+ dir_data = DMA_FROM_DEVICE; -+ -+ dma_unmap_sg(dma_chan->device->dev, -+ host->data->sg, host->data->sg_len, -+ dir_data); -+ -+ bcm2835_mmc_finish_data(host); -+ } -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_bcm2835_mmc_read_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len, chunk; -+ -+ u32 uninitialized_var(scratch); -+ u8 *buf; -+ -+ blksize = host->data->blksz; -+ chunk = 0; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = host->sg_miter.addr; -+ -+ while (len) { -+ if (chunk == 0) { -+ scratch = bcm2835_mmc_readl(host, SDHCI_BUFFER); -+ chunk = 4; -+ } -+ -+ *buf = scratch & 0xFF; -+ -+ buf++; -+ scratch >>= 8; -+ chunk--; -+ len--; -+ } -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+static void bcm2835_bcm2835_mmc_write_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len, chunk; -+ u32 scratch; -+ u8 *buf; -+ -+ blksize = host->data->blksz; -+ chunk = 0; -+ chunk = 0; -+ scratch = 0; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = host->sg_miter.addr; -+ -+ while (len) { -+ scratch |= (u32)*buf << (chunk * 8); -+ -+ buf++; -+ chunk++; -+ len--; -+ -+ if ((chunk == 4) || ((len == 0) && (blksize == 0))) { -+ mmc_raw_writel(host, scratch, SDHCI_BUFFER); -+ chunk = 0; -+ scratch = 0; -+ } -+ } -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+ -+static void bcm2835_mmc_transfer_pio(struct bcm2835_host *host) -+{ -+ u32 mask; -+ -+ BUG_ON(!host->data); -+ -+ if (host->blocks == 0) -+ return; -+ -+ if (host->data->flags & MMC_DATA_READ) -+ mask = SDHCI_DATA_AVAILABLE; -+ else -+ mask = SDHCI_SPACE_AVAILABLE; -+ -+ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { -+ -+ if (host->data->flags & MMC_DATA_READ) -+ bcm2835_bcm2835_mmc_read_block_pio(host); -+ else -+ bcm2835_bcm2835_mmc_write_block_pio(host); -+ -+ host->blocks--; -+ -+ /* QUIRK used in sdhci.c removes the 'if' */ -+ /* but it seems this is unnecessary */ -+ if (host->blocks == 0) -+ break; -+ -+ -+ } -+} -+ -+ -+static void bcm2835_mmc_transfer_dma(struct bcm2835_host *host) -+{ -+ u32 len, dir_data, dir_slave; -+ struct dma_async_tx_descriptor *desc = NULL; -+ struct dma_chan *dma_chan; -+ -+ -+ WARN_ON(!host->data); -+ -+ if (!host->data) -+ return; -+ -+ if (host->blocks == 0) -+ return; -+ -+ if (host->data->flags & MMC_DATA_READ) { -+ dma_chan = host->dma_chan_rx; -+ dir_data = DMA_FROM_DEVICE; -+ dir_slave = DMA_DEV_TO_MEM; -+ } else { -+ dma_chan = host->dma_chan_tx; -+ dir_data = DMA_TO_DEVICE; -+ dir_slave = DMA_MEM_TO_DEV; -+ } -+ -+ BUG_ON(!dma_chan->device); -+ BUG_ON(!dma_chan->device->dev); -+ BUG_ON(!host->data->sg); -+ -+ len = dma_map_sg(dma_chan->device->dev, host->data->sg, -+ host->data->sg_len, dir_data); -+ if (len > 0) { -+ desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg, -+ len, dir_slave, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -+ } else { -+ dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); -+ } -+ if (desc) { -+ bcm2835_mmc_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | -+ SDHCI_INT_SPACE_AVAIL); -+ host->tx_desc = desc; -+ desc->callback = bcm2835_mmc_dma_complete; -+ desc->callback_param = host; -+ dmaengine_submit(desc); -+ dma_async_issue_pending(dma_chan); -+ } -+ -+} -+ -+ -+ -+static void bcm2835_mmc_set_transfer_irqs(struct bcm2835_host *host) -+{ -+ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; -+ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; -+ -+ if (host->use_dma) -+ host->ier = (host->ier & ~pio_irqs) | dma_irqs; -+ else -+ host->ier = (host->ier & ~dma_irqs) | pio_irqs; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+ -+static void bcm2835_mmc_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ u8 count; -+ struct mmc_data *data = cmd->data; -+ -+ WARN_ON(host->data); -+ -+ if (data || (cmd->flags & MMC_RSP_BUSY)) { -+ count = TIMEOUT_VAL; -+ bcm2835_mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL); -+ } -+ -+ if (!data) -+ return; -+ -+ /* Sanity checks */ -+ BUG_ON(data->blksz * data->blocks > 524288); -+ BUG_ON(data->blksz > host->mmc->max_blk_size); -+ BUG_ON(data->blocks > 65535); -+ -+ host->data = data; -+ host->data_early = 0; -+ host->data->bytes_xfered = 0; -+ -+ -+ if (!(host->flags & SDHCI_REQ_USE_DMA)) { -+ int flags; -+ -+ flags = SG_MITER_ATOMIC; -+ if (host->data->flags & MMC_DATA_READ) -+ flags |= SG_MITER_TO_SG; -+ else -+ flags |= SG_MITER_FROM_SG; -+ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); -+ host->blocks = data->blocks; -+ } -+ -+ host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER; -+ -+ bcm2835_mmc_set_transfer_irqs(host); -+ -+ /* Set the DMA boundary value and block size */ -+ bcm2835_mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, -+ data->blksz), SDHCI_BLOCK_SIZE); -+ bcm2835_mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT); -+ -+ BUG_ON(!host->data); -+} -+ -+static void bcm2835_mmc_set_transfer_mode(struct bcm2835_host *host, -+ struct mmc_command *cmd) -+{ -+ u16 mode; -+ struct mmc_data *data = cmd->data; -+ -+ if (data == NULL) { -+ /* clear Auto CMD settings for no data CMDs */ -+ mode = bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE); -+ bcm2835_mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | -+ SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); -+ return; -+ } -+ -+ WARN_ON(!host->data); -+ -+ mode = SDHCI_TRNS_BLK_CNT_EN; -+ -+ if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) { -+ mode |= SDHCI_TRNS_MULTI; -+ -+ /* -+ * If we are sending CMD23, CMD12 never gets sent -+ * on successful completion (so no Auto-CMD12). -+ */ -+ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) -+ mode |= SDHCI_TRNS_AUTO_CMD12; -+ else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { -+ mode |= SDHCI_TRNS_AUTO_CMD23; -+ bcm2835_mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); -+ } -+ } -+ -+ if (data->flags & MMC_DATA_READ) -+ mode |= SDHCI_TRNS_READ; -+ if (host->flags & SDHCI_REQ_USE_DMA) -+ mode |= SDHCI_TRNS_DMA; -+ -+ bcm2835_mmc_writew(host, mode, SDHCI_TRANSFER_MODE); -+} -+ -+void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ int flags; -+ u32 mask; -+ unsigned long timeout; -+ -+ WARN_ON(host->cmd); -+ -+ /* Wait max 10 ms */ -+ timeout = 1000; -+ -+ mask = SDHCI_CMD_INHIBIT; -+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) -+ mask |= SDHCI_DATA_INHIBIT; -+ -+ /* We shouldn't wait for data inihibit for stop commands, even -+ though they might use busy signaling */ -+ if (host->mrq->data && (cmd == host->mrq->data->stop)) -+ mask &= ~SDHCI_DATA_INHIBIT; -+ -+ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { -+ if (timeout == 0) { -+ pr_err("%s: Controller never released inhibit bit(s).\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ cmd->error = -EIO; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ timeout--; -+ udelay(10); -+ } -+ -+ if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) { -+ host->max_delay = (1000-timeout)/100; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+ -+ timeout = jiffies; -+#ifdef CONFIG_OF -+ if (!cmd->data && cmd->busy_timeout > 9000) -+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; -+ else -+#endif -+ timeout += 10 * HZ; -+ mod_timer(&host->timer, timeout); -+ -+ host->cmd = cmd; -+ -+ bcm2835_mmc_prepare_data(host, cmd); -+ -+ bcm2835_mmc_writel(host, cmd->arg, SDHCI_ARGUMENT); -+ -+ bcm2835_mmc_set_transfer_mode(host, cmd); -+ -+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { -+ pr_err("%s: Unsupported response type!\n", -+ mmc_hostname(host->mmc)); -+ cmd->error = -EINVAL; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ -+ if (!(cmd->flags & MMC_RSP_PRESENT)) -+ flags = SDHCI_CMD_RESP_NONE; -+ else if (cmd->flags & MMC_RSP_136) -+ flags = SDHCI_CMD_RESP_LONG; -+ else if (cmd->flags & MMC_RSP_BUSY) -+ flags = SDHCI_CMD_RESP_SHORT_BUSY; -+ else -+ flags = SDHCI_CMD_RESP_SHORT; -+ -+ if (cmd->flags & MMC_RSP_CRC) -+ flags |= SDHCI_CMD_CRC; -+ if (cmd->flags & MMC_RSP_OPCODE) -+ flags |= SDHCI_CMD_INDEX; -+ -+ if (cmd->data) -+ flags |= SDHCI_CMD_DATA; -+ -+ bcm2835_mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); -+} -+ -+ -+static void bcm2835_mmc_finish_data(struct bcm2835_host *host) -+{ -+ struct mmc_data *data; -+ -+ BUG_ON(!host->data); -+ -+ data = host->data; -+ host->data = NULL; -+ -+ if (data->error) -+ data->bytes_xfered = 0; -+ else -+ data->bytes_xfered = data->blksz * data->blocks; -+ -+ /* -+ * Need to send CMD12 if - -+ * a) open-ended multiblock transfer (no CMD23) -+ * b) error in multiblock transfer -+ */ -+ if (data->stop && -+ (data->error || -+ !host->mrq->sbc)) { -+ -+ /* -+ * The controller needs a reset of internal state machines -+ * upon error conditions. -+ */ -+ if (data->error) { -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); -+ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); -+ } -+ -+ bcm2835_mmc_send_command(host, data->stop); -+ } else -+ tasklet_schedule(&host->finish_tasklet); -+} -+ -+static void bcm2835_mmc_finish_command(struct bcm2835_host *host) -+{ -+ int i; -+ -+ BUG_ON(host->cmd == NULL); -+ -+ if (host->cmd->flags & MMC_RSP_PRESENT) { -+ if (host->cmd->flags & MMC_RSP_136) { -+ /* CRC is stripped so we need to do some shifting. */ -+ for (i = 0; i < 4; i++) { -+ host->cmd->resp[i] = bcm2835_mmc_readl(host, -+ SDHCI_RESPONSE + (3-i)*4) << 8; -+ if (i != 3) -+ host->cmd->resp[i] |= -+ bcm2835_mmc_readb(host, -+ SDHCI_RESPONSE + (3-i)*4-1); -+ } -+ } else { -+ host->cmd->resp[0] = bcm2835_mmc_readl(host, SDHCI_RESPONSE); -+ } -+ } -+ -+ host->cmd->error = 0; -+ -+ /* Finished CMD23, now send actual command. */ -+ if (host->cmd == host->mrq->sbc) { -+ host->cmd = NULL; -+ bcm2835_mmc_send_command(host, host->mrq->cmd); -+ -+ if (host->mrq->cmd->data && host->use_dma) { -+ /* DMA transfer starts now, PIO starts after interrupt */ -+ bcm2835_mmc_transfer_dma(host); -+ } -+ } else { -+ -+ /* Processed actual command. */ -+ if (host->data && host->data_early) -+ bcm2835_mmc_finish_data(host); -+ -+ if (!host->cmd->data) -+ tasklet_schedule(&host->finish_tasklet); -+ -+ host->cmd = NULL; -+ } -+} -+ -+ -+static void bcm2835_mmc_timeout_timer(unsigned long data) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = (struct bcm2835_host *)data; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (host->mrq) { -+ pr_err("%s: Timeout waiting for hardware interrupt.\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ -+ if (host->data) { -+ host->data->error = -ETIMEDOUT; -+ bcm2835_mmc_finish_data(host); -+ } else { -+ if (host->cmd) -+ host->cmd->error = -ETIMEDOUT; -+ else -+ host->mrq->cmd->error = -ETIMEDOUT; -+ -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ } -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+ -+static void bcm2835_mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) -+{ -+ if (!(host->flags & SDHCI_DEVICE_DEAD)) { -+ if (enable) -+ host->ier |= SDHCI_INT_CARD_INT; -+ else -+ host->ier &= ~SDHCI_INT_CARD_INT; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+ mmiowb(); -+ } -+} -+ -+static void bcm2835_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) -+{ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ if (enable) -+ host->flags |= SDHCI_SDIO_IRQ_ENABLED; -+ else -+ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; -+ -+ bcm2835_mmc_enable_sdio_irq_nolock(host, enable); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_mmc_cmd_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ -+ BUG_ON(intmask == 0); -+ -+ if (!host->cmd) { -+ pr_err("%s: Got command interrupt 0x%08x even " -+ "though no command operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_TIMEOUT) -+ host->cmd->error = -ETIMEDOUT; -+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | -+ SDHCI_INT_INDEX)) { -+ host->cmd->error = -EILSEQ; -+ } -+ -+ if (host->cmd->error) { -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_RESPONSE) -+ bcm2835_mmc_finish_command(host); -+ -+} -+ -+static void bcm2835_mmc_data_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ struct dma_chan *dma_chan; -+ u32 dir_data; -+ -+ BUG_ON(intmask == 0); -+ -+ if (!host->data) { -+ /* -+ * The "data complete" interrupt is also used to -+ * indicate that a busy state has ended. See comment -+ * above in sdhci_cmd_irq(). -+ */ -+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { -+ if (intmask & SDHCI_INT_DATA_END) { -+ bcm2835_mmc_finish_command(host); -+ return; -+ } -+ } -+ -+ pr_debug("%s: Got data interrupt 0x%08x even " -+ "though no data operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_mmc_dumpregs(host); -+ -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_DATA_TIMEOUT) -+ host->data->error = -ETIMEDOUT; -+ else if (intmask & SDHCI_INT_DATA_END_BIT) -+ host->data->error = -EILSEQ; -+ else if ((intmask & SDHCI_INT_DATA_CRC) && -+ SDHCI_GET_CMD(bcm2835_mmc_readw(host, SDHCI_COMMAND)) -+ != MMC_BUS_TEST_R) -+ host->data->error = -EILSEQ; -+ -+ if (host->use_dma) { -+ if (host->data->flags & MMC_DATA_WRITE) { -+ /* IRQ handled here */ -+ -+ dma_chan = host->dma_chan_tx; -+ dir_data = DMA_TO_DEVICE; -+ dma_unmap_sg(dma_chan->device->dev, -+ host->data->sg, host->data->sg_len, -+ dir_data); -+ -+ bcm2835_mmc_finish_data(host); -+ } -+ -+ } else { -+ if (host->data->error) -+ bcm2835_mmc_finish_data(host); -+ else { -+ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) -+ bcm2835_mmc_transfer_pio(host); -+ -+ if (intmask & SDHCI_INT_DATA_END) { -+ if (host->cmd) { -+ /* -+ * Data managed to finish before the -+ * command completed. Make sure we do -+ * things in the proper order. -+ */ -+ host->data_early = 1; -+ } else { -+ bcm2835_mmc_finish_data(host); -+ } -+ } -+ } -+ } -+} -+ -+ -+static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) -+{ -+ irqreturn_t result = IRQ_NONE; -+ struct bcm2835_host *host = dev_id; -+ u32 intmask, mask, unexpected = 0; -+ int max_loops = 16; -+#ifndef CONFIG_OF -+ int cardint = 0; -+#endif -+ -+ spin_lock(&host->lock); -+ -+ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ -+ if (!intmask || intmask == 0xffffffff) { -+ result = IRQ_NONE; -+ goto out; -+ } -+ -+ do { -+ /* Clear selected interrupts. */ -+ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | -+ SDHCI_INT_BUS_POWER); -+ bcm2835_mmc_writel(host, mask, SDHCI_INT_STATUS); -+ -+ -+ if (intmask & SDHCI_INT_CMD_MASK) -+ bcm2835_mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); -+ -+ if (intmask & SDHCI_INT_DATA_MASK) -+ bcm2835_mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK); -+ -+ if (intmask & SDHCI_INT_BUS_POWER) -+ pr_err("%s: Card is consuming too much power!\n", -+ mmc_hostname(host->mmc)); -+ -+ if (intmask & SDHCI_INT_CARD_INT) { -+#ifndef CONFIG_OF -+ cardint = 1; -+#else -+ bcm2835_mmc_enable_sdio_irq_nolock(host, false); -+ host->thread_isr |= SDHCI_INT_CARD_INT; -+ result = IRQ_WAKE_THREAD; -+#endif -+ } -+ -+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | -+ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | -+ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | -+ SDHCI_INT_CARD_INT); -+ -+ if (intmask) { -+ unexpected |= intmask; -+ bcm2835_mmc_writel(host, intmask, SDHCI_INT_STATUS); -+ } -+ -+ if (result == IRQ_NONE) -+ result = IRQ_HANDLED; -+ -+ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ } while (intmask && --max_loops); -+out: -+ spin_unlock(&host->lock); -+ -+ if (unexpected) { -+ pr_err("%s: Unexpected interrupt 0x%08x.\n", -+ mmc_hostname(host->mmc), unexpected); -+ bcm2835_mmc_dumpregs(host); -+ } -+ -+#ifndef CONFIG_OF -+ if (cardint) -+ mmc_signal_sdio_irq(host->mmc); -+#endif -+ -+ return result; -+} -+ -+#ifdef CONFIG_OF -+static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) -+{ -+ struct bcm2835_host *host = dev_id; -+ unsigned long flags; -+ u32 isr; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ isr = host->thread_isr; -+ host->thread_isr = 0; -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ if (isr & SDHCI_INT_CARD_INT) { -+ sdio_run_irqs(host->mmc); -+ -+ spin_lock_irqsave(&host->lock, flags); -+ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) -+ bcm2835_mmc_enable_sdio_irq_nolock(host, true); -+ spin_unlock_irqrestore(&host->lock, flags); -+ } -+ -+ return isr ? IRQ_HANDLED : IRQ_NONE; -+} -+#endif -+ -+ -+ -+void bcm2835_mmc_set_clock(struct bcm2835_host *host, unsigned int clock) -+{ -+ int div = 0; /* Initialized for compiler warning */ -+ int real_div = div, clk_mul = 1; -+ u16 clk = 0; -+ unsigned long timeout; -+ -+ -+ host->mmc->actual_clock = 0; -+ -+ bcm2835_mmc_writew(host, 0, SDHCI_CLOCK_CONTROL); -+ -+ if (clock == 0) -+ return; -+ -+ /* Version 3.00 divisors must be a multiple of 2. */ -+ if (host->max_clk <= clock) -+ div = 1; -+ else { -+ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; -+ div += 2) { -+ if ((host->max_clk / div) <= clock) -+ break; -+ } -+ } -+ -+ real_div = div; -+ div >>= 1; -+ -+ if (real_div) -+ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; -+ -+ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; -+ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) -+ << SDHCI_DIVIDER_HI_SHIFT; -+ clk |= SDHCI_CLOCK_INT_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+ -+ /* Wait max 20 ms */ -+ timeout = 20; -+ while (!((clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)) -+ & SDHCI_CLOCK_INT_STABLE)) { -+ if (timeout == 0) { -+ pr_err("%s: Internal clock never " -+ "stabilised.\n", mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } -+ -+ if (20-timeout > 10 && 20-timeout > host->max_delay) { -+ host->max_delay = 20-timeout; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+ -+ clk |= SDHCI_CLOCK_CARD_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+} -+ -+static void bcm2835_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = mmc_priv(mmc); -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ WARN_ON(host->mrq != NULL); -+ -+ host->mrq = mrq; -+ -+ if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) -+ bcm2835_mmc_send_command(host, mrq->sbc); -+ else -+ bcm2835_mmc_send_command(host, mrq->cmd); -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ if (!(mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) && mrq->cmd->data && host->use_dma) { -+ /* DMA transfer starts now, PIO starts after interrupt */ -+ bcm2835_mmc_transfer_dma(host); -+ } -+} -+ -+ -+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -+{ -+ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ u8 ctrl; -+ u16 clk, ctrl_2; -+ -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (!ios->clock || ios->clock != host->clock) { -+ bcm2835_mmc_set_clock(host, ios->clock); -+ host->clock = ios->clock; -+ } -+ -+ if (host->pwr != SDHCI_POWER_330) { -+ host->pwr = SDHCI_POWER_330; -+ bcm2835_mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL); -+ } -+ -+ ctrl = bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL); -+ -+ /* set bus width */ -+ ctrl &= ~SDHCI_CTRL_8BITBUS; -+ if (ios->bus_width == MMC_BUS_WIDTH_4) -+ ctrl |= SDHCI_CTRL_4BITBUS; -+ else -+ ctrl &= ~SDHCI_CTRL_4BITBUS; -+ -+ ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */ -+ -+ -+ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); -+ /* -+ * We only need to set Driver Strength if the -+ * preset value enable is not set. -+ */ -+ ctrl_2 = bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2); -+ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; -+ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) -+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; -+ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) -+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; -+ -+ bcm2835_mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); -+ -+ /* Reset SD Clock Enable */ -+ clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL); -+ clk &= ~SDHCI_CLOCK_CARD_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+ -+ /* Re-enable SD Clock */ -+ bcm2835_mmc_set_clock(host, host->clock); -+ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+ -+static struct mmc_host_ops bcm2835_ops = { -+ .request = bcm2835_mmc_request, -+ .set_ios = bcm2835_mmc_set_ios, -+ .enable_sdio_irq = bcm2835_mmc_enable_sdio_irq, -+}; -+ -+ -+static void bcm2835_mmc_tasklet_finish(unsigned long param) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ struct mmc_request *mrq; -+ -+ host = (struct bcm2835_host *)param; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ /* -+ * If this tasklet gets rescheduled while running, it will -+ * be run again afterwards but without any active request. -+ */ -+ if (!host->mrq) { -+ spin_unlock_irqrestore(&host->lock, flags); -+ return; -+ } -+ -+ del_timer(&host->timer); -+ -+ mrq = host->mrq; -+ -+ /* -+ * The controller needs a reset of internal state machines -+ * upon error conditions. -+ */ -+ if (!(host->flags & SDHCI_DEVICE_DEAD) && -+ ((mrq->cmd && mrq->cmd->error) || -+ (mrq->data && (mrq->data->error || -+ (mrq->data->stop && mrq->data->stop->error))))) { -+ -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); -+ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); -+ } -+ -+ host->mrq = NULL; -+ host->cmd = NULL; -+ host->data = NULL; -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ mmc_request_done(host->mmc, mrq); -+} -+ -+ -+ -+int bcm2835_mmc_add_host(struct bcm2835_host *host) -+{ -+ struct mmc_host *mmc; -+#ifndef FORCE_PIO -+ struct dma_slave_config cfg; -+#endif -+ int ret; -+ -+ mmc = host->mmc; -+ -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ host->clk_mul = 0; -+ -+ mmc->ops = &bcm2835_ops; -+ mmc->f_max = host->max_clk; -+ mmc->f_max = host->max_clk; -+ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; -+ -+ /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ -+ host->timeout_clk = mmc->f_max / 1000; -+#ifdef CONFIG_OF -+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; -+#endif -+ /* host controller capabilities */ -+ mmc->caps = MMC_CAP_CMD23 | MMC_CAP_ERASE | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | -+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_4_BIT_DATA; -+ -+ host->flags = SDHCI_AUTO_CMD23; -+ -+ spin_lock_init(&host->lock); -+ -+ -+#ifdef FORCE_PIO -+ pr_info("Forcing PIO mode\n"); -+ host->have_dma = false; -+#else -+ if (!host->dma_chan_tx || !host->dma_chan_rx || -+ IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { -+ pr_err("%s: Unable to initialise DMA channels. Falling back to PIO\n", DRIVER_NAME); -+ host->have_dma = false; -+ } else { -+ pr_info("DMA channels allocated for the MMC driver"); -+ host->have_dma = true; -+ -+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ cfg.slave_id = 11; /* DREQ channel */ -+ -+ cfg.direction = DMA_MEM_TO_DEV; -+ cfg.src_addr = 0; -+ cfg.dst_addr = host->phys_addr + SDHCI_BUFFER; -+ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->phys_addr + SDHCI_BUFFER; -+ cfg.dst_addr = 0; -+ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); -+ } -+#endif -+ -+ -+ mmc->max_segs = 128; -+ mmc->max_req_size = 524288; -+ mmc->max_seg_size = mmc->max_req_size; -+ mmc->max_blk_size = 512; -+ mmc->max_blk_count = 65535; -+ -+ /* report supported voltage ranges */ -+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -+ -+ tasklet_init(&host->finish_tasklet, -+ bcm2835_mmc_tasklet_finish, (unsigned long)host); -+ -+ setup_timer(&host->timer, bcm2835_mmc_timeout_timer, (unsigned long)host); -+ init_waitqueue_head(&host->buf_ready_int); -+ -+ bcm2835_mmc_init(host, 0); -+#ifndef CONFIG_OF -+ ret = request_irq(host->irq, bcm2835_mmc_irq, 0 /*IRQF_SHARED*/, -+ mmc_hostname(mmc), host); -+#else -+ ret = request_threaded_irq(host->irq, bcm2835_mmc_irq, bcm2835_mmc_thread_irq, -+ IRQF_SHARED, mmc_hostname(mmc), host); -+#endif -+ if (ret) { -+ pr_err("%s: Failed to request IRQ %d: %d\n", -+ mmc_hostname(mmc), host->irq, ret); -+ goto untasklet; -+ } -+ -+ mmiowb(); -+ mmc_add_host(mmc); -+ -+ pr_info("Load BCM2835 MMC driver\n"); -+ -+ return 0; -+ -+untasklet: -+ tasklet_kill(&host->finish_tasklet); -+ -+ return ret; -+} -+ -+static int bcm2835_mmc_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+#ifdef CONFIG_OF -+ struct device_node *node = dev->of_node; -+ struct clk *clk; -+#endif -+ struct resource *iomem; -+ struct bcm2835_host *host = NULL; -+ -+ int ret; -+ struct mmc_host *mmc; -+#if !defined(CONFIG_OF) && !defined(FORCE_PIO) -+ dma_cap_mask_t mask; -+#endif -+ -+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!iomem) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if (resource_size(iomem) < 0x100) -+ dev_err(&pdev->dev, "Invalid iomem size!\n"); -+ -+ mmc = mmc_alloc_host(sizeof(struct bcm2835_host), dev); -+ host = mmc_priv(mmc); -+ host->mmc = mmc; -+ -+ -+ if (IS_ERR(host)) { -+ ret = PTR_ERR(host); -+ goto err; -+ } -+ -+ host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; -+ -+#ifndef CONFIG_OF -+#ifndef FORCE_PIO -+ dma_cap_zero(mask); -+ /* we don't care about the channel, any would work */ -+ dma_cap_set(DMA_SLAVE, mask); -+ -+ host->dma_chan_tx = dma_request_channel(mask, NULL, NULL); -+ host->dma_chan_rx = dma_request_channel(mask, NULL, NULL); -+#endif -+ host->max_clk = BCM2835_CLOCK_FREQ; -+ -+#else -+#ifndef FORCE_PIO -+ host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); -+ host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); -+#endif -+ clk = of_clk_get(node, 0); -+ if (IS_ERR(clk)) { -+ dev_err(dev, "get CLOCK failed\n"); -+ ret = PTR_ERR(clk); -+ goto out; -+ } -+ host->max_clk = (clk_get_rate(clk)); -+#endif -+ host->irq = platform_get_irq(pdev, 0); -+ -+ if (!request_mem_region(iomem->start, resource_size(iomem), -+ mmc_hostname(host->mmc))) { -+ dev_err(&pdev->dev, "cannot request region\n"); -+ ret = -EBUSY; -+ goto err_request; -+ } -+ -+ host->ioaddr = ioremap(iomem->start, resource_size(iomem)); -+ if (!host->ioaddr) { -+ dev_err(&pdev->dev, "failed to remap registers\n"); -+ ret = -ENOMEM; -+ goto err_remap; -+ } -+ -+ platform_set_drvdata(pdev, host); -+ -+ -+ if (host->irq <= 0) { -+ dev_err(dev, "get IRQ failed\n"); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ -+#ifndef CONFIG_OF -+ mmc->caps |= MMC_CAP_4_BIT_DATA; -+#else -+ mmc_of_parse(mmc); -+#endif -+ host->timeout = msecs_to_jiffies(1000); -+ spin_lock_init(&host->lock); -+ mmc->ops = &bcm2835_ops; -+ return bcm2835_mmc_add_host(host); -+ -+ -+err_remap: -+ release_mem_region(iomem->start, resource_size(iomem)); -+err_request: -+ mmc_free_host(host->mmc); -+err: -+ dev_err(&pdev->dev, "%s failed %d\n", __func__, ret); -+ return ret; -+out: -+ if (mmc) -+ mmc_free_host(mmc); -+ return ret; -+} -+ -+static int bcm2835_mmc_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_host *host = platform_get_drvdata(pdev); -+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ unsigned long flags; -+ int dead; -+ u32 scratch; -+ -+ dead = 0; -+ scratch = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ if (scratch == (u32)-1) -+ dead = 1; -+ -+ -+ if (dead) { -+ spin_lock_irqsave(&host->lock, flags); -+ -+ host->flags |= SDHCI_DEVICE_DEAD; -+ -+ if (host->mrq) { -+ pr_err("%s: Controller removed during " -+ " transfer!\n", mmc_hostname(host->mmc)); -+ -+ host->mrq->cmd->error = -ENOMEDIUM; -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ } -+ -+ mmc_remove_host(host->mmc); -+ -+ if (!dead) -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ free_irq(host->irq, host); -+ -+ del_timer_sync(&host->timer); -+ -+ tasklet_kill(&host->finish_tasklet); -+ -+ iounmap(host->ioaddr); -+ release_mem_region(iomem->start, resource_size(iomem)); -+ mmc_free_host(host->mmc); -+ platform_set_drvdata(pdev, NULL); -+ -+ return 0; -+} -+ -+ -+static const struct of_device_id bcm2835_mmc_match[] = { -+ { .compatible = "brcm,bcm2835-mmc" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, bcm2835_mmc_match); -+ -+ -+ -+static struct platform_driver bcm2835_mmc_driver = { -+ .probe = bcm2835_mmc_probe, -+ .remove = bcm2835_mmc_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_mmc_match, -+ }, -+}; -+module_platform_driver(bcm2835_mmc_driver); -+ -+MODULE_ALIAS("platform:mmc-bcm2835"); -+MODULE_DESCRIPTION("BCM2835 SDHCI driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Gellert Weisz"); - -From 0ccc5b840254ab00236b28ccb0156f59ddc72807 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Tue, 13 Jan 2015 17:12:18 +0000 -Subject: [PATCH 060/172] mmc: Disable CMD23 transfers on all cards - -Pending wire-level investigation of these types of transfers -and associated errors on bcm2835-mmc, disable for now. Fallback of -CMD18/CMD25 transfers will be used automatically by the MMC layer. - -Reported/Tested-by: Gellert Weisz ---- - drivers/mmc/core/quirks.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c -index dd1d1e0..f472082 100644 ---- a/drivers/mmc/core/quirks.c -+++ b/drivers/mmc/core/quirks.c -@@ -95,5 +95,9 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) - f->vendor_fixup(card, f->data); - } - } -+ /* SDHCI on BCM2708 - bug causes a certain sequence of CMD23 operations to fail. -+ * Disable this flag for all cards (fall-back to CMD25/CMD18 multi-block transfers). -+ */ -+ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; - } - EXPORT_SYMBOL(mmc_fixup_device); - -From 83621e3bccbabed1a256973918dc5bcf9039a56b Mon Sep 17 00:00:00 2001 +From 61df27580635db9976f02cc1f67a76fef3775092 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Mon, 4 Aug 2014 10:06:56 +0200 -Subject: [PATCH 061/172] Added support for HiFiBerry DAC+ +Subject: [PATCH 050/104] Added support for HiFiBerry DAC+ The driver is based on the HiFiBerry DAC driver. However HiFiBerry DAC+ uses a different codec chip (PCM5122), therefore a new driver is necessary. --- - arch/arm/configs/bcmrpi_defconfig | 1 + arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++ sound/soc/bcm/Kconfig | 7 +++ sound/soc/bcm/Makefile | 2 + sound/soc/bcm/hifiberry_dacplus.c | 119 ++++++++++++++++++++++++++++++++++++++ - 5 files changed, 148 insertions(+) + 4 files changed, 147 insertions(+) create mode 100644 sound/soc/bcm/hifiberry_dacplus.c -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 0db8119..a8c60cf 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 5ff1299..aa62e55 100644 +index a81200e..5f7df9e 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -645,6 +645,20 @@ static struct platform_device snd_pcm5102a_codec_device = { +@@ -649,6 +649,20 @@ static struct platform_device snd_pcm5102a_codec_device = { }; #endif @@ -116735,20 +117209,20 @@ index 5ff1299..aa62e55 100644 #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) static struct platform_device snd_hifiberry_digi_device = { .name = "snd-hifiberry-digi", -@@ -845,6 +859,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); +@@ -852,6 +866,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); #endif +#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+ bcm_register_device(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); ++ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); +#endif + #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) - bcm_register_device(&snd_hifiberry_digi_device); - i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); + bcm_register_device_dt(&snd_hifiberry_digi_device); + i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index c621a5e..926a82b 100644 +index faa89ef..7675501 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC @@ -116909,45 +117383,32 @@ index 0000000..c63387b +MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+"); +MODULE_LICENSE("GPL v2"); -From ecc316d9abb1a30fae40c0581aba6439aa732474 Mon Sep 17 00:00:00 2001 +From 7d55e4b440fa99bb3427e23d33aa9c055531a907 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Mon, 4 Aug 2014 11:09:58 +0200 -Subject: [PATCH 062/172] Added driver for HiFiBerry Amp amplifier add-on board +Subject: [PATCH 051/104] Added driver for HiFiBerry Amp amplifier add-on board The driver contains a low-level hardware driver for the TAS5713 and the drivers for the Raspberry Pi I2S subsystem. --- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 20 +++ - sound/soc/bcm/Kconfig | 7 + - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_amp.c | 106 +++++++++++ - sound/soc/codecs/Kconfig | 4 + - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/tas5713.c | 362 ++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/tas5713.h | 210 ++++++++++++++++++++++ - 9 files changed, 714 insertions(+) + arch/arm/mach-bcm2708/bcm2708.c | 19 +++ + sound/soc/bcm/Kconfig | 7 + + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_amp.c | 106 ++++++++++++ + sound/soc/codecs/Kconfig | 4 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/tas5713.c | 362 ++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/tas5713.h | 210 +++++++++++++++++++++++ + 8 files changed, 712 insertions(+) create mode 100644 sound/soc/bcm/hifiberry_amp.c create mode 100644 sound/soc/codecs/tas5713.c create mode 100644 sound/soc/codecs/tas5713.h -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index a8c60cf..8381041 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -784,6 +784,7 @@ CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m - CONFIG_SND_SIMPLE_CARD=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index aa62e55..5f6a1fa 100644 +index 5f7df9e..66bc839 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -674,6 +674,20 @@ static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { +@@ -678,6 +678,20 @@ static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { #endif @@ -116968,21 +117429,20 @@ index aa62e55..5f6a1fa 100644 #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) static struct platform_device snd_rpi_dac_device = { .name = "snd-rpi-dac", -@@ -869,6 +883,12 @@ void __init bcm2708_init(void) - i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); +@@ -876,6 +890,11 @@ void __init bcm2708_init(void) + i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); #endif +#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+ bcm_register_device(&snd_hifiberry_amp_device); -+ i2c_register_board_info(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); ++ bcm_register_device_dt(&snd_hifiberry_amp_device); ++ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); +#endif -+ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - bcm_register_device(&snd_rpi_dac_device); - bcm_register_device(&snd_pcm1794a_codec_device); + bcm_register_device_dt(&snd_rpi_dac_device); + bcm_register_device_dt(&snd_pcm1794a_codec_device); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 926a82b..a562ddf 100644 +index 7675501..40d27c1 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -40,6 +40,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DIGI @@ -117019,7 +117479,7 @@ index c02e3a2..17ea2b0 100644 obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o diff --git a/sound/soc/bcm/hifiberry_amp.c b/sound/soc/bcm/hifiberry_amp.c new file mode 100644 -index 0000000..1e87ee0 +index 0000000..1e87ee06 --- /dev/null +++ b/sound/soc/bcm/hifiberry_amp.c @@ -0,0 +1,106 @@ @@ -117130,10 +117590,10 @@ index 0000000..1e87ee0 +MODULE_DESCRIPTION("ASoC driver for HiFiBerry-AMP"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index 0a7aaf7..45dd4fd 100644 +index 8682bcf..f49e15d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig -@@ -107,6 +107,7 @@ config SND_SOC_ALL_CODECS +@@ -108,6 +108,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -117141,7 +117601,7 @@ index 0a7aaf7..45dd4fd 100644 select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC31XX if I2C select SND_SOC_TLV320AIC32X4 if I2C -@@ -612,6 +613,9 @@ config SND_SOC_TFA9879 +@@ -618,6 +619,9 @@ config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C @@ -117152,10 +117612,10 @@ index 0a7aaf7..45dd4fd 100644 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index 1548bf33..756f628 100644 +index c9bceba..078943d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile -@@ -107,6 +107,7 @@ snd-soc-sta529-objs := sta529.o +@@ -108,6 +108,7 @@ snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o snd-soc-tfa9879-objs := tfa9879.o @@ -117163,7 +117623,7 @@ index 1548bf33..756f628 100644 snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o -@@ -287,6 +288,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +@@ -289,6 +290,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o @@ -117756,1537 +118216,10 @@ index 0000000..8f019e0 + +#endif /* _TAS5713_H */ -From 0d81e702b6c48cf3f39d4a13588d273868554490 Mon Sep 17 00:00:00 2001 +From 2deb059167387273e5e3e83e463db54dbd460cad Mon Sep 17 00:00:00 2001 From: Phil Elwell -Date: Mon, 13 Oct 2014 11:47:53 +0100 -Subject: [PATCH 063/172] Improve __copy_to_user and __copy_from_user - performance - -Provide a __copy_from_user that uses memcpy. On BCM2708, use -optimised memcpy/memmove/memcmp/memset implementations. ---- - arch/arm/include/asm/string.h | 5 + - arch/arm/include/asm/uaccess.h | 1 + - arch/arm/lib/Makefile | 15 +- - arch/arm/lib/arm-mem.h | 159 ++++++++++++ - arch/arm/lib/copy_from_user.S | 4 +- - arch/arm/lib/exports_rpi.c | 37 +++ - arch/arm/lib/memcmp_rpi.S | 285 +++++++++++++++++++++ - arch/arm/lib/memcpy_rpi.S | 59 +++++ - arch/arm/lib/memcpymove.h | 506 +++++++++++++++++++++++++++++++++++++ - arch/arm/lib/memmove_rpi.S | 61 +++++ - arch/arm/lib/memset_rpi.S | 121 +++++++++ - arch/arm/lib/uaccess_with_memcpy.c | 112 +++++++- - 12 files changed, 1359 insertions(+), 6 deletions(-) - create mode 100644 arch/arm/lib/arm-mem.h - create mode 100644 arch/arm/lib/exports_rpi.c - create mode 100644 arch/arm/lib/memcmp_rpi.S - create mode 100644 arch/arm/lib/memcpy_rpi.S - create mode 100644 arch/arm/lib/memcpymove.h - create mode 100644 arch/arm/lib/memmove_rpi.S - create mode 100644 arch/arm/lib/memset_rpi.S - -diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h -index cf4f3aa..9fe7780 100644 ---- a/arch/arm/include/asm/string.h -+++ b/arch/arm/include/asm/string.h -@@ -24,6 +24,11 @@ extern void * memchr(const void *, int, __kernel_size_t); - #define __HAVE_ARCH_MEMSET - extern void * memset(void *, int, __kernel_size_t); - -+#ifdef CONFIG_MACH_BCM2708 -+#define __HAVE_ARCH_MEMCMP -+extern int memcmp(const void *, const void *, size_t); -+#endif -+ - extern void __memzero(void *ptr, __kernel_size_t n); - - #define memset(p,v,n) \ -diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h -index 4767eb9..bcd43de 100644 ---- a/arch/arm/include/asm/uaccess.h -+++ b/arch/arm/include/asm/uaccess.h -@@ -475,6 +475,7 @@ do { \ - - #ifdef CONFIG_MMU - extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n); -+extern unsigned long __must_check __copy_from_user_std(void *to, const void __user *from, unsigned long n); - extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n); - extern unsigned long __must_check __copy_to_user_std(void __user *to, const void *from, unsigned long n); - extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n); -diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile -index 0573faa..498f5dd 100644 ---- a/arch/arm/lib/Makefile -+++ b/arch/arm/lib/Makefile -@@ -6,15 +6,24 @@ - - lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ - csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ -- delay.o delay-loop.o findbit.o memchr.o memcpy.o \ -- memmove.o memset.o memzero.o setbit.o \ -- strchr.o strrchr.o \ -+ delay.o delay-loop.o findbit.o memchr.o memzero.o \ -+ setbit.o strchr.o strrchr.o \ - testchangebit.o testclearbit.o testsetbit.o \ - ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ - ucmpdi2.o lib1funcs.o div64.o \ - io-readsb.o io-writesb.o io-readsl.o io-writesl.o \ - call_with_stack.o bswapsdi2.o - -+# Choose optimised implementations for Raspberry Pi -+ifeq ($(CONFIG_MACH_BCM2708),y) -+ CFLAGS_uaccess_with_memcpy.o += -DCOPY_FROM_USER_THRESHOLD=1600 -+ CFLAGS_uaccess_with_memcpy.o += -DCOPY_TO_USER_THRESHOLD=672 -+ obj-$(CONFIG_MODULES) += exports_rpi.o -+ lib-y += memcpy_rpi.o memmove_rpi.o memset_rpi.o memcmp_rpi.o -+else -+ lib-y += memcpy.o memmove.o memset.o -+endif -+ - mmu-y := clear_user.o copy_page.o getuser.o putuser.o - - # the code in uaccess.S is not preemption safe and -diff --git a/arch/arm/lib/arm-mem.h b/arch/arm/lib/arm-mem.h -new file mode 100644 -index 0000000..5d4bda1 ---- /dev/null -+++ b/arch/arm/lib/arm-mem.h -@@ -0,0 +1,159 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+.macro myfunc fname -+ .func fname -+ .global fname -+fname: -+.endm -+ -+.macro preload_leading_step1 backwards, ptr, base -+/* If the destination is already 16-byte aligned, then we need to preload -+ * between 0 and prefetch_distance (inclusive) cache lines ahead so there -+ * are no gaps when the inner loop starts. -+ */ -+ .if backwards -+ sub ptr, base, #1 -+ bic ptr, ptr, #31 -+ .else -+ bic ptr, base, #31 -+ .endif -+ .set OFFSET, 0 -+ .rept prefetch_distance+1 -+ pld [ptr, #OFFSET] -+ .if backwards -+ .set OFFSET, OFFSET-32 -+ .else -+ .set OFFSET, OFFSET+32 -+ .endif -+ .endr -+.endm -+ -+.macro preload_leading_step2 backwards, ptr, base, leading_bytes, tmp -+/* However, if the destination is not 16-byte aligned, we may need to -+ * preload one more cache line than that. The question we need to ask is: -+ * are the leading bytes more than the amount by which the source -+ * pointer will be rounded down for preloading, and if so, by how many -+ * cache lines? -+ */ -+ .if backwards -+/* Here we compare against how many bytes we are into the -+ * cache line, counting down from the highest such address. -+ * Effectively, we want to calculate -+ * leading_bytes = dst&15 -+ * cacheline_offset = 31-((src-leading_bytes-1)&31) -+ * extra_needed = leading_bytes - cacheline_offset -+ * and test if extra_needed is <= 0, or rearranging: -+ * leading_bytes + (src-leading_bytes-1)&31 <= 31 -+ */ -+ mov tmp, base, lsl #32-5 -+ sbc tmp, tmp, leading_bytes, lsl #32-5 -+ adds tmp, tmp, leading_bytes, lsl #32-5 -+ bcc 61f -+ pld [ptr, #-32*(prefetch_distance+1)] -+ .else -+/* Effectively, we want to calculate -+ * leading_bytes = (-dst)&15 -+ * cacheline_offset = (src+leading_bytes)&31 -+ * extra_needed = leading_bytes - cacheline_offset -+ * and test if extra_needed is <= 0. -+ */ -+ mov tmp, base, lsl #32-5 -+ add tmp, tmp, leading_bytes, lsl #32-5 -+ rsbs tmp, tmp, leading_bytes, lsl #32-5 -+ bls 61f -+ pld [ptr, #32*(prefetch_distance+1)] -+ .endif -+61: -+.endm -+ -+.macro preload_trailing backwards, base, remain, tmp -+ /* We need either 0, 1 or 2 extra preloads */ -+ .if backwards -+ rsb tmp, base, #0 -+ mov tmp, tmp, lsl #32-5 -+ .else -+ mov tmp, base, lsl #32-5 -+ .endif -+ adds tmp, tmp, remain, lsl #32-5 -+ adceqs tmp, tmp, #0 -+ /* The instruction above has two effects: ensures Z is only -+ * set if C was clear (so Z indicates that both shifted quantities -+ * were 0), and clears C if Z was set (so C indicates that the sum -+ * of the shifted quantities was greater and not equal to 32) */ -+ beq 82f -+ .if backwards -+ sub tmp, base, #1 -+ bic tmp, tmp, #31 -+ .else -+ bic tmp, base, #31 -+ .endif -+ bcc 81f -+ .if backwards -+ pld [tmp, #-32*(prefetch_distance+1)] -+81: -+ pld [tmp, #-32*prefetch_distance] -+ .else -+ pld [tmp, #32*(prefetch_distance+2)] -+81: -+ pld [tmp, #32*(prefetch_distance+1)] -+ .endif -+82: -+.endm -+ -+.macro preload_all backwards, narrow_case, shift, base, remain, tmp0, tmp1 -+ .if backwards -+ sub tmp0, base, #1 -+ bic tmp0, tmp0, #31 -+ pld [tmp0] -+ sub tmp1, base, remain, lsl #shift -+ .else -+ bic tmp0, base, #31 -+ pld [tmp0] -+ add tmp1, base, remain, lsl #shift -+ sub tmp1, tmp1, #1 -+ .endif -+ bic tmp1, tmp1, #31 -+ cmp tmp1, tmp0 -+ beq 92f -+ .if narrow_case -+ /* In this case, all the data fits in either 1 or 2 cache lines */ -+ pld [tmp1] -+ .else -+91: -+ .if backwards -+ sub tmp0, tmp0, #32 -+ .else -+ add tmp0, tmp0, #32 -+ .endif -+ cmp tmp0, tmp1 -+ pld [tmp0] -+ bne 91b -+ .endif -+92: -+.endm -diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S -index 7a235b9..8aff879 100644 ---- a/arch/arm/lib/copy_from_user.S -+++ b/arch/arm/lib/copy_from_user.S -@@ -89,11 +89,13 @@ - - .text - --ENTRY(__copy_from_user) -+ENTRY(__copy_from_user_std) -+WEAK(__copy_from_user) - - #include "copy_template.S" - - ENDPROC(__copy_from_user) -+ENDPROC(__copy_from_user_std) - - .pushsection .fixup,"ax" - .align 0 -diff --git a/arch/arm/lib/exports_rpi.c b/arch/arm/lib/exports_rpi.c -new file mode 100644 -index 0000000..1f82604 ---- /dev/null -+++ b/arch/arm/lib/exports_rpi.c -@@ -0,0 +1,37 @@ -+/** -+ * Copyright (c) 2014, Raspberry Pi (Trading) Ltd. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+ -+EXPORT_SYMBOL(memcmp); -diff --git a/arch/arm/lib/memcmp_rpi.S b/arch/arm/lib/memcmp_rpi.S -new file mode 100644 -index 0000000..bf6e4ed ---- /dev/null -+++ b/arch/arm/lib/memcmp_rpi.S -@@ -0,0 +1,285 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+.macro memcmp_process_head unaligned -+ .if unaligned -+ ldr DAT0, [S_1], #4 -+ ldr DAT1, [S_1], #4 -+ ldr DAT2, [S_1], #4 -+ ldr DAT3, [S_1], #4 -+ .else -+ ldmia S_1!, {DAT0, DAT1, DAT2, DAT3} -+ .endif -+ ldmia S_2!, {DAT4, DAT5, DAT6, DAT7} -+.endm -+ -+.macro memcmp_process_tail -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ cmpeq DAT3, DAT7 -+ bne 200f -+.endm -+ -+.macro memcmp_leading_31bytes -+ movs DAT0, OFF, lsl #31 -+ ldrmib DAT0, [S_1], #1 -+ ldrcsh DAT1, [S_1], #2 -+ ldrmib DAT4, [S_2], #1 -+ ldrcsh DAT5, [S_2], #2 -+ movpl DAT0, #0 -+ movcc DAT1, #0 -+ movpl DAT4, #0 -+ movcc DAT5, #0 -+ submi N, N, #1 -+ subcs N, N, #2 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ bne 200f -+ movs DAT0, OFF, lsl #29 -+ ldrmi DAT0, [S_1], #4 -+ ldrcs DAT1, [S_1], #4 -+ ldrcs DAT2, [S_1], #4 -+ ldrmi DAT4, [S_2], #4 -+ ldmcsia S_2!, {DAT5, DAT6} -+ movpl DAT0, #0 -+ movcc DAT1, #0 -+ movcc DAT2, #0 -+ movpl DAT4, #0 -+ movcc DAT5, #0 -+ movcc DAT6, #0 -+ submi N, N, #4 -+ subcs N, N, #8 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ bne 200f -+ tst OFF, #16 -+ beq 105f -+ memcmp_process_head 1 -+ sub N, N, #16 -+ memcmp_process_tail -+105: -+.endm -+ -+.macro memcmp_trailing_15bytes unaligned -+ movs N, N, lsl #29 -+ .if unaligned -+ ldrcs DAT0, [S_1], #4 -+ ldrcs DAT1, [S_1], #4 -+ .else -+ ldmcsia S_1!, {DAT0, DAT1} -+ .endif -+ ldrmi DAT2, [S_1], #4 -+ ldmcsia S_2!, {DAT4, DAT5} -+ ldrmi DAT6, [S_2], #4 -+ movcc DAT0, #0 -+ movcc DAT1, #0 -+ movpl DAT2, #0 -+ movcc DAT4, #0 -+ movcc DAT5, #0 -+ movpl DAT6, #0 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ bne 200f -+ movs N, N, lsl #2 -+ ldrcsh DAT0, [S_1], #2 -+ ldrmib DAT1, [S_1] -+ ldrcsh DAT4, [S_2], #2 -+ ldrmib DAT5, [S_2] -+ movcc DAT0, #0 -+ movpl DAT1, #0 -+ movcc DAT4, #0 -+ movpl DAT5, #0 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ bne 200f -+.endm -+ -+.macro memcmp_long_inner_loop unaligned -+110: -+ memcmp_process_head unaligned -+ pld [S_2, #prefetch_distance*32 + 16] -+ memcmp_process_tail -+ memcmp_process_head unaligned -+ pld [S_1, OFF] -+ memcmp_process_tail -+ subs N, N, #32 -+ bhs 110b -+ /* Just before the final (prefetch_distance+1) 32-byte blocks, -+ * deal with final preloads */ -+ preload_trailing 0, S_1, N, DAT0 -+ preload_trailing 0, S_2, N, DAT0 -+ add N, N, #(prefetch_distance+2)*32 - 16 -+120: -+ memcmp_process_head unaligned -+ memcmp_process_tail -+ subs N, N, #16 -+ bhs 120b -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcmp_trailing_15bytes unaligned -+199: /* Reached end without detecting a difference */ -+ mov a1, #0 -+ setend le -+ pop {DAT1-DAT6, pc} -+.endm -+ -+.macro memcmp_short_inner_loop unaligned -+ subs N, N, #16 /* simplifies inner loop termination */ -+ blo 122f -+120: -+ memcmp_process_head unaligned -+ memcmp_process_tail -+ subs N, N, #16 -+ bhs 120b -+122: /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcmp_trailing_15bytes unaligned -+199: /* Reached end without detecting a difference */ -+ mov a1, #0 -+ setend le -+ pop {DAT1-DAT6, pc} -+.endm -+ -+/* -+ * int memcmp(const void *s1, const void *s2, size_t n); -+ * On entry: -+ * a1 = pointer to buffer 1 -+ * a2 = pointer to buffer 2 -+ * a3 = number of bytes to compare (as unsigned chars) -+ * On exit: -+ * a1 = >0/=0/<0 if s1 >/=/< s2 -+ */ -+ -+.set prefetch_distance, 2 -+ -+ENTRY(memcmp) -+ S_1 .req a1 -+ S_2 .req a2 -+ N .req a3 -+ DAT0 .req a4 -+ DAT1 .req v1 -+ DAT2 .req v2 -+ DAT3 .req v3 -+ DAT4 .req v4 -+ DAT5 .req v5 -+ DAT6 .req v6 -+ DAT7 .req ip -+ OFF .req lr -+ -+ push {DAT1-DAT6, lr} -+ setend be /* lowest-addressed bytes are most significant */ -+ -+ /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */ -+ cmp N, #(prefetch_distance+3)*32 - 1 -+ blo 170f -+ -+ /* Long case */ -+ /* Adjust N so that the decrement instruction can also test for -+ * inner loop termination. We want it to stop when there are -+ * (prefetch_distance+1) complete blocks to go. */ -+ sub N, N, #(prefetch_distance+2)*32 -+ preload_leading_step1 0, DAT0, S_1 -+ preload_leading_step1 0, DAT1, S_2 -+ tst S_2, #31 -+ beq 154f -+ rsb OFF, S_2, #0 /* no need to AND with 15 here */ -+ preload_leading_step2 0, DAT0, S_1, OFF, DAT2 -+ preload_leading_step2 0, DAT1, S_2, OFF, DAT2 -+ memcmp_leading_31bytes -+154: /* Second source now cacheline (32-byte) aligned; we have at -+ * least one prefetch to go. */ -+ /* Prefetch offset is best selected such that it lies in the -+ * first 8 of each 32 bytes - but it's just as easy to aim for -+ * the first one */ -+ and OFF, S_1, #31 -+ rsb OFF, OFF, #32*prefetch_distance -+ tst S_1, #3 -+ bne 140f -+ memcmp_long_inner_loop 0 -+140: memcmp_long_inner_loop 1 -+ -+170: /* Short case */ -+ teq N, #0 -+ beq 199f -+ preload_all 0, 0, 0, S_1, N, DAT0, DAT1 -+ preload_all 0, 0, 0, S_2, N, DAT0, DAT1 -+ tst S_2, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199f -+ ldrb DAT0, [S_1], #1 -+ ldrb DAT4, [S_2], #1 -+ cmp DAT0, DAT4 -+ bne 200f -+ tst S_2, #3 -+ bne 172b -+174: /* Second source now 4-byte aligned; we have 0 or more bytes to go */ -+ tst S_1, #3 -+ bne 140f -+ memcmp_short_inner_loop 0 -+140: memcmp_short_inner_loop 1 -+ -+200: /* Difference found: determine sign. */ -+ movhi a1, #1 -+ movlo a1, #-1 -+ setend le -+ pop {DAT1-DAT6, pc} -+ -+ .unreq S_1 -+ .unreq S_2 -+ .unreq N -+ .unreq DAT0 -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ .unreq DAT4 -+ .unreq DAT5 -+ .unreq DAT6 -+ .unreq DAT7 -+ .unreq OFF -+ENDPROC(memcmp) -diff --git a/arch/arm/lib/memcpy_rpi.S b/arch/arm/lib/memcpy_rpi.S -new file mode 100644 -index 0000000..15ff2bd ---- /dev/null -+++ b/arch/arm/lib/memcpy_rpi.S -@@ -0,0 +1,59 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+#include "memcpymove.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memcpy(void * restrict s1, const void * restrict s2, size_t n); -+ * On entry: -+ * a1 = pointer to destination -+ * a2 = pointer to source -+ * a3 = number of bytes to copy -+ * On exit: -+ * a1 preserved -+ */ -+ -+.set prefetch_distance, 3 -+ -+ENTRY(memcpy) -+ memcpy 0 -+ENDPROC(memcpy) -diff --git a/arch/arm/lib/memcpymove.h b/arch/arm/lib/memcpymove.h -new file mode 100644 -index 0000000..d8be584 ---- /dev/null -+++ b/arch/arm/lib/memcpymove.h -@@ -0,0 +1,506 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+.macro unaligned_words backwards, align, use_pld, words, r0, r1, r2, r3, r4, r5, r6, r7, r8 -+ .if words == 1 -+ .if backwards -+ mov r1, r0, lsl #32-align*8 -+ ldr r0, [S, #-4]! -+ orr r1, r1, r0, lsr #align*8 -+ str r1, [D, #-4]! -+ .else -+ mov r0, r1, lsr #align*8 -+ ldr r1, [S, #4]! -+ orr r0, r0, r1, lsl #32-align*8 -+ str r0, [D], #4 -+ .endif -+ .elseif words == 2 -+ .if backwards -+ ldr r1, [S, #-4]! -+ mov r2, r0, lsl #32-align*8 -+ ldr r0, [S, #-4]! -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r1, r2} -+ .else -+ ldr r1, [S, #4]! -+ mov r0, r2, lsr #align*8 -+ ldr r2, [S, #4]! -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ stmia D!, {r0, r1} -+ .endif -+ .elseif words == 4 -+ .if backwards -+ ldmdb S!, {r2, r3} -+ mov r4, r0, lsl #32-align*8 -+ ldmdb S!, {r0, r1} -+ orr r4, r4, r3, lsr #align*8 -+ mov r3, r3, lsl #32-align*8 -+ orr r3, r3, r2, lsr #align*8 -+ mov r2, r2, lsl #32-align*8 -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r1, r2, r3, r4} -+ .else -+ ldmib S!, {r1, r2} -+ mov r0, r4, lsr #align*8 -+ ldmib S!, {r3, r4} -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ mov r2, r2, lsr #align*8 -+ orr r2, r2, r3, lsl #32-align*8 -+ mov r3, r3, lsr #align*8 -+ orr r3, r3, r4, lsl #32-align*8 -+ stmia D!, {r0, r1, r2, r3} -+ .endif -+ .elseif words == 8 -+ .if backwards -+ ldmdb S!, {r4, r5, r6, r7} -+ mov r8, r0, lsl #32-align*8 -+ ldmdb S!, {r0, r1, r2, r3} -+ .if use_pld -+ pld [S, OFF] -+ .endif -+ orr r8, r8, r7, lsr #align*8 -+ mov r7, r7, lsl #32-align*8 -+ orr r7, r7, r6, lsr #align*8 -+ mov r6, r6, lsl #32-align*8 -+ orr r6, r6, r5, lsr #align*8 -+ mov r5, r5, lsl #32-align*8 -+ orr r5, r5, r4, lsr #align*8 -+ mov r4, r4, lsl #32-align*8 -+ orr r4, r4, r3, lsr #align*8 -+ mov r3, r3, lsl #32-align*8 -+ orr r3, r3, r2, lsr #align*8 -+ mov r2, r2, lsl #32-align*8 -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r5, r6, r7, r8} -+ stmdb D!, {r1, r2, r3, r4} -+ .else -+ ldmib S!, {r1, r2, r3, r4} -+ mov r0, r8, lsr #align*8 -+ ldmib S!, {r5, r6, r7, r8} -+ .if use_pld -+ pld [S, OFF] -+ .endif -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ mov r2, r2, lsr #align*8 -+ orr r2, r2, r3, lsl #32-align*8 -+ mov r3, r3, lsr #align*8 -+ orr r3, r3, r4, lsl #32-align*8 -+ mov r4, r4, lsr #align*8 -+ orr r4, r4, r5, lsl #32-align*8 -+ mov r5, r5, lsr #align*8 -+ orr r5, r5, r6, lsl #32-align*8 -+ mov r6, r6, lsr #align*8 -+ orr r6, r6, r7, lsl #32-align*8 -+ mov r7, r7, lsr #align*8 -+ orr r7, r7, r8, lsl #32-align*8 -+ stmia D!, {r0, r1, r2, r3} -+ stmia D!, {r4, r5, r6, r7} -+ .endif -+ .endif -+.endm -+ -+.macro memcpy_leading_15bytes backwards, align -+ movs DAT1, DAT2, lsl #31 -+ sub N, N, DAT2 -+ .if backwards -+ ldrmib DAT0, [S, #-1]! -+ ldrcsh DAT1, [S, #-2]! -+ strmib DAT0, [D, #-1]! -+ strcsh DAT1, [D, #-2]! -+ .else -+ ldrmib DAT0, [S], #1 -+ ldrcsh DAT1, [S], #2 -+ strmib DAT0, [D], #1 -+ strcsh DAT1, [D], #2 -+ .endif -+ movs DAT1, DAT2, lsl #29 -+ .if backwards -+ ldrmi DAT0, [S, #-4]! -+ .if align == 0 -+ ldmcsdb S!, {DAT1, DAT2} -+ .else -+ ldrcs DAT2, [S, #-4]! -+ ldrcs DAT1, [S, #-4]! -+ .endif -+ strmi DAT0, [D, #-4]! -+ stmcsdb D!, {DAT1, DAT2} -+ .else -+ ldrmi DAT0, [S], #4 -+ .if align == 0 -+ ldmcsia S!, {DAT1, DAT2} -+ .else -+ ldrcs DAT1, [S], #4 -+ ldrcs DAT2, [S], #4 -+ .endif -+ strmi DAT0, [D], #4 -+ stmcsia D!, {DAT1, DAT2} -+ .endif -+.endm -+ -+.macro memcpy_trailing_15bytes backwards, align -+ movs N, N, lsl #29 -+ .if backwards -+ .if align == 0 -+ ldmcsdb S!, {DAT0, DAT1} -+ .else -+ ldrcs DAT1, [S, #-4]! -+ ldrcs DAT0, [S, #-4]! -+ .endif -+ ldrmi DAT2, [S, #-4]! -+ stmcsdb D!, {DAT0, DAT1} -+ strmi DAT2, [D, #-4]! -+ .else -+ .if align == 0 -+ ldmcsia S!, {DAT0, DAT1} -+ .else -+ ldrcs DAT0, [S], #4 -+ ldrcs DAT1, [S], #4 -+ .endif -+ ldrmi DAT2, [S], #4 -+ stmcsia D!, {DAT0, DAT1} -+ strmi DAT2, [D], #4 -+ .endif -+ movs N, N, lsl #2 -+ .if backwards -+ ldrcsh DAT0, [S, #-2]! -+ ldrmib DAT1, [S, #-1] -+ strcsh DAT0, [D, #-2]! -+ strmib DAT1, [D, #-1] -+ .else -+ ldrcsh DAT0, [S], #2 -+ ldrmib DAT1, [S] -+ strcsh DAT0, [D], #2 -+ strmib DAT1, [D] -+ .endif -+.endm -+ -+.macro memcpy_long_inner_loop backwards, align -+ .if align != 0 -+ .if backwards -+ ldr DAT0, [S, #-align]! -+ .else -+ ldr LAST, [S, #-align]! -+ .endif -+ .endif -+110: -+ .if align == 0 -+ .if backwards -+ ldmdb S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ pld [S, OFF] -+ stmdb D!, {DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT0, DAT1, DAT2, DAT3} -+ .else -+ ldmia S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ pld [S, OFF] -+ stmia D!, {DAT0, DAT1, DAT2, DAT3} -+ stmia D!, {DAT4, DAT5, DAT6, LAST} -+ .endif -+ .else -+ unaligned_words backwards, align, 1, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST -+ .endif -+ subs N, N, #32 -+ bhs 110b -+ /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */ -+ preload_trailing backwards, S, N, OFF -+ add N, N, #(prefetch_distance+2)*32 - 32 -+120: -+ .if align == 0 -+ .if backwards -+ ldmdb S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT0, DAT1, DAT2, DAT3} -+ .else -+ ldmia S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ stmia D!, {DAT0, DAT1, DAT2, DAT3} -+ stmia D!, {DAT4, DAT5, DAT6, LAST} -+ .endif -+ .else -+ unaligned_words backwards, align, 0, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST -+ .endif -+ subs N, N, #32 -+ bhs 120b -+ tst N, #16 -+ .if align == 0 -+ .if backwards -+ ldmnedb S!, {DAT0, DAT1, DAT2, LAST} -+ stmnedb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldmneia S!, {DAT0, DAT1, DAT2, LAST} -+ stmneia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ .else -+ beq 130f -+ unaligned_words backwards, align, 0, 4, DAT0, DAT1, DAT2, DAT3, LAST -+130: -+ .endif -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ .if align != 0 -+ add S, S, #align -+ .endif -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {DAT3, DAT4, DAT5, DAT6, DAT7} -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy_medium_inner_loop backwards, align -+120: -+ .if backwards -+ .if align == 0 -+ ldmdb S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldr LAST, [S, #-4]! -+ ldr DAT2, [S, #-4]! -+ ldr DAT1, [S, #-4]! -+ ldr DAT0, [S, #-4]! -+ .endif -+ stmdb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ .if align == 0 -+ ldmia S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldr DAT0, [S], #4 -+ ldr DAT1, [S], #4 -+ ldr DAT2, [S], #4 -+ ldr LAST, [S], #4 -+ .endif -+ stmia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ subs N, N, #16 -+ bhs 120b -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy_short_inner_loop backwards, align -+ tst N, #16 -+ .if backwards -+ .if align == 0 -+ ldmnedb S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldrne LAST, [S, #-4]! -+ ldrne DAT2, [S, #-4]! -+ ldrne DAT1, [S, #-4]! -+ ldrne DAT0, [S, #-4]! -+ .endif -+ stmnedb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ .if align == 0 -+ ldmneia S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldrne DAT0, [S], #4 -+ ldrne DAT1, [S], #4 -+ ldrne DAT2, [S], #4 -+ ldrne LAST, [S], #4 -+ .endif -+ stmneia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy backwards -+ D .req a1 -+ S .req a2 -+ N .req a3 -+ DAT0 .req a4 -+ DAT1 .req v1 -+ DAT2 .req v2 -+ DAT3 .req v3 -+ DAT4 .req v4 -+ DAT5 .req v5 -+ DAT6 .req v6 -+ DAT7 .req sl -+ LAST .req ip -+ OFF .req lr -+ -+ .cfi_startproc -+ -+ push {D, DAT1, DAT2, lr} -+ -+ .cfi_def_cfa_offset 16 -+ .cfi_rel_offset D, 0 -+ .cfi_undefined S -+ .cfi_undefined N -+ .cfi_undefined DAT0 -+ .cfi_rel_offset DAT1, 4 -+ .cfi_rel_offset DAT2, 8 -+ .cfi_undefined LAST -+ .cfi_rel_offset lr, 12 -+ -+ .if backwards -+ add D, D, N -+ add S, S, N -+ .endif -+ -+ /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */ -+ cmp N, #31 -+ blo 170f -+ /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */ -+ cmp N, #(prefetch_distance+3)*32 - 1 -+ blo 160f -+ -+ /* Long case */ -+ push {DAT3, DAT4, DAT5, DAT6, DAT7} -+ -+ .cfi_def_cfa_offset 36 -+ .cfi_rel_offset D, 20 -+ .cfi_rel_offset DAT1, 24 -+ .cfi_rel_offset DAT2, 28 -+ .cfi_rel_offset DAT3, 0 -+ .cfi_rel_offset DAT4, 4 -+ .cfi_rel_offset DAT5, 8 -+ .cfi_rel_offset DAT6, 12 -+ .cfi_rel_offset DAT7, 16 -+ .cfi_rel_offset lr, 32 -+ -+ /* Adjust N so that the decrement instruction can also test for -+ * inner loop termination. We want it to stop when there are -+ * (prefetch_distance+1) complete blocks to go. */ -+ sub N, N, #(prefetch_distance+2)*32 -+ preload_leading_step1 backwards, DAT0, S -+ .if backwards -+ /* Bug in GAS: it accepts, but mis-assembles the instruction -+ * ands DAT2, D, #60, 2 -+ * which sets DAT2 to the number of leading bytes until destination is aligned and also clears C (sets borrow) -+ */ -+ .word 0xE210513C -+ beq 154f -+ .else -+ ands DAT2, D, #15 -+ beq 154f -+ rsb DAT2, DAT2, #16 /* number of leading bytes until destination aligned */ -+ .endif -+ preload_leading_step2 backwards, DAT0, S, DAT2, OFF -+ memcpy_leading_15bytes backwards, 1 -+154: /* Destination now 16-byte aligned; we have at least one prefetch as well as at least one 16-byte output block */ -+ /* Prefetch offset is best selected such that it lies in the first 8 of each 32 bytes - but it's just as easy to aim for the first one */ -+ .if backwards -+ rsb OFF, S, #3 -+ and OFF, OFF, #28 -+ sub OFF, OFF, #32*(prefetch_distance+1) -+ .else -+ and OFF, S, #28 -+ rsb OFF, OFF, #32*prefetch_distance -+ .endif -+ movs DAT0, S, lsl #31 -+ bhi 157f -+ bcs 156f -+ bmi 155f -+ memcpy_long_inner_loop backwards, 0 -+155: memcpy_long_inner_loop backwards, 1 -+156: memcpy_long_inner_loop backwards, 2 -+157: memcpy_long_inner_loop backwards, 3 -+ -+ .cfi_def_cfa_offset 16 -+ .cfi_rel_offset D, 0 -+ .cfi_rel_offset DAT1, 4 -+ .cfi_rel_offset DAT2, 8 -+ .cfi_same_value DAT3 -+ .cfi_same_value DAT4 -+ .cfi_same_value DAT5 -+ .cfi_same_value DAT6 -+ .cfi_same_value DAT7 -+ .cfi_rel_offset lr, 12 -+ -+160: /* Medium case */ -+ preload_all backwards, 0, 0, S, N, DAT2, OFF -+ sub N, N, #16 /* simplifies inner loop termination */ -+ .if backwards -+ ands DAT2, D, #15 -+ beq 164f -+ .else -+ ands DAT2, D, #15 -+ beq 164f -+ rsb DAT2, DAT2, #16 -+ .endif -+ memcpy_leading_15bytes backwards, align -+164: /* Destination now 16-byte aligned; we have at least one 16-byte output block */ -+ tst S, #3 -+ bne 140f -+ memcpy_medium_inner_loop backwards, 0 -+140: memcpy_medium_inner_loop backwards, 1 -+ -+170: /* Short case, less than 31 bytes, so no guarantee of at least one 16-byte block */ -+ teq N, #0 -+ beq 199f -+ preload_all backwards, 1, 0, S, N, DAT2, LAST -+ tst D, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199f -+ .if backwards -+ ldrb DAT0, [S, #-1]! -+ strb DAT0, [D, #-1]! -+ .else -+ ldrb DAT0, [S], #1 -+ strb DAT0, [D], #1 -+ .endif -+ tst D, #3 -+ bne 172b -+174: /* Destination now 4-byte aligned; we have 0 or more output bytes to go */ -+ tst S, #3 -+ bne 140f -+ memcpy_short_inner_loop backwards, 0 -+140: memcpy_short_inner_loop backwards, 1 -+ -+ .cfi_endproc -+ -+ .unreq D -+ .unreq S -+ .unreq N -+ .unreq DAT0 -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ .unreq DAT4 -+ .unreq DAT5 -+ .unreq DAT6 -+ .unreq DAT7 -+ .unreq LAST -+ .unreq OFF -+.endm -diff --git a/arch/arm/lib/memmove_rpi.S b/arch/arm/lib/memmove_rpi.S -new file mode 100644 -index 0000000..8b0760c ---- /dev/null -+++ b/arch/arm/lib/memmove_rpi.S -@@ -0,0 +1,61 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+#include "memcpymove.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memmove(void *s1, const void *s2, size_t n); -+ * On entry: -+ * a1 = pointer to destination -+ * a2 = pointer to source -+ * a3 = number of bytes to copy -+ * On exit: -+ * a1 preserved -+ */ -+ -+.set prefetch_distance, 3 -+ -+ENTRY(memmove) -+ cmp a2, a1 -+ bpl memcpy /* pl works even over -1 - 0 and 0x7fffffff - 0x80000000 boundaries */ -+ memcpy 1 -+ENDPROC(memmove) -diff --git a/arch/arm/lib/memset_rpi.S b/arch/arm/lib/memset_rpi.S -new file mode 100644 -index 0000000..2cde883 ---- /dev/null -+++ b/arch/arm/lib/memset_rpi.S -@@ -0,0 +1,121 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memset(void *s, int c, size_t n); -+ * On entry: -+ * a1 = pointer to buffer to fill -+ * a2 = byte pattern to fill with (caller-narrowed) -+ * a3 = number of bytes to fill -+ * On exit: -+ * a1 preserved -+ */ -+ENTRY(memset) -+ S .req a1 -+ DAT0 .req a2 -+ N .req a3 -+ DAT1 .req a4 -+ DAT2 .req ip -+ DAT3 .req lr -+ -+ orr DAT0, DAT0, lsl #8 -+ push {S, lr} -+ orr DAT0, DAT0, lsl #16 -+ mov DAT1, DAT0 -+ -+ /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */ -+ cmp N, #31 -+ blo 170f -+ -+161: sub N, N, #16 /* simplifies inner loop termination */ -+ /* Leading words and bytes */ -+ tst S, #15 -+ beq 164f -+ rsb DAT3, S, #0 /* bits 0-3 = number of leading bytes until aligned */ -+ movs DAT2, DAT3, lsl #31 -+ submi N, N, #1 -+ strmib DAT0, [S], #1 -+ subcs N, N, #2 -+ strcsh DAT0, [S], #2 -+ movs DAT2, DAT3, lsl #29 -+ submi N, N, #4 -+ strmi DAT0, [S], #4 -+ subcs N, N, #8 -+ stmcsia S!, {DAT0, DAT1} -+164: /* Delayed set up of DAT2 and DAT3 so we could use them as scratch registers above */ -+ mov DAT2, DAT0 -+ mov DAT3, DAT0 -+ /* Now the inner loop of 16-byte stores */ -+165: stmia S!, {DAT0, DAT1, DAT2, DAT3} -+ subs N, N, #16 -+ bhs 165b -+166: /* Trailing words and bytes */ -+ movs N, N, lsl #29 -+ stmcsia S!, {DAT0, DAT1} -+ strmi DAT0, [S], #4 -+ movs N, N, lsl #2 -+ strcsh DAT0, [S], #2 -+ strmib DAT0, [S] -+199: pop {S, pc} -+ -+170: /* Short case */ -+ mov DAT2, DAT0 -+ mov DAT3, DAT0 -+ tst S, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199b -+ strb DAT0, [S], #1 -+ tst S, #3 -+ bne 172b -+174: tst N, #16 -+ stmneia S!, {DAT0, DAT1, DAT2, DAT3} -+ b 166b -+ -+ .unreq S -+ .unreq DAT0 -+ .unreq N -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ENDPROC(memset) -diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c -index 3e58d71..0622891 100644 ---- a/arch/arm/lib/uaccess_with_memcpy.c -+++ b/arch/arm/lib/uaccess_with_memcpy.c -@@ -22,6 +22,14 @@ - #include - #include - -+#ifndef COPY_FROM_USER_THRESHOLD -+#define COPY_FROM_USER_THRESHOLD 64 -+#endif -+ -+#ifndef COPY_TO_USER_THRESHOLD -+#define COPY_TO_USER_THRESHOLD 64 -+#endif -+ - static int - pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) - { -@@ -85,7 +93,44 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) - return 1; - } - --static unsigned long noinline -+static int -+pin_page_for_read(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) -+{ -+ unsigned long addr = (unsigned long)_addr; -+ pgd_t *pgd; -+ pmd_t *pmd; -+ pte_t *pte; -+ pud_t *pud; -+ spinlock_t *ptl; -+ -+ pgd = pgd_offset(current->mm, addr); -+ if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd))) -+ { -+ return 0; -+ } -+ pud = pud_offset(pgd, addr); -+ if (unlikely(pud_none(*pud) || pud_bad(*pud))) -+ { -+ return 0; -+ } -+ -+ pmd = pmd_offset(pud, addr); -+ if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd))) -+ return 0; -+ -+ pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); -+ if (unlikely(!pte_present(*pte) || !pte_young(*pte))) { -+ pte_unmap_unlock(pte, ptl); -+ return 0; -+ } -+ -+ *ptep = pte; -+ *ptlp = ptl; -+ -+ return 1; -+} -+ -+unsigned long noinline - __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) - { - int atomic; -@@ -135,6 +180,54 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) - return n; - } - -+unsigned long noinline -+__copy_from_user_memcpy(void *to, const void __user *from, unsigned long n) -+{ -+ int atomic; -+ -+ if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { -+ memcpy(to, (const void *)from, n); -+ return 0; -+ } -+ -+ /* the mmap semaphore is taken only if not in an atomic context */ -+ atomic = in_atomic(); -+ -+ if (!atomic) -+ down_read(¤t->mm->mmap_sem); -+ while (n) { -+ pte_t *pte; -+ spinlock_t *ptl; -+ int tocopy; -+ -+ while (!pin_page_for_read(from, &pte, &ptl)) { -+ char temp; -+ if (!atomic) -+ up_read(¤t->mm->mmap_sem); -+ if (__get_user(temp, (char __user *)from)) -+ goto out; -+ if (!atomic) -+ down_read(¤t->mm->mmap_sem); -+ } -+ -+ tocopy = (~(unsigned long)from & ~PAGE_MASK) + 1; -+ if (tocopy > n) -+ tocopy = n; -+ -+ memcpy(to, (const void *)from, tocopy); -+ to += tocopy; -+ from += tocopy; -+ n -= tocopy; -+ -+ pte_unmap_unlock(pte, ptl); -+ } -+ if (!atomic) -+ up_read(¤t->mm->mmap_sem); -+ -+out: -+ return n; -+} -+ - unsigned long - __copy_to_user(void __user *to, const void *from, unsigned long n) - { -@@ -145,10 +238,25 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) - * With frame pointer disabled, tail call optimization kicks in - * as well making this test almost invisible. - */ -- if (n < 64) -+ if (n < COPY_TO_USER_THRESHOLD) - return __copy_to_user_std(to, from, n); - return __copy_to_user_memcpy(to, from, n); - } -+ -+unsigned long -+__copy_from_user(void *to, const void __user *from, unsigned long n) -+{ -+ /* -+ * This test is stubbed out of the main function above to keep -+ * the overhead for small copies low by avoiding a large -+ * register dump on the stack just to reload them right away. -+ * With frame pointer disabled, tail call optimization kicks in -+ * as well making this test almost invisible. -+ */ -+ if (n < COPY_FROM_USER_THRESHOLD) -+ return __copy_from_user_std(to, from, n); -+ return __copy_from_user_memcpy(to, from, n); -+} - - static unsigned long noinline - __clear_user_memset(void __user *addr, unsigned long n) - -From 35fe870af907a92f71132723506d92329c3de6fe Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 1 Sep 2014 16:35:56 +0100 -Subject: [PATCH 064/172] bcm2708: Allow option card devices to be configured +Date: Mon, 13 Apr 2015 19:14:18 +0100 +Subject: [PATCH 052/104] bcm2708: Allow option card devices to be configured via DT If the kernel is built with Device Tree support, and if a DT blob @@ -119295,421 +118228,9 @@ for option cards are not created. This avoids both the need to blacklist unwanted devices, and the need to update the board support code with each new device. --- - arch/arm/mach-bcm2708/bcm2708.c | 70 ++++++++++++++++++++--------------------- - drivers/dma/bcm2708-dmaengine.c | 14 ++++----- - drivers/mmc/host/bcm2835-mmc.c | 24 +++++++------- - sound/soc/bcm/bcm2708-i2s.c | 29 +++++++++++------ - sound/soc/bcm/bcm2835-i2s.c | 1 + - 5 files changed, 75 insertions(+), 63 deletions(-) + sound/soc/bcm/bcm2835-i2s.c | 1 + + 1 file changed, 1 insertion(+) -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 5f6a1fa..2c6a29d 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -93,6 +94,8 @@ static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; - -+static unsigned use_dt = 0; -+ - static void __init bcm2708_init_led(void); - - void __init bcm2708_init_irq(void) -@@ -514,7 +517,6 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - --#ifndef CONFIG_OF - static struct resource bcm2708_spi_resources[] = { - { - .start = SPI0_BASE, -@@ -538,7 +540,6 @@ static struct platform_device bcm2708_spi_device = { - .dma_mask = &bcm2708_spi_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, - }; --#endif - - #ifdef CONFIG_BCM2708_SPIDEV - static struct spi_board_info bcm2708_spi_devices[] = { -@@ -560,7 +561,6 @@ static struct spi_board_info bcm2708_spi_devices[] = { - }; - #endif - --#ifndef CONFIG_OF - static struct resource bcm2708_bsc0_resources[] = { - { - .start = BSC0_BASE, -@@ -599,7 +599,6 @@ static struct platform_device bcm2708_bsc1_device = { - .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), - .resource = bcm2708_bsc1_resources, - }; --#endif - - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", -@@ -609,7 +608,7 @@ static struct platform_device bcm2835_thermal_device = { - .name = "bcm2835_thermal", - }; - --#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - static struct resource bcm2708_i2s_resources[] = { - { - .start = I2S_BASE, -@@ -731,14 +730,14 @@ int __init bcm_register_device(struct platform_device *pdev) - } - - /* -- * Use this macro for platform devices that are present in the Device Tree. -- * This way the device is only added on non-DT builds. -+ * Use these macros for platform and i2c devices that are present in the -+ * Device Tree. This way the devices are only added on non-DT systems. - */ --#ifdef CONFIG_OF --#define bcm_register_device_dt(pdev) --#else --#define bcm_register_device_dt(pdev) bcm_register_device(pdev) --#endif -+#define bcm_register_device_dt(pdev) \ -+ if (!use_dt) bcm_register_device(pdev) -+ -+#define i2c_register_board_info_dt(busnum, info, n) \ -+ if (!use_dt) i2c_register_board_info(busnum, info, n) - - int calc_rsts(int partition) - { -@@ -814,7 +813,9 @@ static void __init bcm2708_dt_init(void) - ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - if (ret) { - pr_err("of_platform_populate failed: %d\n", ret); -- BUG(); -+ /* Proceed as if CONFIG_OF was not defined */ -+ } else { -+ use_dt = 1; - } - } - #else -@@ -842,7 +843,7 @@ void __init bcm2708_init(void) - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; - w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -- platform_device_register(&w1_device); -+ bcm_register_device_dt(&w1_device); - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -@@ -857,46 +858,45 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ - bcm_register_device_dt(&bcm2708_spi_device); - bcm_register_device_dt(&bcm2708_bsc0_device); - bcm_register_device_dt(&bcm2708_bsc1_device); - -- bcm_register_device(&bcm2835_hwmon_device); -- bcm_register_device(&bcm2835_thermal_device); -- --#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -- bcm_register_device(&bcm2708_i2s_device); -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+ bcm_register_device_dt(&bcm2708_i2s_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -- bcm_register_device(&snd_hifiberry_dac_device); -- bcm_register_device(&snd_pcm5102a_codec_device); -+ bcm_register_device_dt(&snd_hifiberry_dac_device); -+ bcm_register_device_dt(&snd_pcm5102a_codec_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -- bcm_register_device(&snd_rpi_hifiberry_dacplus_device); -- i2c_register_board_info(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); -+ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -- bcm_register_device(&snd_hifiberry_digi_device); -- i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+ bcm_register_device_dt(&snd_hifiberry_digi_device); -+ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -- bcm_register_device(&snd_hifiberry_amp_device); -- i2c_register_board_info(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); -+ bcm_register_device_dt(&snd_hifiberry_amp_device); -+ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); - #endif - -- - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -- bcm_register_device(&snd_rpi_dac_device); -- bcm_register_device(&snd_pcm1794a_codec_device); -+ bcm_register_device_dt(&snd_rpi_dac_device); -+ bcm_register_device_dt(&snd_pcm1794a_codec_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -- bcm_register_device(&snd_rpi_iqaudio_dac_device); -- i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); - #endif - - -@@ -1041,9 +1041,9 @@ static struct platform_device bcm2708_led_device = { - - static void __init bcm2708_init_led(void) - { -- bcm2708_leds[0].gpio = disk_led_gpio; -- bcm2708_leds[0].active_low = disk_led_active_low; -- platform_device_register(&bcm2708_led_device); -+ bcm2708_leds[0].gpio = disk_led_gpio; -+ bcm2708_leds[0].active_low = disk_led_active_low; -+ bcm_register_device_dt(&bcm2708_led_device); - } - #else - static inline void bcm2708_init_led(void) -diff --git a/drivers/dma/bcm2708-dmaengine.c b/drivers/dma/bcm2708-dmaengine.c -index 10463db..3f9be02 100644 ---- a/drivers/dma/bcm2708-dmaengine.c -+++ b/drivers/dma/bcm2708-dmaengine.c -@@ -42,7 +42,7 @@ - #include - #include - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - /* dma manager */ - #include -@@ -721,7 +721,7 @@ static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - } - } - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) - { - struct bcm2835_chan *c; -@@ -784,7 +784,7 @@ static const struct of_device_id bcm2835_dma_of_match[] = { - }; - MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, - struct of_dma *ofdma) - { -@@ -817,7 +817,7 @@ static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, - static int bcm2835_dma_probe(struct platform_device *pdev) - { - struct bcm2835_dmadev *od; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - struct resource *res; - void __iomem *base; - uint32_t chans_available; -@@ -830,10 +830,10 @@ static int bcm2835_dma_probe(struct platform_device *pdev) - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - -- /* If CONFIG_OF is selected, device tree is used */ -+ /* If CONFIG_ARCH_BCM2835 is selected, device tree is used */ - /* hence the difference between probing */ - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (rc) -@@ -987,7 +987,7 @@ static int bcm2835_dma_remove(struct platform_device *pdev) - return 0; - } - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - - static struct platform_driver bcm2835_dma_driver = { -diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c -index 92febc3..79808cb 100644 ---- a/drivers/mmc/host/bcm2835-mmc.c -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -42,7 +42,7 @@ - #include "sdhci.h" - - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - #define BCM2835_CLOCK_FREQ 250000000 - #endif - -@@ -658,7 +658,7 @@ void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd - } - - timeout = jiffies; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - if (!cmd->data && cmd->busy_timeout > 9000) - timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; - else -@@ -958,7 +958,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - struct bcm2835_host *host = dev_id; - u32 intmask, mask, unexpected = 0; - int max_loops = 16; --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - int cardint = 0; - #endif - -@@ -989,7 +989,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - mmc_hostname(host->mmc)); - - if (intmask & SDHCI_INT_CARD_INT) { --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - cardint = 1; - #else - bcm2835_mmc_enable_sdio_irq_nolock(host, false); -@@ -1022,7 +1022,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - bcm2835_mmc_dumpregs(host); - } - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - if (cardint) - mmc_signal_sdio_irq(host->mmc); - #endif -@@ -1030,7 +1030,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - return result; - } - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) - { - struct bcm2835_host *host = dev_id; -@@ -1284,7 +1284,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - - /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ - host->timeout_clk = mmc->f_max / 1000; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; - #endif - /* host controller capabilities */ -@@ -1341,7 +1341,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - init_waitqueue_head(&host->buf_ready_int); - - bcm2835_mmc_init(host, 0); --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - ret = request_irq(host->irq, bcm2835_mmc_irq, 0 /*IRQF_SHARED*/, - mmc_hostname(mmc), host); - #else -@@ -1370,7 +1370,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - static int bcm2835_mmc_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - struct device_node *node = dev->of_node; - struct clk *clk; - #endif -@@ -1379,7 +1379,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - - int ret; - struct mmc_host *mmc; --#if !defined(CONFIG_OF) && !defined(FORCE_PIO) -+#if !defined(CONFIG_ARCH_BCM2835) && !defined(FORCE_PIO) - dma_cap_mask_t mask; - #endif - -@@ -1404,7 +1404,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - - host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - #ifndef FORCE_PIO - dma_cap_zero(mask); - /* we don't care about the channel, any would work */ -@@ -1454,7 +1454,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - } - - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - mmc->caps |= MMC_CAP_4_BIT_DATA; - #else - mmc_of_parse(mmc); -diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c -index 0b5322a..a3b65dc 100644 ---- a/sound/soc/bcm/bcm2708-i2s.c -+++ b/sound/soc/bcm/bcm2708-i2s.c -@@ -493,15 +493,19 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, - divf = dividend & BCM2708_CLK_DIVF_MASK; - } - -- /* Set clock divider */ -- regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -- | BCM2708_CLK_DIVI(divi) -- | BCM2708_CLK_DIVF(divf)); -- -- /* Setup clock, but don't start it yet */ -- regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -- | BCM2708_CLK_MASH(mash) -- | BCM2708_CLK_SRC(clk_src)); -+ /* Clock should only be set up here if CPU is clock master */ -+ if (((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS) || -+ ((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFM)) { -+ /* Set clock divider */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_DIVI(divi) -+ | BCM2708_CLK_DIVF(divf)); -+ -+ /* Setup clock, but don't start it yet */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_MASH(mash) -+ | BCM2708_CLK_SRC(clk_src)); -+ } - - /* Setup the frame format */ - format = BCM2708_I2S_CHEN; -@@ -981,12 +985,19 @@ static int bcm2708_i2s_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_i2s_of_match[] = { -+ { .compatible = "brcm,bcm2708-i2s", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_i2s_of_match); -+ - static struct platform_driver bcm2708_i2s_driver = { - .probe = bcm2708_i2s_probe, - .remove = bcm2708_i2s_remove, - .driver = { - .name = "bcm2708-i2s", - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_i2s_of_match, - }, - }; - diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 03fa1cb..c816526 100644 --- a/sound/soc/bcm/bcm2835-i2s.c @@ -119723,225 +118244,29 @@ index 03fa1cb..c816526 100644 static struct platform_driver bcm2835_i2s_driver = { .probe = bcm2835_i2s_probe, -From 905b491a759bb3264e073771cbd200e747ef7a02 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 12 Nov 2014 17:07:02 +0000 -Subject: [PATCH 065/172] Adding Device Tree support for some RPi audio cards +From 9823900486e99df17ba7f5e7b782750b7bce286b Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 18:45:39 +0100 +Subject: [PATCH 053/104] Adding Device Tree support for some RPi audio cards --- - arch/arm/boot/dts/Makefile | 4 ++ - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 81 +++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708-rpi-b.dts | 19 +++++- - arch/arm/boot/dts/bcm2708.dtsi | 18 ++++-- - arch/arm/boot/dts/hifiberry-dac-overlay.dts | 34 +++++++++++ - arch/arm/boot/dts/hifiberry-dacplus-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/hifiberry-digi-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/iqaudio-dac-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/iqaudio-dacplus-overlay.dts | 39 ++++++++++++ - sound/soc/bcm/hifiberry_dac.c | 22 +++++++ - sound/soc/bcm/hifiberry_dacplus.c | 22 +++++++ - sound/soc/bcm/hifiberry_digi.c | 22 +++++++ - sound/soc/bcm/iqaudio-dac.c | 16 +++++ - sound/soc/codecs/pcm5102a.c | 7 +++ - 14 files changed, 395 insertions(+), 6 deletions(-) - create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-plus.dts + arch/arm/boot/dts/hifiberry-dac-overlay.dts | 34 +++++++++++++++++++++ + arch/arm/boot/dts/hifiberry-dacplus-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/hifiberry-digi-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/iqaudio-dac-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/iqaudio-dacplus-overlay.dts | 39 +++++++++++++++++++++++++ + sound/soc/bcm/hifiberry_dac.c | 22 ++++++++++++++ + sound/soc/bcm/hifiberry_dacplus.c | 22 ++++++++++++++ + sound/soc/bcm/hifiberry_digi.c | 22 ++++++++++++++ + sound/soc/bcm/iqaudio-dac.c | 16 ++++++++++ + sound/soc/codecs/pcm5102a.c | 7 +++++ + 10 files changed, 279 insertions(+) create mode 100644 arch/arm/boot/dts/hifiberry-dac-overlay.dts create mode 100644 arch/arm/boot/dts/hifiberry-dacplus-overlay.dts create mode 100644 arch/arm/boot/dts/hifiberry-digi-overlay.dts create mode 100644 arch/arm/boot/dts/iqaudio-dac-overlay.dts create mode 100644 arch/arm/boot/dts/iqaudio-dacplus-overlay.dts -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index d177201..76e5f96 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -54,6 +54,7 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb -+dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -@@ -557,6 +558,9 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt6589-aquaris5.dtb \ - mt8127-moose.dtb \ - mt8135-evbp1.dtb - -+targets += dtbs dtbs_install -+targets += $(dtb-y) -+ - endif - - always := $(dtb-y) -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -new file mode 100644 -index 0000000..983c23f ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -0,0 +1,81 @@ -+/dts-v1/; -+ -+/include/ "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi Model B+"; -+ -+ aliases { -+ spi0 = &spi0; -+ i2c0 = &i2c0; -+ i2c1 = &i2c1; -+ i2s = &i2s; -+ gpio = &gpio; -+ sound = &sound; -+ }; -+ -+ sound: sound { -+ }; -+}; -+ -+&gpio { -+ spi0_pins: spi0_pins { -+ brcm,pins = <7 8 9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ i2c0_pins: i2c0 { -+ brcm,pins = <0 1>; -+ brcm,function = <4>; -+ }; -+ -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; -+ }; -+ -+ i2s_pins: i2s { -+ brcm,pins = <18 19 20 21>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins>; -+ -+ spidev@0{ -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+ -+ spidev@1{ -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+}; -+ -+&i2c0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c0_pins>; -+ clock-frequency = <100000>; -+}; -+ -+&i2c1 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c1_pins>; -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 5893122..d8c6d15 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -4,12 +4,18 @@ - - / { - compatible = "brcm,bcm2708"; -- model = "Raspberry Pi"; -+ model = "Raspberry Pi Model B"; - - aliases { - spi0 = &spi0; - i2c0 = &i2c0; - i2c1 = &i2c1; -+ i2s = &i2s; -+ gpio = &gpio; -+ sound = &sound; -+ }; -+ -+ sound: sound { - }; - }; - -@@ -28,6 +34,11 @@ - brcm,pins = <2 3>; - brcm,function = <4>; - }; -+ -+ i2s_pins: i2s { -+ brcm,pins = <28 29 30 31>; -+ brcm,function = <4>; /* alt0 */ -+ }; - }; - - &spi0 { -@@ -62,3 +73,9 @@ - pinctrl-0 = <&i2c1_pins>; - clock-frequency = <100000>; - }; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 2ca6d63..6b36128 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -7,11 +7,8 @@ - interrupt-parent = <&intc>; - - chosen { -- /* -- bootargs must be 1024 characters long because the -- VC bootloader can't expand it -- */ -- bootargs = "console=ttyAMA0 "; -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; - }; - - soc { -@@ -39,6 +36,17 @@ - #interrupt-cells = <2>; - }; - -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2708-i2s"; -+ reg = <0x7e203000 0x20>, -+ <0x7e101098 0x02>; -+ -+ //dmas = <&dma 2>, -+ // <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ - spi0: spi@7e204000 { - compatible = "brcm,bcm2708-spi"; - reg = <0x7e204000 0x1000>; diff --git a/arch/arm/boot/dts/hifiberry-dac-overlay.dts b/arch/arm/boot/dts/hifiberry-dac-overlay.dts new file mode 100644 index 0000000..5e7633a @@ -120251,7 +118576,7 @@ index c63387b..11e4f39 100644 .probe = snd_rpi_hifiberry_dacplus_probe, .remove = snd_rpi_hifiberry_dacplus_remove, diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index e4f769d..76af8a6 100644 +index 446ecdd..74a8c73 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -125,6 +125,21 @@ static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) @@ -120353,17 +118678,17 @@ index 126f1e9..7c6598e 100644 }; -From 50224e08199906d8bad2b40db5a2445f18f1851b Mon Sep 17 00:00:00 2001 +From 069d2e987c6261c6d95b0212d23d214c7bada60d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 5 Dec 2014 17:26:26 +0000 -Subject: [PATCH 066/172] fdt: Add support for the CONFIG_CMDLINE_EXTEND option +Subject: [PATCH 054/104] fdt: Add support for the CONFIG_CMDLINE_EXTEND option --- drivers/of/fdt.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c -index 5100742..d6d944c 100644 +index 3a896c9..0320a6f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -915,19 +915,38 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, @@ -120411,32 +118736,10 @@ index 5100742..d6d944c 100644 pr_debug("Command line is: %s\n", (char*)data); -From e321ee5ea13db9279cc73e4ed85feb572738f520 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 25 Nov 2014 13:39:03 +0000 -Subject: [PATCH 067/172] config: Enable device tree - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 8381041..c1621fa 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -39,6 +39,7 @@ CONFIG_PARTITION_ADVANCED=y - CONFIG_MAC_PARTITION=y - CONFIG_CFQ_GROUP_IOSCHED=y - CONFIG_ARCH_BCM2708=y -+CONFIG_BCM2708_DT=y - CONFIG_PREEMPT=y - CONFIG_AEABI=y - CONFIG_CLEANCACHE=y - -From 30d815a127ab3fc90b634fc9e78b0a54e8cecd13 Mon Sep 17 00:00:00 2001 +From 0e1b5b9dbf5fcd0f1b3ec8ffd272aa52b19e1c6a Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 16 Dec 2014 10:23:48 +0000 -Subject: [PATCH 068/172] DT: Add overrides to enable i2c0, i2c1, spi and i2s +Subject: [PATCH 055/104] DT: Add overrides to enable i2c0, i2c1, spi and i2s --- arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 10 ++++++++++ @@ -120480,10 +118783,10 @@ index d8c6d15..167b22b 100644 + }; +}; -From 4221b401eb7ca80823af64dfabf78e5663b96077 Mon Sep 17 00:00:00 2001 +From e97c92c33e54e047c1adc0e0db6e8b17ce9576c7 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 18 Dec 2014 16:48:32 +0000 -Subject: [PATCH 069/172] lirc-rpi: Add device tree support, and a suitable +Subject: [PATCH 056/104] lirc-rpi: Add device tree support, and a suitable overlay The overlay supports DT parameters that match the old module @@ -120561,10 +118864,10 @@ index 0000000..7d5d82b + }; +}; diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -index c688364..cd66ca2 100644 +index 8aa452d..24563ec 100644 --- a/drivers/staging/media/lirc/lirc_rpi.c +++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -40,6 +40,7 @@ +@@ -41,6 +41,7 @@ #include #include #include @@ -120572,7 +118875,7 @@ index c688364..cd66ca2 100644 #include -@@ -295,32 +296,117 @@ static int is_right_chip(struct gpio_chip *chip, void *data) +@@ -303,32 +304,117 @@ static int is_right_chip(struct gpio_chip *chip, void *data) return 0; } @@ -120704,7 +119007,7 @@ index c688364..cd66ca2 100644 gpiochip->set(gpiochip, gpio_out_pin, invert); irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); -@@ -514,15 +600,23 @@ static struct lirc_driver driver = { +@@ -522,15 +608,23 @@ static struct lirc_driver driver = { .owner = THIS_MODULE, }; @@ -120728,7 +119031,7 @@ index c688364..cd66ca2 100644 int result; /* Init read buffer. */ -@@ -537,15 +631,26 @@ static int __init lirc_rpi_init(void) +@@ -545,15 +639,26 @@ static int __init lirc_rpi_init(void) goto exit_buffer_free; } @@ -120762,7 +119065,7 @@ index c688364..cd66ca2 100644 return 0; -@@ -577,13 +682,6 @@ static int __init lirc_rpi_init_module(void) +@@ -585,13 +690,6 @@ static int __init lirc_rpi_init_module(void) if (result) return result; @@ -120777,10 +119080,10 @@ index c688364..cd66ca2 100644 if (result < 0) goto exit_rpi; -From 5dc9913bd2a47c049baf736b93efc014725c0527 Mon Sep 17 00:00:00 2001 +From 9d3dac432c1080306d150765f4860f4cad48887f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 6 Jan 2015 12:06:55 +0000 -Subject: [PATCH 070/172] Fix the activity LED in DT mode +Subject: [PATCH 057/104] Fix the activity LED in DT mode Add a "leds" node to the base DTBs, and a subnode for the activity LED. You can change the LED function like this: @@ -120883,7 +119186,7 @@ index 167b22b..0631f45 100644 }; }; diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 6b36128..1b56cb5 100644 +index 0a916a4..d879316 100644 --- a/arch/arm/boot/dts/bcm2708.dtsi +++ b/arch/arm/boot/dts/bcm2708.dtsi @@ -11,7 +11,7 @@ @@ -120912,10 +119215,10 @@ index 6b36128..1b56cb5 100644 clocks { -From 86473a27065173f994c13a3431b1ebf3310e52e9 Mon Sep 17 00:00:00 2001 +From f3f8cd9a4c0cc9eef090b7fff1fc10f4982f9d5d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 15 Jan 2015 10:39:06 +0000 -Subject: [PATCH 071/172] Adding w1-gpio device tree overlays +Subject: [PATCH 058/104] Adding w1-gpio device tree overlays N.B. Requires firmware supporting multi-target overrides @@ -121025,10 +119328,10 @@ index 0000000..b3e97c2 + }; +}; -From 323b2e846c0d31be3fabcbed62734c0bb5023f80 Mon Sep 17 00:00:00 2001 +From 93f38c0e716cfab415fe3ea215b15fafaeabb80b Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Fri, 23 Jan 2015 16:41:17 +0100 -Subject: [PATCH 072/172] TAS5713: return error if initialisation fails +Subject: [PATCH 059/104] TAS5713: return error if initialisation fails Existing TAS5713 driver logs errors during initialisation, but does not return an error code. Therefore even if initialisation fails, the driver will still be @@ -121095,10 +119398,10 @@ index a24c1da..9b27138 100644 return 0; } -From 62301cba70c8943a53af4664c805573bdd86face Mon Sep 17 00:00:00 2001 +From 59b36868e0cc474049be8e8f687fcaee2d08f972 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 23 Jan 2015 14:48:55 +0000 -Subject: [PATCH 073/172] scripts/dtc: Update to upstream version with overlay +Subject: [PATCH 060/104] scripts/dtc: Update to upstream version with overlay patches --- @@ -126013,7 +124316,7 @@ index b61465f..e229b84 100644 else return get_node_by_label(tree, ref); diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c -index c20bc53..f534c22 100644 +index c20bc531..f534c22 100644 --- a/scripts/dtc/srcpos.c +++ b/scripts/dtc/srcpos.c @@ -34,7 +34,7 @@ struct search_path { @@ -126311,48 +124614,40 @@ index 54d4e904..d644002 100644 -#define DTC_VERSION "DTC 1.4.0-dirty" +#define DTC_VERSION "DTC 1.4.1-g36c70742" -From 919255c79510712b052391025207715033647954 Mon Sep 17 00:00:00 2001 +From 3b2dc82b29b1675dc8d64971fa13fd2d58b8e1b1 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 23 Jan 2015 15:18:03 +0000 -Subject: [PATCH 074/172] BCM2708_DT: Build the overlays as well +Subject: [PATCH 061/104] BCM2708_DT: Build the overlays as well --- - arch/arm/boot/dts/Makefile | 13 +++++++++++++ - 1 file changed, 13 insertions(+) + arch/arm/boot/dts/Makefile | 9 +++++++++ + 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 76e5f96..54ee8c5 100644 +index c86833d..0784306 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -55,6 +55,14 @@ dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -@@ -565,3 +573,8 @@ endif +@@ -12,6 +12,15 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif - always := $(dtb-y) - clean-files := *.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb + -+# Enable fixups to support overlays on BCM2708 platforms -+ifeq ($(CONFIG_BCM2708_DT),y) -+ DTC_FLAGS ?= -@ -+endif + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb + # Keep at91 dtb files sorted alphabetically for each SoC -From 404893c9ba110f8ea40335f8a62fb49465cf85df Mon Sep 17 00:00:00 2001 +From 6fc5c1aed7033ff0d900ea7a44ef051d9ce1ce9a Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Sun, 25 Jan 2015 19:41:06 +0100 -Subject: [PATCH 075/172] Add device tree overlay for HiFiBerry Amp/Amp+ +Subject: [PATCH 062/104] Add device tree overlay for HiFiBerry Amp/Amp+ This patch add the missing device tree file for the HiFiBerry Amp and Amp+ boards. --- @@ -126362,17 +124657,17 @@ This patch add the missing device tree file for the HiFiBerry Amp and Amp+ board create mode 100644 arch/arm/boot/dts/hifiberry-amp-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 54ee8c5..08a89bf 100644 +index 0784306..8e48eb3 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -58,6 +58,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb +@@ -15,6 +15,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb diff --git a/arch/arm/boot/dts/hifiberry-amp-overlay.dts b/arch/arm/boot/dts/hifiberry-amp-overlay.dts new file mode 100644 index 0000000..2c81448 @@ -126419,10 +124714,10 @@ index 0000000..2c81448 + }; +}; -From c38d9b463556778c95bdaafbae9d2a0617850797 Mon Sep 17 00:00:00 2001 +From 3159d1fd9e49d48303421d65570e3affcbc2268a Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 26 Jan 2015 09:18:24 +0000 -Subject: [PATCH 076/172] Add pps-gpio DT overlay +Subject: [PATCH 063/104] Add pps-gpio DT overlay Parameters: gpiopin= // Default 18 @@ -126433,17 +124728,17 @@ Parameters: create mode 100644 arch/arm/boot/dts/pps-gpio-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 08a89bf..3ae8fe1 100644 +index 8e48eb3..f542d0b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -62,6 +62,7 @@ dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb + diff --git a/arch/arm/boot/dts/pps-gpio-overlay.dts b/arch/arm/boot/dts/pps-gpio-overlay.dts new file mode 100644 index 0000000..40bf0e1 @@ -126485,66 +124780,20 @@ index 0000000..40bf0e1 + }; +}; -From a272e3850258f898431b680c23c771d6cd8a99a5 Mon Sep 17 00:00:00 2001 -From: Serge Schneider -Date: Wed, 3 Sep 2014 14:44:22 +0100 -Subject: [PATCH 077/172] I2C: Only register the I2C device for the current - board revision - ---- - arch/arm/mach-bcm2708/bcm2708.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 2c6a29d..b36037b 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -93,6 +93,7 @@ static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; -+static bool vc_i2c_override = false; - - static unsigned use_dt = 0; - -@@ -862,8 +863,15 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2835_thermal_device); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device_dt(&bcm2708_bsc0_device); -- bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+ if (vc_i2c_override) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ } else { -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } - - #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - bcm_register_device_dt(&bcm2708_i2s_device); -@@ -1093,3 +1101,5 @@ module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); - module_param(w1_gpio_pin, uint, 0644); - module_param(w1_gpio_pullup, uint, 0644); -+module_param(vc_i2c_override, bool, 0644); -+MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); - -From be479e15759f47ee002fa335e7ad5120fb493a54 Mon Sep 17 00:00:00 2001 +From a7eea64b0d9b471933bc65cf42d476456fa3af48 Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Wed, 29 Oct 2014 23:30:30 -0700 -Subject: [PATCH 078/172] Added support to reserve/enable a GPIO pin to be used +Subject: [PATCH 064/104] Added support to reserve/enable a GPIO pin to be used from pps-gpio module (LinuxPPS). Enable PPS modules in default config for RPi. --- arch/arm/mach-bcm2708/bcm2708.c | 27 +++++++++++++++++++++++++++ - 1 file changed, 27 insertions(+) + arch/arm/mach-bcm2709/bcm2709.c | 27 +++++++++++++++++++++++++++ + 2 files changed, 54 insertions(+) diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index b36037b..762e17c 100644 +index 66bc839..cc3e56c 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c @@ -37,6 +37,7 @@ @@ -126560,10 +124809,10 @@ index b36037b..762e17c 100644 static unsigned w1_gpio_pin = W1_GPIO; static unsigned w1_gpio_pullup = W1_PULLUP; +static int pps_gpio_pin = -1; - static bool vc_i2c_override = false; static unsigned use_dt = 0; -@@ -274,6 +276,19 @@ static struct platform_device w1_device = { + +@@ -278,6 +280,19 @@ static struct platform_device w1_device = { }; #endif @@ -126583,7 +124832,7 @@ index b36037b..762e17c 100644 static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); static struct platform_device bcm2708_fb_device = { -@@ -841,6 +856,16 @@ void __init bcm2708_init(void) +@@ -846,6 +861,16 @@ void __init bcm2708_init(void) #ifdef CONFIG_BCM2708_GPIO bcm_register_device_dt(&bcm2708_gpio_device); #endif @@ -126600,42 +124849,164 @@ index b36037b..762e17c 100644 #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) w1_gpio_pdata.pin = w1_gpio_pin; w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -@@ -1101,5 +1126,7 @@ module_param(disk_led_active_low, uint, 0644); +@@ -1099,3 +1124,5 @@ module_param(disk_led_active_low, uint, 0644); + module_param(reboot_part, uint, 0644); + module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); ++module_param(pps_gpio_pin, int, 0644); ++MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index 9edb06f..010cb76 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -95,6 +96,7 @@ static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; ++static int pps_gpio_pin = -1; + + static unsigned use_dt = 0; + +@@ -288,6 +290,19 @@ static struct platform_device w1_device = { + }; + #endif + ++static struct pps_gpio_platform_data pps_gpio_info = { ++ .assert_falling_edge = false, ++ .capture_clear = false, ++ .gpio_pin = -1, ++ .gpio_label = "PPS", ++}; ++ ++static struct platform_device pps_gpio_device = { ++ .name = "pps-gpio", ++ .id = PLATFORM_DEVID_NONE, ++ .dev.platform_data = &pps_gpio_info, ++}; ++ + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -868,6 +883,16 @@ void __init bcm2709_init(void) + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); + #endif ++ ++#if defined(CONFIG_PPS_CLIENT_GPIO) || defined(CONFIG_PPS_CLIENT_GPIO_MODULE) ++ if (!use_dt && (pps_gpio_pin >= 0)) { ++ pr_info("bcm2709: GPIO %d setup as pps-gpio device\n", pps_gpio_pin); ++ pps_gpio_info.gpio_pin = pps_gpio_pin; ++ pps_gpio_device.id = pps_gpio_pin; ++ bcm_register_device(&pps_gpio_device); ++ } ++#endif ++ + #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) + w1_gpio_pdata.pin = w1_gpio_pin; + w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; +@@ -1261,3 +1286,5 @@ module_param(disk_led_active_low, uint, 0644); module_param(reboot_part, uint, 0644); module_param(w1_gpio_pin, uint, 0644); module_param(w1_gpio_pullup, uint, 0644); +module_param(pps_gpio_pin, int, 0644); +MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); - module_param(vc_i2c_override, bool, 0644); - MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); -From f12f6d91c196ce12836bafcab75b0031903dc74c Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 26 Jan 2015 15:26:10 +0000 -Subject: [PATCH 079/172] BCM2708_DT: Correct length of the peripheral space +From 1585897a85631276569eb5f70f5c3957a1ca8123 Mon Sep 17 00:00:00 2001 +From: Serge Schneider +Date: Wed, 3 Sep 2014 14:44:22 +0100 +Subject: [PATCH 065/104] I2C: Only register the I2C device for the current + board revision --- - arch/arm/boot/dts/bcm2708.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) + arch/arm/mach-bcm2708/bcm2708.c | 14 ++++++++++++-- + arch/arm/mach-bcm2709/bcm2709.c | 14 ++++++++++++-- + 2 files changed, 24 insertions(+), 4 deletions(-) -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 1b56cb5..d879316 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -15,7 +15,7 @@ - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; -- ranges = <0x7e000000 0x20000000 0x02000000>; -+ ranges = <0x7e000000 0x20000000 0x01000000>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index cc3e56c..c8aea5b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -95,6 +95,7 @@ static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; + static int pps_gpio_pin = -1; ++static bool vc_i2c_override = false; - intc: interrupt-controller { - compatible = "brcm,bcm2708-armctrl-ic"; + static unsigned use_dt = 0; + +@@ -890,8 +891,15 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_alsa_devices[i]); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device_dt(&bcm2708_bsc0_device); +- bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++ if (vc_i2c_override) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ } else { ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } + + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); +@@ -1126,3 +1134,5 @@ module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); + module_param(pps_gpio_pin, int, 0644); + MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); ++module_param(vc_i2c_override, bool, 0644); ++MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index 010cb76..e3882f3 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -97,6 +97,7 @@ static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; + static int pps_gpio_pin = -1; ++static bool vc_i2c_override = false; + + static unsigned use_dt = 0; + +@@ -917,8 +918,15 @@ void __init bcm2709_init(void) + bcm_register_device(&bcm2835_thermal_device); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device_dt(&bcm2708_bsc0_device); +- bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++ if (vc_i2c_override) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ } else { ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } + + #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) + bcm_register_device_dt(&bcm2708_i2s_device); +@@ -1288,3 +1296,5 @@ module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); + module_param(pps_gpio_pin, int, 0644); + MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); ++module_param(vc_i2c_override, bool, 0644); ++MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); -From c9ee485e8bd038ba21ad37da134fe4170d32ab5c Mon Sep 17 00:00:00 2001 +From b93158e13710b07b0cb18b22ab9b0ad98871927f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 28 Jan 2015 16:22:04 +0000 -Subject: [PATCH 080/172] BCM2708_DT: Add pcf8523-rtc overlay +Subject: [PATCH 066/104] BCM2708_DT: Add pcf8523-rtc overlay --- arch/arm/boot/dts/Makefile | 1 + @@ -126644,17 +125015,17 @@ Subject: [PATCH 080/172] BCM2708_DT: Add pcf8523-rtc overlay create mode 100644 arch/arm/boot/dts/pcf8523-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 3ae8fe1..5ecd1005 100644 +index f542d0b..efd1c04 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -62,6 +62,7 @@ dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb diff --git a/arch/arm/boot/dts/pcf8523-rtc-overlay.dts b/arch/arm/boot/dts/pcf8523-rtc-overlay.dts new file mode 100644 index 0000000..0071f62 @@ -126684,10 +125055,10 @@ index 0000000..0071f62 + }; +}; -From 10beb512f97f3d5c1bf4279ee2890316081e9edb Mon Sep 17 00:00:00 2001 +From 039a864d9cb29552c4a61eb8b017d35afad635b3 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Sat, 31 Jan 2015 16:07:56 +0100 -Subject: [PATCH 081/172] Add a parameter to turn off SPDIF output if no audio +Subject: [PATCH 067/104] Add a parameter to turn off SPDIF output if no audio is playing This patch adds the paramater auto_shutdown_output to the kernel module. @@ -126698,7 +125069,7 @@ is set to 1, the SPDIF oputput will shutdown if no stream is playing. 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index 76af8a6..b0e3d28 100644 +index 74a8c73..a245995 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -26,6 +26,11 @@ @@ -126742,7 +125113,7 @@ index 76af8a6..b0e3d28 100644 @@ -70,7 +94,8 @@ static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, break; default: - dev_err(substream->pcm->dev, + dev_err(codec->dev, - "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); + "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", + samplerate); @@ -126759,17 +125130,17 @@ index 76af8a6..b0e3d28 100644 static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { -From 1993de13349743b8edfa8cfc3be4dc4219bd1a33 Mon Sep 17 00:00:00 2001 +From 4f9c0d63e5041763cdce6705208808bee1486382 Mon Sep 17 00:00:00 2001 From: Joerg Hohensohn Date: Sun, 1 Feb 2015 22:08:03 +0100 -Subject: [PATCH 082/172] bugfix for 32kHz sample rate, was missing +Subject: [PATCH 068/104] bugfix for 32kHz sample rate, was missing --- sound/soc/bcm/hifiberry_digi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index b0e3d28..133d51b 100644 +index a245995..a294a1b 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -80,6 +80,7 @@ static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, @@ -126781,10 +125152,10 @@ index b0e3d28..133d51b 100644 case 48000: case 88200: -From 6085a9df53b0d0cc9898f09717f334a7c3eca4bc Mon Sep 17 00:00:00 2001 +From 4e37e8e0a34280ce4c0ebbf263a768277ab68685 Mon Sep 17 00:00:00 2001 From: Ryan Coe Date: Sat, 31 Jan 2015 18:25:49 -0700 -Subject: [PATCH 083/172] Update ds1307 driver for device-tree support +Subject: [PATCH 069/104] Update ds1307 driver for device-tree support Signed-off-by: Ryan Coe --- @@ -126811,10 +125182,10 @@ index 4ffabb3..c6789a7 100644 .driver = { .name = "rtc-ds1307", -From a4d37a3da2f277ca9e0dc3ad5b1e1aebe16f34b5 Mon Sep 17 00:00:00 2001 +From be4638cf2bd268dd40e6f6f9b9fca1c0f95eff82 Mon Sep 17 00:00:00 2001 From: Ryan Coe Date: Sat, 31 Jan 2015 18:26:03 -0700 -Subject: [PATCH 084/172] Add device-tree overlay for ds1307 +Subject: [PATCH 070/104] Add device-tree overlay for ds1307 Signed-off-by: Ryan Coe --- @@ -126824,17 +125195,17 @@ Signed-off-by: Ryan Coe create mode 100644 arch/arm/boot/dts/ds1307-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 5ecd1005..2685794 100644 +index efd1c04..d45424c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -64,6 +64,7 @@ dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += ds1307-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +@@ -12,6 +12,7 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif + ++dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb diff --git a/arch/arm/boot/dts/ds1307-rtc-overlay.dts b/arch/arm/boot/dts/ds1307-rtc-overlay.dts new file mode 100644 index 0000000..7d27044 @@ -126864,224 +125235,108 @@ index 0000000..7d27044 + }; +}; -From 2a055c5bad25645d230161b596df0a9702bbcc2b Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 7 May 2013 14:32:27 +0100 -Subject: [PATCH 085/172] Add 2709 platform for Raspberry Pi 2 +From a2a317e2349fba2c9b3b66ea0794836d57a178a9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 6 Feb 2015 13:50:57 +0000 +Subject: [PATCH 071/104] BCM270x_DT: Add pwr_led, and the required "input" + trigger +The "input" trigger makes the associated GPIO an input. This is to support +the Raspberry Pi PWR LED, which is driven by external hardware in normal use. + +N.B. pwr_led is not available on Model A or B boards. --- - arch/arm/Kconfig | 21 + - arch/arm/Makefile | 1 + - arch/arm/boot/dts/Makefile | 10 +- - arch/arm/boot/dts/bcm2708.dtsi | 2 +- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 101 ++ - arch/arm/boot/dts/bcm2709.dtsi | 160 +++ - arch/arm/configs/bcm2709_defconfig | 1156 ++++++++++++++++++++ - arch/arm/configs/bcm2709_sdcard_defconfig | 129 +++ - arch/arm/configs/bcm2709_small_defconfig | 61 ++ - arch/arm/configs/bcm2835_sdcard_defconfig | 176 +++ - arch/arm/configs/bcmrpi_sdcard_defconfig | 176 +++ - arch/arm/configs/bcmrpi_small_defconfig | 103 ++ - arch/arm/kernel/head.S | 8 + - arch/arm/mach-bcm2709/Kconfig | 49 + - arch/arm/mach-bcm2709/Makefile | 7 + - arch/arm/mach-bcm2709/Makefile.boot | 3 + - arch/arm/mach-bcm2709/armctrl.c | 357 +++++++ - arch/arm/mach-bcm2709/armctrl.h | 27 + - arch/arm/mach-bcm2709/bcm2708_gpio.c | 426 ++++++++ - arch/arm/mach-bcm2709/bcm2709.c | 1234 ++++++++++++++++++++++ - arch/arm/mach-bcm2709/bcm2709.h | 49 + - arch/arm/mach-bcm2709/clock.c | 61 ++ - arch/arm/mach-bcm2709/clock.h | 24 + - arch/arm/mach-bcm2709/delay.S | 21 + - arch/arm/mach-bcm2709/dma.c | 409 +++++++ - arch/arm/mach-bcm2709/dmaer.c | 886 ++++++++++++++++ - arch/arm/mach-bcm2709/include/mach/arm_control.h | 493 +++++++++ - arch/arm/mach-bcm2709/include/mach/arm_power.h | 62 ++ - arch/arm/mach-bcm2709/include/mach/barriers.h | 3 + - arch/arm/mach-bcm2709/include/mach/clkdev.h | 7 + - arch/arm/mach-bcm2709/include/mach/debug-macro.S | 22 + - arch/arm/mach-bcm2709/include/mach/dma.h | 94 ++ - arch/arm/mach-bcm2709/include/mach/entry-macro.S | 123 +++ - arch/arm/mach-bcm2709/include/mach/frc.h | 38 + - arch/arm/mach-bcm2709/include/mach/gpio.h | 17 + - arch/arm/mach-bcm2709/include/mach/hardware.h | 28 + - arch/arm/mach-bcm2709/include/mach/io.h | 27 + - arch/arm/mach-bcm2709/include/mach/irqs.h | 225 ++++ - arch/arm/mach-bcm2709/include/mach/memory.h | 57 + - arch/arm/mach-bcm2709/include/mach/platform.h | 225 ++++ - arch/arm/mach-bcm2709/include/mach/power.h | 26 + - arch/arm/mach-bcm2709/include/mach/system.h | 38 + - arch/arm/mach-bcm2709/include/mach/timex.h | 23 + - arch/arm/mach-bcm2709/include/mach/uncompress.h | 84 ++ - arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 + - arch/arm/mach-bcm2709/include/mach/vc_support.h | 69 ++ - arch/arm/mach-bcm2709/include/mach/vcio.h | 165 +++ - arch/arm/mach-bcm2709/include/mach/vmalloc.h | 20 + - arch/arm/mach-bcm2709/power.c | 195 ++++ - arch/arm/mach-bcm2709/vc_mem.c | 431 ++++++++ - arch/arm/mach-bcm2709/vc_support.c | 318 ++++++ - arch/arm/mach-bcm2709/vcio.c | 474 +++++++++ - arch/arm/mm/proc-v7.S | 1 + - arch/arm/tools/mach-types | 1 + - drivers/char/hw_random/Kconfig | 2 +- - drivers/clocksource/arm_arch_timer.c | 36 + - drivers/dma/Kconfig | 2 +- - drivers/i2c/busses/Kconfig | 4 +- - drivers/media/platform/bcm2835/Kconfig | 2 +- - drivers/misc/vc04_services/Kconfig | 2 +- - drivers/misc/vc04_services/Makefile | 3 - - drivers/mmc/host/Kconfig | 2 +- - drivers/spi/Kconfig | 4 +- - drivers/watchdog/Kconfig | 2 +- - sound/arm/Kconfig | 2 +- - sound/soc/bcm/Kconfig | 2 +- - 66 files changed, 9004 insertions(+), 17 deletions(-) + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 18 ++++- + arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 ++- + arch/arm/boot/dts/bcm2708.dtsi | 7 +- + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 115 +++++++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 5 -- + drivers/leds/trigger/Kconfig | 7 ++ + drivers/leds/trigger/Makefile | 1 + + drivers/leds/trigger/ledtrig-input.c | 65 +++++++++++++++++ + 8 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 arch/arm/boot/dts/bcm2709-rpi-2-b.dts - create mode 100644 arch/arm/boot/dts/bcm2709.dtsi - create mode 100644 arch/arm/configs/bcm2709_defconfig - create mode 100644 arch/arm/configs/bcm2709_sdcard_defconfig - create mode 100644 arch/arm/configs/bcm2709_small_defconfig - create mode 100644 arch/arm/configs/bcm2835_sdcard_defconfig - create mode 100644 arch/arm/configs/bcmrpi_sdcard_defconfig - create mode 100644 arch/arm/configs/bcmrpi_small_defconfig - create mode 100644 arch/arm/mach-bcm2709/Kconfig - create mode 100644 arch/arm/mach-bcm2709/Makefile - create mode 100644 arch/arm/mach-bcm2709/Makefile.boot - create mode 100644 arch/arm/mach-bcm2709/armctrl.c - create mode 100644 arch/arm/mach-bcm2709/armctrl.h - create mode 100644 arch/arm/mach-bcm2709/bcm2708_gpio.c - create mode 100644 arch/arm/mach-bcm2709/bcm2709.c - create mode 100644 arch/arm/mach-bcm2709/bcm2709.h - create mode 100644 arch/arm/mach-bcm2709/clock.c - create mode 100644 arch/arm/mach-bcm2709/clock.h - create mode 100644 arch/arm/mach-bcm2709/delay.S - create mode 100644 arch/arm/mach-bcm2709/dma.c - create mode 100755 arch/arm/mach-bcm2709/dmaer.c - create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_control.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_power.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/barriers.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/clkdev.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/debug-macro.S - create mode 100644 arch/arm/mach-bcm2709/include/mach/dma.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/entry-macro.S - create mode 100644 arch/arm/mach-bcm2709/include/mach/frc.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/gpio.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/hardware.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/io.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/irqs.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/memory.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/platform.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/power.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/system.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/timex.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/uncompress.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h - create mode 100755 arch/arm/mach-bcm2709/include/mach/vc_support.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vcio.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vmalloc.h - create mode 100644 arch/arm/mach-bcm2709/power.c - create mode 100644 arch/arm/mach-bcm2709/vc_mem.c - create mode 100755 arch/arm/mach-bcm2709/vc_support.c - create mode 100644 arch/arm/mach-bcm2709/vcio.c + create mode 100644 drivers/leds/trigger/ledtrig-input.c -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 96f98ce..062ef6a 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -791,6 +791,26 @@ config ARCH_OMAP1 - help - Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +index 95f03ba..b409c2c 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -83,8 +83,18 @@ + pinctrl-0 = <&i2s_pins>; + }; -+config ARCH_BCM2709 -+ bool "Broadcom BCM2709 family" -+ select ARCH_HAS_BARRIERS if SMP -+ select CPU_V7 -+ select HAVE_SMP -+ select ARM_AMBA -+ select MIGHT_HAVE_CACHE_L2X0 -+ select HAVE_SCHED_CLOCK -+ select NEED_MACH_MEMORY_H -+ select NEED_MACH_IO_H -+ select COMMON_CLK -+ select ARCH_HAS_CPUFREQ -+ select GENERIC_CLOCKEVENTS -+ select MACH_BCM2709 -+ select VC4 -+ select FIQ -+# select ZONE_DMA -+ help -+ This enables support for Broadcom BCM2709 boards. +-&act_led { +- gpios = <&gpio 47 0>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; + - endchoice ++ pwr_led: pwr { ++ label = "led1"; ++ linux,default-trigger = "input"; ++ gpios = <&gpio 35 0>; ++ }; + }; - menu "Multiple platform selection" -@@ -980,6 +1000,7 @@ source "arch/arm/mach-vt8500/Kconfig" - - source "arch/arm/mach-w90x900/Kconfig" - source "arch/arm/mach-bcm2708/Kconfig" -+source "arch/arm/mach-bcm2709/Kconfig" - - source "arch/arm/mach-zynq/Kconfig" - -diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index ceb8a4e..2952f09 100644 ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -147,6 +147,7 @@ machine-$(CONFIG_ARCH_AT91) += at91 - machine-$(CONFIG_ARCH_AXXIA) += axxia - machine-$(CONFIG_ARCH_BCM) += bcm - machine-$(CONFIG_ARCH_BCM2708) += bcm2708 -+machine-$(CONFIG_ARCH_BCM2709) += bcm2709 - machine-$(CONFIG_ARCH_BERLIN) += berlin - machine-$(CONFIG_ARCH_CLPS711X) += clps711x - machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 2685794..83dceff 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -53,6 +53,14 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb + / { +@@ -97,5 +107,9 @@ + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; + -+# Raspberry Pi -+ifeq ($(CONFIG_BCM2708_DT),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_BCM2709_DT),y) -+ RPI_DT_OVERLAYS=y -+endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb -@@ -579,6 +587,6 @@ always := $(dtb-y) - clean-files := *.dtb ++ pwr_led_gpio = <&pwr_led>,"gpios:4"; ++ pwr_led_activelow = <&pwr_led>,"gpios:8"; ++ pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; + }; + }; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +index 0631f45..1ecd1a1 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -83,8 +83,12 @@ + pinctrl-0 = <&i2s_pins>; + }; - # Enable fixups to support overlays on BCM2708 platforms --ifeq ($(CONFIG_BCM2708_DT),y) -+ifeq ($(RPI_DT_OVERLAYS),y) - DTC_FLAGS ?= -@ - endif +-&act_led { +- gpios = <&gpio 16 1>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 16 1>; ++ }; + }; + + / { diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index d879316..af6b1d9 100644 +index d879316..0713a1ed 100644 --- a/arch/arm/boot/dts/bcm2708.dtsi +++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -81,7 +81,7 @@ - compatible = "gpio-leds"; +@@ -79,11 +79,10 @@ - act_led: act { + leds: leds { + compatible = "gpio-leds"; ++ }; + +- act_led: act { - label = "ACT"; -+ label = "led0"; - linux,default-trigger = "mmc0"; - }; +- linux,default-trigger = "mmc0"; +- }; ++ arm-pmu { ++ compatible = "arm,arm1176-pmu"; }; + }; + diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts new file mode 100644 -index 0000000..0cdff31 +index 0000000..46f4908 --- /dev/null +++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -0,0 +1,101 @@ +@@ -0,0 +1,115 @@ +/dts-v1/; + +/include/ "bcm2709.dtsi" @@ -127167,8 +125422,18 @@ index 0000000..0cdff31 + pinctrl-0 = <&i2s_pins>; +}; + -+&act_led { -+ gpios = <&gpio 47 0>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; ++ ++ pwr_led: pwr { ++ label = "led1"; ++ linux,default-trigger = "input"; ++ gpios = <&gpio 35 0>; ++ }; +}; + +/ { @@ -127181,9478 +125446,14 @@ index 0000000..0cdff31 + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; -+ }; -+}; -diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -new file mode 100644 -index 0000000..fee2053 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2709.dtsi -@@ -0,0 +1,160 @@ -+/include/ "skeleton.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2709"; -+ model = "BCM2709"; -+ -+ interrupt-parent = <&intc>; -+ -+ chosen { -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; -+ }; -+ -+ soc: soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges = <0x7e000000 0x3f000000 0x01000000>; -+ -+ intc: interrupt-controller { -+ compatible = "brcm,bcm2708-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio: gpio { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ interrupts = <2 17>, <2 18>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2708-i2s"; -+ reg = <0x7e203000 0x20>, -+ <0x7e101098 0x02>; -+ -+ //dmas = <&dma 2>, -+ // <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ -+ spi0: spi@7e204000 { -+ compatible = "brcm,bcm2708-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clk_spi>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ leds: leds { -+ compatible = "gpio-leds"; -+ -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ }; -+ }; -+ }; -+ -+ clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clk_i2c: i2c { -+ compatible = "fixed-clock"; -+ reg = <1>; -+ #clock-cells = <0>; -+ clock-frequency = <250000000>; -+ }; -+ -+ clk_spi: clock@2 { -+ compatible = "fixed-clock"; -+ reg = <2>; -+ #clock-cells = <0>; -+ clock-output-names = "spi"; -+ clock-frequency = <250000000>; -+ }; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ clock-frequency = <19200000>; -+ interrupts = <3 0>, // PHYS_SECURE_PPI -+ <3 1>, // PHYS_NONSECURE_PPI -+ <3 3>, // VIRT_PPI -+ <3 2>; // HYP_PPI -+ always-on; -+ }; -+ -+ cpus: cpus { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ v7_cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf00>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf01>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu2: cpu@2 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf02>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu3: cpu@3 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf03>; -+ clock-frequency = <800000000>; -+ }; -+ }; -+ -+ __overrides__ { -+ arm_freq = <&v7_cpu0>, "clock-frequency:0", -+ <&v7_cpu1>, "clock-frequency:0", -+ <&v7_cpu2>, "clock-frequency:0", -+ <&v7_cpu3>, "clock-frequency:0"; -+ }; -+}; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -new file mode 100644 -index 0000000..62f33d6 ---- /dev/null -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -0,0 +1,1156 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0 -+CONFIG_LOCALVERSION="-v7" -+# CONFIG_LOCALVERSION_AUTO is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_EMBEDDED=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_PROFILING=y -+CONFIG_OPROFILE=m -+CONFIG_KPROBES=y -+CONFIG_JUMP_LABEL=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_PARTITION_ADVANCED=y -+CONFIG_MAC_PARTITION=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2709=y -+CONFIG_BCM2709_DT=y -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_VMSPLIT_2G=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_KEXEC=y -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_STAT=m -+CONFIG_CPU_FREQ_STAT_DETAILS=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_NEON=y -+CONFIG_KERNEL_MODE_NEON=y -+CONFIG_BINFMT_MISC=m -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_XFRM_USER=y -+CONFIG_NET_KEY=m -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_ADVANCED_ROUTER=y -+CONFIG_IP_MULTIPLE_TABLES=y -+CONFIG_IP_ROUTE_MULTIPATH=y -+CONFIG_IP_ROUTE_VERBOSE=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_NET_IPIP=m -+CONFIG_NET_IPGRE_DEMUX=m -+CONFIG_NET_IPGRE=m -+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_INET_AH=m -+CONFIG_INET_ESP=m -+CONFIG_INET_IPCOMP=m -+CONFIG_INET_XFRM_MODE_TRANSPORT=m -+CONFIG_INET_XFRM_MODE_TUNNEL=m -+CONFIG_INET_XFRM_MODE_BEET=m -+CONFIG_INET_LRO=m -+CONFIG_INET_DIAG=m -+CONFIG_INET6_AH=m -+CONFIG_INET6_ESP=m -+CONFIG_INET6_IPCOMP=m -+CONFIG_IPV6_TUNNEL=m -+CONFIG_IPV6_MULTIPLE_TABLES=y -+CONFIG_IPV6_MROUTE=y -+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y -+CONFIG_IPV6_PIMSM_V2=y -+CONFIG_NETFILTER=y -+CONFIG_NF_CONNTRACK=m -+CONFIG_NF_CONNTRACK_ZONES=y -+CONFIG_NF_CONNTRACK_EVENTS=y -+CONFIG_NF_CONNTRACK_TIMESTAMP=y -+CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m -+CONFIG_NETFILTER_XT_TARGET_AUDIT=m -+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m -+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -+CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m -+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -+CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 -+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_ESP=m -+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m -+CONFIG_NETFILTER_XT_MATCH_HELPER=m -+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m -+CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y -+CONFIG_IP_VS_PROTO_UDP=y -+CONFIG_IP_VS_PROTO_ESP=y -+CONFIG_IP_VS_PROTO_AH=y -+CONFIG_IP_VS_PROTO_SCTP=y -+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 -+CONFIG_IP_VS_FTP=m -+CONFIG_IP_VS_PE_SIP=m -+CONFIG_NF_CONNTRACK_IPV4=m -+CONFIG_IP_NF_IPTABLES=m -+CONFIG_IP_NF_MATCH_AH=m -+CONFIG_IP_NF_MATCH_ECN=m -+CONFIG_IP_NF_MATCH_TTL=m -+CONFIG_IP_NF_FILTER=m -+CONFIG_IP_NF_TARGET_REJECT=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 -+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_RT=m -+CONFIG_IP6_NF_TARGET_HL=m -+CONFIG_IP6_NF_FILTER=m -+CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y -+CONFIG_ATM=m -+CONFIG_L2TP=m -+CONFIG_L2TP_V3=y -+CONFIG_L2TP_IP=m -+CONFIG_L2TP_ETH=m -+CONFIG_BRIDGE=m -+CONFIG_VLAN_8021Q=m -+CONFIG_VLAN_8021Q_GVRP=y -+CONFIG_ATALK=m -+CONFIG_NET_SCHED=y -+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 -+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_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_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_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_BATMAN_ADV=m -+CONFIG_OPENVSWITCH=m -+CONFIG_NET_PKTGEN=m -+CONFIG_HAMRADIO=y -+CONFIG_AX25=m -+CONFIG_NETROM=m -+CONFIG_ROSE=m -+CONFIG_MKISS=m -+CONFIG_6PACK=m -+CONFIG_BPQETHER=m -+CONFIG_BAYCOM_SER_FDX=m -+CONFIG_BAYCOM_SER_HDX=m -+CONFIG_YAM=m -+CONFIG_IRDA=m -+CONFIG_IRLAN=m -+CONFIG_IRNET=m -+CONFIG_IRCOMM=m -+CONFIG_IRDA_ULTRA=y -+CONFIG_IRDA_CACHE_LAST_LSAP=y -+CONFIG_IRDA_FAST_RR=y -+CONFIG_IRTTY_SIR=m -+CONFIG_KINGSUN_DONGLE=m -+CONFIG_KSDAZZLE_DONGLE=m -+CONFIG_KS959_DONGLE=m -+CONFIG_USB_IRDA=m -+CONFIG_SIGMATEL_FIR=m -+CONFIG_MCS_FIR=m -+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 -+CONFIG_BT_HCIBTUSB=m -+CONFIG_BT_HCIBCM203X=m -+CONFIG_BT_HCIBPA10X=m -+CONFIG_BT_HCIBFUSB=m -+CONFIG_BT_HCIVHCI=m -+CONFIG_BT_MRVL=m -+CONFIG_BT_MRVL_SDIO=m -+CONFIG_BT_ATH3K=m -+CONFIG_BT_WILINK=m -+CONFIG_CFG80211_WEXT=y -+CONFIG_MAC80211=m -+CONFIG_MAC80211_MESH=y -+CONFIG_WIMAX=m -+CONFIG_RFKILL=m -+CONFIG_RFKILL_INPUT=y -+CONFIG_NET_9P=m -+CONFIG_NFC=m -+CONFIG_NFC_PN533=m -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=5 -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_DRBD=m -+CONFIG_BLK_DEV_NBD=m -+CONFIG_BLK_DEV_RAM=y -+CONFIG_CDROM_PKTCDVD=m -+CONFIG_EEPROM_AT24=m -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+CONFIG_BLK_DEV_SD=y -+CONFIG_CHR_DEV_ST=m -+CONFIG_CHR_DEV_OSST=m -+CONFIG_BLK_DEV_SR=m -+CONFIG_CHR_DEV_SG=m -+CONFIG_SCSI_ISCSI_ATTRS=y -+CONFIG_ISCSI_TCP=m -+CONFIG_ISCSI_BOOT_SYSFS=m -+CONFIG_MD=y -+CONFIG_MD_LINEAR=m -+CONFIG_MD_RAID0=m -+CONFIG_BLK_DEV_DM=m -+CONFIG_DM_CRYPT=m -+CONFIG_DM_SNAPSHOT=m -+CONFIG_DM_MIRROR=m -+CONFIG_DM_LOG_USERSPACE=m -+CONFIG_DM_RAID=m -+CONFIG_DM_ZERO=m -+CONFIG_DM_DELAY=m -+CONFIG_NETDEVICES=y -+CONFIG_BONDING=m -+CONFIG_DUMMY=m -+CONFIG_IFB=m -+CONFIG_MACVLAN=m -+CONFIG_NETCONSOLE=m -+CONFIG_TUN=m -+CONFIG_VETH=m -+CONFIG_ENC28J60=m -+CONFIG_MDIO_BITBANG=m -+CONFIG_PPP=m -+CONFIG_PPP_BSDCOMP=m -+CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_FILTER=y -+CONFIG_PPP_MPPE=m -+CONFIG_PPP_MULTILINK=y -+CONFIG_PPPOATM=m -+CONFIG_PPPOE=m -+CONFIG_PPPOL2TP=m -+CONFIG_PPP_ASYNC=m -+CONFIG_PPP_SYNC_TTY=m -+CONFIG_SLIP=m -+CONFIG_SLIP_COMPRESSED=y -+CONFIG_SLIP_SMART=y -+CONFIG_USB_CATC=m -+CONFIG_USB_KAWETH=m -+CONFIG_USB_PEGASUS=m -+CONFIG_USB_RTL8150=m -+CONFIG_USB_RTL8152=m -+CONFIG_USB_USBNET=y -+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=y -+CONFIG_USB_NET_GL620A=m -+CONFIG_USB_NET_NET1080=m -+CONFIG_USB_NET_PLUSB=m -+CONFIG_USB_NET_MCS7830=m -+CONFIG_USB_NET_CDC_SUBSET=m -+CONFIG_USB_ALI_M5632=y -+CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m -+CONFIG_LIBERTAS_THINFIRM_USB=m -+CONFIG_AT76C50X_USB=m -+CONFIG_USB_ZD1201=m -+CONFIG_USB_NET_RNDIS_WLAN=m -+CONFIG_RTL8187=m -+CONFIG_MAC80211_HWSIM=m -+CONFIG_ATH_CARDS=m -+CONFIG_ATH9K=m -+CONFIG_ATH9K_HTC=m -+CONFIG_CARL9170=m -+CONFIG_ATH6KL=m -+CONFIG_ATH6KL_USB=m -+CONFIG_AR5523=m -+CONFIG_B43=m -+# CONFIG_B43_PHY_N is not set -+CONFIG_B43LEGACY=m -+CONFIG_BRCMFMAC=m -+CONFIG_BRCMFMAC_USB=y -+CONFIG_HOSTAP=m -+CONFIG_LIBERTAS=m -+CONFIG_LIBERTAS_USB=m -+CONFIG_LIBERTAS_SDIO=m -+CONFIG_P54_COMMON=m -+CONFIG_P54_USB=m -+CONFIG_RT2X00=m -+CONFIG_RT2500USB=m -+CONFIG_RT73USB=m -+CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT3573=y -+CONFIG_RT2800USB_RT53XX=y -+CONFIG_RT2800USB_RT55XX=y -+CONFIG_RT2800USB_UNKNOWN=y -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_JOYSTICK=y -+CONFIG_JOYSTICK_IFORCE=m -+CONFIG_JOYSTICK_IFORCE_USB=y -+CONFIG_JOYSTICK_XPAD=m -+CONFIG_JOYSTICK_XPAD_FF=y -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_INPUT_MISC=y -+CONFIG_INPUT_AD714X=m -+CONFIG_INPUT_ATI_REMOTE2=m -+CONFIG_INPUT_KEYSPAN_REMOTE=m -+CONFIG_INPUT_POWERMATE=m -+CONFIG_INPUT_YEALINK=m -+CONFIG_INPUT_CM109=m -+CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m -+CONFIG_INPUT_ADXL34X=m -+CONFIG_INPUT_CMA3000=m -+CONFIG_SERIO=m -+CONFIG_SERIO_RAW=m -+CONFIG_GAMEPORT=m -+CONFIG_GAMEPORT_NS558=m -+CONFIG_GAMEPORT_L4=m -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=m -+CONFIG_RAW_DRIVER=y -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VC_SM=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_MUX=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2708=m -+CONFIG_SPI_SPIDEV=y -+CONFIG_PPS=m -+CONFIG_PPS_CLIENT_LDISC=m -+CONFIG_PPS_CLIENT_GPIO=m -+CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_ARIZONA=m -+CONFIG_W1=m -+CONFIG_W1_MASTER_DS2490=m -+CONFIG_W1_MASTER_DS2482=m -+CONFIG_W1_MASTER_DS1WM=m -+CONFIG_W1_MASTER_GPIO=m -+CONFIG_W1_SLAVE_THERM=m -+CONFIG_W1_SLAVE_SMEM=m -+CONFIG_W1_SLAVE_DS2408=m -+CONFIG_W1_SLAVE_DS2413=m -+CONFIG_W1_SLAVE_DS2406=m -+CONFIG_W1_SLAVE_DS2423=m -+CONFIG_W1_SLAVE_DS2431=m -+CONFIG_W1_SLAVE_DS2433=m -+CONFIG_W1_SLAVE_DS2760=m -+CONFIG_W1_SLAVE_DS2780=m -+CONFIG_W1_SLAVE_DS2781=m -+CONFIG_W1_SLAVE_DS28E04=m -+CONFIG_W1_SLAVE_BQ27000=m -+CONFIG_BATTERY_DS2760=m -+# CONFIG_HWMON is not set -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=m -+CONFIG_UCB1400_CORE=m -+CONFIG_MFD_ARIZONA_I2C=m -+CONFIG_MFD_ARIZONA_SPI=m -+CONFIG_MFD_WM5102=y -+CONFIG_MEDIA_SUPPORT=m -+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_LIRC=m -+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_MEDIA_USB_SUPPORT=y -+CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m -+CONFIG_USB_ZR364XX=m -+CONFIG_USB_STKWEBCAM=m -+CONFIG_USB_S2255=m -+CONFIG_VIDEO_USBTV=m -+CONFIG_VIDEO_PVRUSB2=m -+CONFIG_VIDEO_HDPVR=m -+CONFIG_VIDEO_TLG2300=m -+CONFIG_VIDEO_USBVISION=m -+CONFIG_VIDEO_STK1160_COMMON=m -+CONFIG_VIDEO_STK1160_AC97=y -+CONFIG_VIDEO_GO7007=m -+CONFIG_VIDEO_GO7007_USB=m -+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m -+CONFIG_VIDEO_AU0828=m -+CONFIG_VIDEO_AU0828_RC=y -+CONFIG_VIDEO_CX231XX=m -+CONFIG_VIDEO_CX231XX_ALSA=m -+CONFIG_VIDEO_CX231XX_DVB=m -+CONFIG_VIDEO_TM6000=m -+CONFIG_VIDEO_TM6000_ALSA=m -+CONFIG_VIDEO_TM6000_DVB=m -+CONFIG_DVB_USB=m -+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_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_AS102=m -+CONFIG_VIDEO_EM28XX=m -+CONFIG_VIDEO_EM28XX_V4L2=m -+CONFIG_VIDEO_EM28XX_ALSA=m -+CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_V4L_PLATFORM_DRIVERS=y -+CONFIG_VIDEO_BCM2835=y -+CONFIG_VIDEO_BCM2835_MMAL=m -+CONFIG_RADIO_SI470X=y -+CONFIG_USB_SI470X=m -+CONFIG_I2C_SI470X=m -+CONFIG_RADIO_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_MA901=m -+CONFIG_RADIO_TEA5764=m -+CONFIG_RADIO_SAA7706H=m -+CONFIG_RADIO_TEF6862=m -+CONFIG_RADIO_WL1273=m -+CONFIG_RADIO_WL128X=m -+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -+CONFIG_VIDEO_UDA1342=m -+CONFIG_VIDEO_SONY_BTF_MPX=m -+CONFIG_VIDEO_TVP5150=m -+CONFIG_VIDEO_TW2804=m -+CONFIG_VIDEO_TW9903=m -+CONFIG_VIDEO_TW9906=m -+CONFIG_VIDEO_OV7640=m -+CONFIG_VIDEO_MT9V011=m -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+# CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=m -+CONFIG_SND_SEQUENCER=m -+CONFIG_SND_SEQ_DUMMY=m -+CONFIG_SND_MIXER_OSS=m -+CONFIG_SND_PCM_OSS=m -+CONFIG_SND_SEQUENCER_OSS=y -+CONFIG_SND_HRTIMER=m -+CONFIG_SND_DUMMY=m -+CONFIG_SND_ALOOP=m -+CONFIG_SND_VIRMIDI=m -+CONFIG_SND_MTPAV=m -+CONFIG_SND_SERIAL_U16550=m -+CONFIG_SND_MPU401=m -+CONFIG_SND_BCM2835=m -+CONFIG_SND_USB_AUDIO=m -+CONFIG_SND_USB_UA101=m -+CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_CAIAQ_INPUT=y -+CONFIG_SND_USB_6FIRE=m -+CONFIG_SND_SOC=m -+CONFIG_SND_BCM2708_SOC_I2S=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m -+CONFIG_SND_BCM2708_SOC_RPI_DAC=m -+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m -+CONFIG_SND_SIMPLE_CARD=m -+CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y -+CONFIG_HID_A4TECH=m -+CONFIG_HID_ACRUX=m -+CONFIG_HID_APPLE=m -+CONFIG_HID_BELKIN=m -+CONFIG_HID_CHERRY=m -+CONFIG_HID_CHICONY=m -+CONFIG_HID_CYPRESS=m -+CONFIG_HID_DRAGONRISE=m -+CONFIG_HID_EMS_FF=m -+CONFIG_HID_ELECOM=m -+CONFIG_HID_ELO=m -+CONFIG_HID_EZKEY=m -+CONFIG_HID_HOLTEK=m -+CONFIG_HID_KEYTOUCH=m -+CONFIG_HID_KYE=m -+CONFIG_HID_UCLOGIC=m -+CONFIG_HID_WALTOP=m -+CONFIG_HID_GYRATION=m -+CONFIG_HID_TWINHAN=m -+CONFIG_HID_KENSINGTON=m -+CONFIG_HID_LCPOWER=m -+CONFIG_HID_LOGITECH=m -+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_HID_PETALYNX=m -+CONFIG_HID_PICOLCD=m -+CONFIG_HID_ROCCAT=m -+CONFIG_HID_SAMSUNG=m -+CONFIG_HID_SONY=m -+CONFIG_HID_SPEEDLINK=m -+CONFIG_HID_SUNPLUS=m -+CONFIG_HID_GREENASIA=m -+CONFIG_HID_SMARTJOYPLUS=m -+CONFIG_HID_TOPSEED=m -+CONFIG_HID_THINGM=m -+CONFIG_HID_THRUSTMASTER=m -+CONFIG_HID_WACOM=m -+CONFIG_HID_WIIMOTE=m -+CONFIG_HID_XINMO=m -+CONFIG_HID_ZEROPLUS=m -+CONFIG_HID_ZYDACRON=m -+CONFIG_HID_PID=y -+CONFIG_USB_HIDDEV=y -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_MON=m -+CONFIG_USB_DWCOTG=y -+CONFIG_USB_PRINTER=m -+CONFIG_USB_STORAGE=y -+CONFIG_USB_STORAGE_REALTEK=m -+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 -+CONFIG_USB_UAS=m -+CONFIG_USB_MDC800=m -+CONFIG_USB_MICROTEK=m -+CONFIG_USBIP_CORE=m -+CONFIG_USBIP_VHCI_HCD=m -+CONFIG_USBIP_HOST=m -+CONFIG_USB_SERIAL=m -+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_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_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_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_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_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 -+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=m -+CONFIG_USB_LD=m -+CONFIG_USB_TRANCEVIBRATOR=m -+CONFIG_USB_IOWARRIOR=m -+CONFIG_USB_TEST=m -+CONFIG_USB_ISIGHTFW=m -+CONFIG_USB_YUREX=m -+CONFIG_USB_ATM=m -+CONFIG_USB_SPEEDTOUCH=m -+CONFIG_USB_CXACRU=m -+CONFIG_USB_UEAGLEATM=m -+CONFIG_USB_XUSBATM=m -+CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y -+CONFIG_MMC_SPI=m -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_GPIO=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=y -+CONFIG_LEDS_TRIGGER_GPIO=y -+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y -+CONFIG_LEDS_TRIGGER_TRANSIENT=m -+CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_RTC_CLASS=y -+# CONFIG_RTC_HCTOSYS is not set -+CONFIG_RTC_DRV_DS1307=m -+CONFIG_RTC_DRV_DS1374=m -+CONFIG_RTC_DRV_DS1672=m -+CONFIG_RTC_DRV_DS3232=m -+CONFIG_RTC_DRV_MAX6900=m -+CONFIG_RTC_DRV_RS5C372=m -+CONFIG_RTC_DRV_ISL1208=m -+CONFIG_RTC_DRV_ISL12022=m -+CONFIG_RTC_DRV_ISL12057=m -+CONFIG_RTC_DRV_X1205=m -+CONFIG_RTC_DRV_PCF2127=m -+CONFIG_RTC_DRV_PCF8523=m -+CONFIG_RTC_DRV_PCF8563=m -+CONFIG_RTC_DRV_PCF8583=m -+CONFIG_RTC_DRV_M41T80=m -+CONFIG_RTC_DRV_BQ32K=m -+CONFIG_RTC_DRV_S35390A=m -+CONFIG_RTC_DRV_FM3130=m -+CONFIG_RTC_DRV_RX8581=m -+CONFIG_RTC_DRV_RX8025=m -+CONFIG_RTC_DRV_EM3027=m -+CONFIG_RTC_DRV_RV3029C2=m -+CONFIG_RTC_DRV_M41T93=m -+CONFIG_RTC_DRV_M41T94=m -+CONFIG_RTC_DRV_DS1305=m -+CONFIG_RTC_DRV_DS1390=m -+CONFIG_RTC_DRV_MAX6902=m -+CONFIG_RTC_DRV_R9701=m -+CONFIG_RTC_DRV_RS5C348=m -+CONFIG_RTC_DRV_DS3234=m -+CONFIG_RTC_DRV_PCF2123=m -+CONFIG_RTC_DRV_RX4581=m -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y -+CONFIG_UIO=m -+CONFIG_UIO_PDRV_GENIRQ=m -+CONFIG_STAGING=y -+CONFIG_PRISM2_USB=m -+CONFIG_R8712U=m -+CONFIG_R8188EU=m -+CONFIG_R8723AU=m -+CONFIG_VT6656=m -+CONFIG_SPEAKUP=m -+CONFIG_SPEAKUP_SYNTH_SOFT=m -+CONFIG_STAGING_MEDIA=y -+CONFIG_LIRC_STAGING=y -+CONFIG_LIRC_IGORPLUGUSB=m -+CONFIG_LIRC_IMON=m -+CONFIG_LIRC_RPI=m -+CONFIG_LIRC_SASEM=m -+CONFIG_LIRC_SERIAL=m -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_REISERFS_FS=m -+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_STATISTICS=y -+CONFIG_XFS_FS=m -+CONFIG_XFS_QUOTA=y -+CONFIG_XFS_POSIX_ACL=y -+CONFIG_XFS_RT=y -+CONFIG_GFS2_FS=m -+CONFIG_OCFS2_FS=m -+CONFIG_BTRFS_FS=m -+CONFIG_BTRFS_FS_POSIX_ACL=y -+CONFIG_NILFS2_FS=m -+CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_ISO9660_FS=m -+CONFIG_JOLIET=y -+CONFIG_ZISOFS=y -+CONFIG_UDF_FS=m -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_NTFS_FS=m -+CONFIG_NTFS_RW=y -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+CONFIG_ECRYPT_FS=m -+CONFIG_HFS_FS=m -+CONFIG_HFSPLUS_FS=m -+CONFIG_SQUASHFS=m -+CONFIG_SQUASHFS_XATTR=y -+CONFIG_SQUASHFS_LZO=y -+CONFIG_SQUASHFS_XZ=y -+CONFIG_F2FS_FS=y -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_NFS_SWAP=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NFSD=m -+CONFIG_NFSD_V3_ACL=y -+CONFIG_NFSD_V4=y -+CONFIG_CIFS=m -+CONFIG_CIFS_WEAK_PW_HASH=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_9P_FS=m -+CONFIG_9P_FS_POSIX_ACL=y -+CONFIG_NLS_DEFAULT="utf8" -+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=y -+CONFIG_NLS_ISO8859_1=m -+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_DLM=m -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_MEMORY_INIT=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+# CONFIG_KPROBE_EVENT is not set -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+CONFIG_KDB_KEYBOARD=y -+CONFIG_CRYPTO_USER=m -+CONFIG_CRYPTO_NULL=m -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_CTS=m -+CONFIG_CRYPTO_XTS=m -+CONFIG_CRYPTO_XCBC=m -+CONFIG_CRYPTO_SHA1_ARM_NEON=m -+CONFIG_CRYPTO_SHA512_ARM_NEON=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_AES_ARM_BS=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcm2709_sdcard_defconfig b/arch/arm/configs/bcm2709_sdcard_defconfig -new file mode 100644 -index 0000000..287b15d ---- /dev/null -+++ b/arch/arm/configs/bcm2709_sdcard_defconfig -@@ -0,0 +1,129 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0x0 -+CONFIG_LOCALVERSION="-sdcard" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_RELAY=y -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_EMBEDDED=y -+# CONFIG_PERF_EVENTS is not set -+# CONFIG_COMPAT_BRK is not set -+CONFIG_PROFILING=y -+CONFIG_JUMP_LABEL=y -+CONFIG_BLK_DEV_BSGLIB=y -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2709=y -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_HOTPLUG_CPU=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+# CONFIG_COREDUMP is not set -+# CONFIG_SUSPEND is not set -+CONFIG_NET=y -+CONFIG_UNIX=y -+CONFIG_CGROUP_NET_CLASSID=y -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=8 -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+# CONFIG_HW_RANDOM is not set -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_GPIO_SYSFS=y -+# CONFIG_HWMON is not set -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+# CONFIG_HID is not set -+# CONFIG_USB_SUPPORT is not set -+CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_FANOTIFY=y -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+# CONFIG_NETWORK_FILESYSTEMS is not set -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_ASCII=y -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_FS=y -+CONFIG_MAGIC_SYSRQ=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_KEYS=y -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_AES=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -+CONFIG_AVERAGE=y -diff --git a/arch/arm/configs/bcm2709_small_defconfig b/arch/arm/configs/bcm2709_small_defconfig -new file mode 100644 -index 0000000..9974a9d ---- /dev/null -+++ b/arch/arm/configs/bcm2709_small_defconfig -@@ -0,0 +1,61 @@ -+CONFIG_LOCALVERSION="-small" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_LOG_BUF_SHIFT=16 -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_INITRAMFS_SOURCE="../target_fs" -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+# CONFIG_IOSCHED_CFQ is not set -+CONFIG_ARCH_BCM2709=y -+# CONFIG_BCM2708_GPIO is not set -+# CONFIG_BCM2708_VCMEM is not set -+CONFIG_ARM_THUMBEE=y -+# CONFIG_SWP_EMULATE is not set -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_ARM_ERRATA_720789=y -+CONFIG_SMP=y -+CONFIG_SCHED_MC=y -+CONFIG_SCHED_SMT=y -+CONFIG_HOTPLUG_CPU=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_HIGHMEM=y -+CONFIG_HIGHPTE=y -+# CONFIG_COMPACTION is not set -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="earlyprintk=ttyAMA0,19200 loglevel=9 console=ttyAMA0,19200" -+CONFIG_AUTO_ZRELADDR=y -+CONFIG_BINFMT_MISC=y -+# CONFIG_SUSPEND is not set -+# CONFIG_UEVENT_HELPER is not set -+# CONFIG_FIRMWARE_IN_KERNEL is not set -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+# CONFIG_HW_RANDOM is not set -+# CONFIG_HWMON is not set -+# CONFIG_HID is not set -+# CONFIG_USB_SUPPORT is not set -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_AUTOFS4_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_PRINTK_TIME=y -+CONFIG_FRAME_WARN=4096 -+CONFIG_MAGIC_SYSRQ=y -+CONFIG_DEBUG_KERNEL=y -+CONFIG_RCU_CPU_STALL_TIMEOUT=60 -+# CONFIG_FTRACE is not set -+# CONFIG_ARM_UNWIND is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_CRC16=y -+CONFIG_CRC_ITU_T=y -diff --git a/arch/arm/configs/bcm2835_sdcard_defconfig b/arch/arm/configs/bcm2835_sdcard_defconfig -new file mode 100644 -index 0000000..987471b ---- /dev/null -+++ b/arch/arm/configs/bcm2835_sdcard_defconfig -@@ -0,0 +1,176 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+# CONFIG_BCM2708_GPIO is not set -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+CONFIG_MII=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_PHYLIB=y -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_RAW_DRIVER=y -+CONFIG_WATCHDOG=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_sdcard_defconfig b/arch/arm/configs/bcmrpi_sdcard_defconfig -new file mode 100644 -index 0000000..fef887a ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_sdcard_defconfig -@@ -0,0 +1,176 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+CONFIG_MII=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_PHYLIB=y -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_RAW_DRIVER=y -+CONFIG_WATCHDOG=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+CONFIG_DETECT_HUNG_TASK=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_small_defconfig b/arch/arm/configs/bcmrpi_small_defconfig -new file mode 100644 -index 0000000..3f6e378 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_small_defconfig -@@ -0,0 +1,103 @@ -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2836=y -+# CONFIG_BCM2708_GPIO is not set -+# CONFIG_BCM2708_VCMEM is not set -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_IDLE=y -+# CONFIG_SUSPEND is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+# CONFIG_BCM2708_VCHIQ is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_RAW_DRIVER=y -+# CONFIG_HID is not set -+CONFIG_DMADEVICES=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NLS=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_KERNEL=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC16=y -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S -index 664eee8..e1885fa 100644 ---- a/arch/arm/kernel/head.S -+++ b/arch/arm/kernel/head.S -@@ -673,6 +673,14 @@ ARM_BE8(rev16 ip, ip) - ldrcc r7, [r4], #4 @ use branch for delay slot - bcc 1b - ret lr -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop - #endif - ENDPROC(__fixup_a_pv_table) - -diff --git a/arch/arm/mach-bcm2709/Kconfig b/arch/arm/mach-bcm2709/Kconfig -new file mode 100644 -index 0000000..4fb6e1b ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Kconfig -@@ -0,0 +1,49 @@ -+menu "Broadcom BCM2709 Implementations" -+ depends on ARCH_BCM2709 -+ -+config MACH_BCM2709 -+ bool "Broadcom BCM2709 Development Platform" -+ help -+ Include support for the Broadcom(R) BCM2709 platform. -+ -+config BCM2709_DT -+ bool "BCM2709 Device Tree support" -+ depends on MACH_BCM2709 -+ default n -+ select USE_OF -+ select ARCH_REQUIRE_GPIOLIB -+ select PINCTRL -+ select PINCTRL_BCM2835 -+ help -+ Enable Device Tree support for BCM2709 -+ -+config BCM2708_GPIO -+ bool "BCM2709 gpio support" -+ depends on MACH_BCM2709 -+ select ARCH_REQUIRE_GPIOLIB -+ default y -+ help -+ Include support for the Broadcom(R) BCM2709 gpio. -+ -+config BCM2708_VCMEM -+ bool "Videocore Memory" -+ depends on MACH_BCM2709 -+ default y -+ help -+ Helper for videocore memory access and total size allocation. -+ -+config BCM2708_NOL2CACHE -+ bool "Videocore L2 cache disable" -+ depends on MACH_BCM2709 -+ default y -+ help -+ Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. -+ -+config BCM2708_SPIDEV -+ bool "Bind spidev to SPI0 master" -+ depends on MACH_BCM2709 -+ depends on SPI -+ default y -+ help -+ Binds spidev driver to the SPI0 master -+endmenu -diff --git a/arch/arm/mach-bcm2709/Makefile b/arch/arm/mach-bcm2709/Makefile -new file mode 100644 -index 0000000..2a803bb ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Makefile -@@ -0,0 +1,7 @@ -+# -+# Makefile for the linux kernel. -+# -+ -+obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o dma.o -+obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o -+obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -diff --git a/arch/arm/mach-bcm2709/Makefile.boot b/arch/arm/mach-bcm2709/Makefile.boot -new file mode 100644 -index 0000000..67039c3 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Makefile.boot -@@ -0,0 +1,3 @@ -+ zreladdr-y := 0x00008000 -+params_phys-y := 0x00000100 -+initrd_phys-y := 0x00800000 -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -new file mode 100644 -index 0000000..8e69a813 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -0,0 +1,357 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/armctrl.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include "armctrl.h" -+ -+/* For support of kernels >= 3.0 assume only one VIC for now*/ -+static unsigned int remap_irqs[(INTERRUPT_ARASANSDIO + 1) - INTERRUPT_JPEG] = { -+ INTERRUPT_VC_JPEG, -+ INTERRUPT_VC_USB, -+ INTERRUPT_VC_3D, -+ INTERRUPT_VC_DMA2, -+ INTERRUPT_VC_DMA3, -+ INTERRUPT_VC_I2C, -+ INTERRUPT_VC_SPI, -+ INTERRUPT_VC_I2SPCM, -+ INTERRUPT_VC_SDIO, -+ INTERRUPT_VC_UART, -+ INTERRUPT_VC_ARASANSDIO -+}; -+ -+static void armctrl_mask_irq(struct irq_data *d) -+{ -+ static const unsigned int disables[4] = { -+ ARM_IRQ_DIBL1, -+ ARM_IRQ_DIBL2, -+ ARM_IRQ_DIBL3, -+ 0 -+ }; -+ int i; -+ if (d->irq >= FIQ_START) { -+ writel(0, __io_address(ARM_IRQ_FAST)); -+ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { -+#if 1 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; -+ for (i=0; i<4; i++) // i = raw_smp_processor_id(); // -+ { -+ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ writel(val &~ (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { -+#if 0 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; -+ for (i=0; i<4; i++) { -+ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ writel(val &~ (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } -+} -+ -+static void armctrl_unmask_irq(struct irq_data *d) -+{ -+ static const unsigned int enables[4] = { -+ ARM_IRQ_ENBL1, -+ ARM_IRQ_ENBL2, -+ ARM_IRQ_ENBL3, -+ 0 -+ }; -+ int i; -+ if (d->irq >= FIQ_START) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); -+ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { -+#if 1 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; -+ for (i=0; i<4; i++) // i = raw_smp_processor_id(); -+ { -+ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ writel(val | (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { -+#if 0 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; -+ for (i=0; i<4; i++) { -+ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ writel(val | (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } -+} -+ -+#ifdef CONFIG_OF -+ -+#define NR_IRQS_BANK0 21 -+#define NR_BANKS 4 -+#define IRQS_PER_BANK 32 -+ -+/* from drivers/irqchip/irq-bcm2835.c */ -+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, -+ const u32 *intspec, unsigned int intsize, -+ unsigned long *out_hwirq, unsigned int *out_type) -+{ -+ if (WARN_ON(intsize != 2)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] >= NR_BANKS)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5)) -+ return -EINVAL; -+ -+ if (intspec[0] == 0) -+ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; -+ else if (intspec[0] == 1) -+ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; -+ else if (intspec[0] == 2) -+ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; -+ else -+ *out_hwirq = ARM_IRQ_LOCAL_BASE + intspec[1]; -+ -+ /* reverse remap_irqs[] */ -+ switch (*out_hwirq) { -+ case INTERRUPT_VC_JPEG: -+ *out_hwirq = INTERRUPT_JPEG; -+ break; -+ case INTERRUPT_VC_USB: -+ *out_hwirq = INTERRUPT_USB; -+ break; -+ case INTERRUPT_VC_3D: -+ *out_hwirq = INTERRUPT_3D; -+ break; -+ case INTERRUPT_VC_DMA2: -+ *out_hwirq = INTERRUPT_DMA2; -+ break; -+ case INTERRUPT_VC_DMA3: -+ *out_hwirq = INTERRUPT_DMA3; -+ break; -+ case INTERRUPT_VC_I2C: -+ *out_hwirq = INTERRUPT_I2C; -+ break; -+ case INTERRUPT_VC_SPI: -+ *out_hwirq = INTERRUPT_SPI; -+ break; -+ case INTERRUPT_VC_I2SPCM: -+ *out_hwirq = INTERRUPT_I2SPCM; -+ break; -+ case INTERRUPT_VC_SDIO: -+ *out_hwirq = INTERRUPT_SDIO; -+ break; -+ case INTERRUPT_VC_UART: -+ *out_hwirq = INTERRUPT_UART; -+ break; -+ case INTERRUPT_VC_ARASANSDIO: -+ *out_hwirq = INTERRUPT_ARASANSDIO; -+ break; -+ } -+ -+ *out_type = IRQ_TYPE_NONE; -+ return 0; -+} -+ -+static struct irq_domain_ops armctrl_ops = { -+ .xlate = armctrl_xlate -+}; -+ -+void __init armctrl_dt_init(void) -+{ -+ struct device_node *np; -+ struct irq_domain *domain; -+ -+ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); -+ if (!np) -+ return; -+ -+ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, -+ IRQ_ARMCTRL_START, 0, -+ &armctrl_ops, NULL); -+ WARN_ON(!domain); -+} -+#else -+void __init armctrl_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ -+#if defined(CONFIG_PM) -+ -+/* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ -+ -+/* Static defines -+ * struct armctrl_device - VIC PM device (< 3.xx) -+ * @sysdev: The system device which is registered. (< 3.xx) -+ * @irq: The IRQ number for the base of the VIC. -+ * @base: The register base for the VIC. -+ * @resume_sources: A bitmask of interrupts for resume. -+ * @resume_irqs: The IRQs enabled for resume. -+ * @int_select: Save for VIC_INT_SELECT. -+ * @int_enable: Save for VIC_INT_ENABLE. -+ * @soft_int: Save for VIC_INT_SOFT. -+ * @protect: Save for VIC_PROTECT. -+ */ -+struct armctrl_info { -+ void __iomem *base; -+ int irq; -+ u32 resume_sources; -+ u32 resume_irqs; -+ u32 int_select; -+ u32 int_enable; -+ u32 soft_int; -+ u32 protect; -+} armctrl; -+ -+static int armctrl_suspend(void) -+{ -+ return 0; -+} -+ -+static void armctrl_resume(void) -+{ -+ return; -+} -+ -+/** -+ * armctrl_pm_register - Register a VIC for later power management control -+ * @base: The base address of the VIC. -+ * @irq: The base IRQ for the VIC. -+ * @resume_sources: bitmask of interrupts allowed for resume sources. -+ * -+ * For older kernels (< 3.xx) do - -+ * Register the VIC with the system device tree so that it can be notified -+ * of suspend and resume requests and ensure that the correct actions are -+ * taken to re-instate the settings on resume. -+ */ -+static void __init armctrl_pm_register(void __iomem * base, unsigned int irq, -+ u32 resume_sources) -+{ -+ armctrl.base = base; -+ armctrl.resume_sources = resume_sources; -+ armctrl.irq = irq; -+} -+ -+static int armctrl_set_wake(struct irq_data *d, unsigned int on) -+{ -+ unsigned int off = d->irq & 31; -+ u32 bit = 1 << off; -+ -+ if (!(bit & armctrl.resume_sources)) -+ return -EINVAL; -+ -+ if (on) -+ armctrl.resume_irqs |= bit; -+ else -+ armctrl.resume_irqs &= ~bit; -+ -+ return 0; -+} -+ -+#else -+static inline void armctrl_pm_register(void __iomem * base, unsigned int irq, -+ u32 arg1) -+{ -+} -+ -+#define armctrl_suspend NULL -+#define armctrl_resume NULL -+#define armctrl_set_wake NULL -+#endif /* CONFIG_PM */ -+ -+static struct syscore_ops armctrl_syscore_ops = { -+ .suspend = armctrl_suspend, -+ .resume = armctrl_resume, -+}; -+ -+/** -+ * armctrl_syscore_init - initicall to register VIC pm functions -+ * -+ * This is called via late_initcall() to register -+ * the resources for the VICs due to the early -+ * nature of the VIC's registration. -+*/ -+static int __init armctrl_syscore_init(void) -+{ -+ register_syscore_ops(&armctrl_syscore_ops); -+ return 0; -+} -+ -+late_initcall(armctrl_syscore_init); -+ -+static struct irq_chip armctrl_chip = { -+ .name = "ARMCTRL", -+ .irq_ack = NULL, -+ .irq_mask = armctrl_mask_irq, -+ .irq_unmask = armctrl_unmask_irq, -+ .irq_set_wake = armctrl_set_wake, -+}; -+ -+/** -+ * armctrl_init - initialise a vectored interrupt controller -+ * @base: iomem base address -+ * @irq_start: starting interrupt number, must be muliple of 32 -+ * @armctrl_sources: bitmask of interrupt sources to allow -+ * @resume_sources: bitmask of interrupt sources to allow for resume -+ */ -+int __init armctrl_init(void __iomem * base, unsigned int irq_start, -+ u32 armctrl_sources, u32 resume_sources) -+{ -+ unsigned int irq; -+ -+ for (irq = 0; irq < BCM2708_ALLOC_IRQS; irq++) { -+ unsigned int data = irq; -+ if (irq >= INTERRUPT_JPEG && irq <= INTERRUPT_ARASANSDIO) -+ data = remap_irqs[irq - INTERRUPT_JPEG]; -+ if (irq >= IRQ_ARM_LOCAL_CNTPSIRQ && irq <= IRQ_ARM_LOCAL_TIMER) { -+ irq_set_percpu_devid(irq); -+ irq_set_chip_and_handler(irq, &armctrl_chip, handle_percpu_devid_irq); -+ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); -+ } else { -+ irq_set_chip_and_handler(irq, &armctrl_chip, handle_level_irq); -+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE | IRQF_DISABLED); -+ } -+ irq_set_chip_data(irq, (void *)data); -+ } -+ -+ armctrl_pm_register(base, irq_start, resume_sources); -+ init_FIQ(FIQ_START); -+ armctrl_dt_init(); -+ return 0; -+} -diff --git a/arch/arm/mach-bcm2709/armctrl.h b/arch/arm/mach-bcm2709/armctrl.h -new file mode 100644 -index 0000000..0aa916e ---- /dev/null -+++ b/arch/arm/mach-bcm2709/armctrl.h -@@ -0,0 +1,27 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/armctrl.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_ARMCTRL_H -+#define __BCM2708_ARMCTRL_H -+ -+extern int __init armctrl_init(void __iomem * base, unsigned int irq_start, -+ u32 armctrl_sources, u32 resume_sources); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/bcm2708_gpio.c b/arch/arm/mach-bcm2709/bcm2708_gpio.c -new file mode 100644 -index 0000000..c1e9254 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2708_gpio.c -@@ -0,0 +1,426 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" -+#define DRIVER_NAME BCM_GPIO_DRIVER_NAME -+#define BCM_GPIO_USE_IRQ 1 -+ -+#define GPIOFSEL(x) (0x00+(x)*4) -+#define GPIOSET(x) (0x1c+(x)*4) -+#define GPIOCLR(x) (0x28+(x)*4) -+#define GPIOLEV(x) (0x34+(x)*4) -+#define GPIOEDS(x) (0x40+(x)*4) -+#define GPIOREN(x) (0x4c+(x)*4) -+#define GPIOFEN(x) (0x58+(x)*4) -+#define GPIOHEN(x) (0x64+(x)*4) -+#define GPIOLEN(x) (0x70+(x)*4) -+#define GPIOAREN(x) (0x7c+(x)*4) -+#define GPIOAFEN(x) (0x88+(x)*4) -+#define GPIOUD(x) (0x94+(x)*4) -+#define GPIOUDCLK(x) (0x98+(x)*4) -+ -+#define GPIO_BANKS 2 -+ -+enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, -+ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, -+ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, -+ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, -+}; -+ -+ /* Each of the two spinlocks protects a different set of hardware -+ * regiters and data structurs. This decouples the code of the IRQ from -+ * the GPIO code. This also makes the case of a GPIO routine call from -+ * the IRQ code simpler. -+ */ -+static DEFINE_SPINLOCK(lock); /* GPIO registers */ -+ -+struct bcm2708_gpio { -+ struct list_head list; -+ void __iomem *base; -+ struct gpio_chip gc; -+ unsigned long rising[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long falling[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long high[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long low[(BCM2708_NR_GPIOS + 31) / 32]; -+}; -+ -+static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, -+ int function) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned long flags; -+ unsigned gpiodir; -+ unsigned gpio_bank = offset / 10; -+ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; -+ -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&lock, flags); -+ -+ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); -+ gpiodir &= ~(7 << gpio_field_offset); -+ gpiodir |= function << gpio_field_offset; -+ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank)); -+ spin_unlock_irqrestore(&lock, flags); -+ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); -+ -+ return 0; -+} -+ -+static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset) -+{ -+ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT); -+} -+ -+static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value); -+static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset, -+ int value) -+{ -+ int ret; -+ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT); -+ if (ret >= 0) -+ bcm2708_gpio_set(gc, offset, value); -+ return ret; -+} -+ -+static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+ unsigned lev; -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return 0; -+ lev = readl(gpio->base + GPIOLEV(gpio_bank)); -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); -+ return 0x1 & (lev >> gpio_field_offset); -+} -+ -+static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); -+ if (offset >= BCM2708_NR_GPIOS) -+ return; -+ if (value) -+ writel(1 << gpio_field_offset, gpio->base + GPIOSET(gpio_bank)); -+ else -+ writel(1 << gpio_field_offset, gpio->base + GPIOCLR(gpio_bank)); -+} -+ -+/********************** -+ * extension to configure pullups -+ */ -+int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, -+ bcm2708_gpio_pull_t value) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ switch (value) { -+ case BCM2708_PULL_UP: -+ writel(2, gpio->base + GPIOUD(0)); -+ break; -+ case BCM2708_PULL_DOWN: -+ writel(1, gpio->base + GPIOUD(0)); -+ break; -+ case BCM2708_PULL_OFF: -+ writel(0, gpio->base + GPIOUD(0)); -+ break; -+ } -+ -+ udelay(5); -+ writel(1 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); -+ udelay(5); -+ writel(0, gpio->base + GPIOUD(0)); -+ writel(0 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); -+ -+ return 0; -+} -+EXPORT_SYMBOL(bcm2708_gpio_setpull); -+ -+/************************************************************************************************************************* -+ * bcm2708 GPIO IRQ -+ */ -+ -+#if BCM_GPIO_USE_IRQ -+ -+static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) -+{ -+ return gpio_to_irq(gpio); -+} -+ -+static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned go = gn % 32; -+ -+ gpio->rising[gb] &= ~(1 << go); -+ gpio->falling[gb] &= ~(1 << go); -+ gpio->high[gb] &= ~(1 << go); -+ gpio->low[gb] &= ~(1 << go); -+ -+ if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) -+ return -EINVAL; -+ -+ if (type & IRQ_TYPE_EDGE_RISING) -+ gpio->rising[gb] |= (1 << go); -+ if (type & IRQ_TYPE_EDGE_FALLING) -+ gpio->falling[gb] |= (1 << go); -+ if (type & IRQ_TYPE_LEVEL_HIGH) -+ gpio->high[gb] |= (1 << go); -+ if (type & IRQ_TYPE_LEVEL_LOW) -+ gpio->low[gb] |= (1 << go); -+ return 0; -+} -+ -+static void bcm2708_gpio_irq_mask(struct irq_data *d) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned long rising = readl(gpio->base + GPIOREN(gb)); -+ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); -+ unsigned long high = readl(gpio->base + GPIOHEN(gb)); -+ unsigned long low = readl(gpio->base + GPIOLEN(gb)); -+ -+ gn = gn % 32; -+ -+ writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); -+ writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); -+ writel(high & ~(1 << gn), gpio->base + GPIOHEN(gb)); -+ writel(low & ~(1 << gn), gpio->base + GPIOLEN(gb)); -+} -+ -+static void bcm2708_gpio_irq_unmask(struct irq_data *d) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned go = gn % 32; -+ unsigned long rising = readl(gpio->base + GPIOREN(gb)); -+ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); -+ unsigned long high = readl(gpio->base + GPIOHEN(gb)); -+ unsigned long low = readl(gpio->base + GPIOLEN(gb)); -+ -+ if (gpio->rising[gb] & (1 << go)) { -+ writel(rising | (1 << go), gpio->base + GPIOREN(gb)); -+ } else { -+ writel(rising & ~(1 << go), gpio->base + GPIOREN(gb)); -+ } -+ -+ if (gpio->falling[gb] & (1 << go)) { -+ writel(falling | (1 << go), gpio->base + GPIOFEN(gb)); -+ } else { -+ writel(falling & ~(1 << go), gpio->base + GPIOFEN(gb)); -+ } -+ -+ if (gpio->high[gb] & (1 << go)) { -+ writel(high | (1 << go), gpio->base + GPIOHEN(gb)); -+ } else { -+ writel(high & ~(1 << go), gpio->base + GPIOHEN(gb)); -+ } -+ -+ if (gpio->low[gb] & (1 << go)) { -+ writel(low | (1 << go), gpio->base + GPIOLEN(gb)); -+ } else { -+ writel(low & ~(1 << go), gpio->base + GPIOLEN(gb)); -+ } -+} -+ -+static struct irq_chip bcm2708_irqchip = { -+ .name = "GPIO", -+ .irq_enable = bcm2708_gpio_irq_unmask, -+ .irq_disable = bcm2708_gpio_irq_mask, -+ .irq_unmask = bcm2708_gpio_irq_unmask, -+ .irq_mask = bcm2708_gpio_irq_mask, -+ .irq_set_type = bcm2708_gpio_irq_set_type, -+}; -+ -+static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id) -+{ -+ unsigned long edsr; -+ unsigned bank; -+ int i; -+ unsigned gpio; -+ unsigned level_bits; -+ struct bcm2708_gpio *gpio_data = dev_id; -+ -+ for (bank = 0; bank < GPIO_BANKS; bank++) { -+ edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank)); -+ level_bits = gpio_data->high[bank] | gpio_data->low[bank]; -+ -+ for_each_set_bit(i, &edsr, 32) { -+ gpio = i + bank * 32; -+ /* ack edge triggered IRQs immediately */ -+ if (!(level_bits & (1<gc.to_irq = bcm2708_gpio_to_irq; -+ -+ for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) { -+ irq_set_chip_data(irq, ucb); -+ irq_set_chip_and_handler(irq, &bcm2708_irqchip, -+ handle_simple_irq); -+ set_irq_flags(irq, IRQF_VALID); -+ } -+ -+ bcm2708_gpio_irq.dev_id = ucb; -+ setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq); -+} -+ -+#else -+ -+static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) -+{ -+} -+ -+#endif /* #if BCM_GPIO_USE_IRQ ***************************************************************************************************************** */ -+ -+static int bcm2708_gpio_probe(struct platform_device *dev) -+{ -+ struct bcm2708_gpio *ucb; -+ struct resource *res; -+ int bank; -+ int err = 0; -+ -+ printk(KERN_INFO DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev); -+ -+ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL); -+ if (NULL == ucb) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "mailbox memory\n"); -+ err = -ENOMEM; -+ goto err; -+ } -+ -+ res = platform_get_resource(dev, IORESOURCE_MEM, 0); -+ -+ platform_set_drvdata(dev, ucb); -+ ucb->base = __io_address(GPIO_BASE); -+ -+ ucb->gc.label = "bcm2708_gpio"; -+ ucb->gc.base = 0; -+ ucb->gc.ngpio = BCM2708_NR_GPIOS; -+ ucb->gc.owner = THIS_MODULE; -+ -+ ucb->gc.direction_input = bcm2708_gpio_dir_in; -+ ucb->gc.direction_output = bcm2708_gpio_dir_out; -+ ucb->gc.get = bcm2708_gpio_get; -+ ucb->gc.set = bcm2708_gpio_set; -+ ucb->gc.can_sleep = 0; -+ -+ for (bank = 0; bank < GPIO_BANKS; bank++) { -+ writel(0, ucb->base + GPIOREN(bank)); -+ writel(0, ucb->base + GPIOFEN(bank)); -+ writel(0, ucb->base + GPIOHEN(bank)); -+ writel(0, ucb->base + GPIOLEN(bank)); -+ writel(0, ucb->base + GPIOAREN(bank)); -+ writel(0, ucb->base + GPIOAFEN(bank)); -+ writel(~0, ucb->base + GPIOEDS(bank)); -+ } -+ -+ bcm2708_gpio_irq_init(ucb); -+ -+ err = gpiochip_add(&ucb->gc); -+ -+err: -+ return err; -+ -+} -+ -+static int bcm2708_gpio_remove(struct platform_device *dev) -+{ -+ int err = 0; -+ struct bcm2708_gpio *ucb = platform_get_drvdata(dev); -+ -+ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev); -+ -+ gpiochip_remove(&ucb->gc); -+ -+ platform_set_drvdata(dev, NULL); -+ kfree(ucb); -+ -+ return err; -+} -+ -+static struct platform_driver bcm2708_gpio_driver = { -+ .probe = bcm2708_gpio_probe, -+ .remove = bcm2708_gpio_remove, -+ .driver = { -+ .name = "bcm2708_gpio"}, -+}; -+ -+static int __init bcm2708_gpio_init(void) -+{ -+ return platform_driver_register(&bcm2708_gpio_driver); -+} -+ -+static void __exit bcm2708_gpio_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_gpio_driver); -+} -+ -+module_init(bcm2708_gpio_init); -+module_exit(bcm2708_gpio_exit); -+ -+MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver"); -+MODULE_LICENSE("GPL"); -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -new file mode 100644 -index 0000000..3055e72d ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -0,0 +1,1234 @@ -+/* -+ * linux/arch/arm/mach-bcm2709/bcm2709.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "bcm2709.h" -+#include "armctrl.h" -+ -+#ifdef CONFIG_BCM_VC_CMA -+#include -+#endif -+ -+//#define SYSTEM_TIMER -+ -+/* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to -+ * give us IO access only to 64Mbytes of physical memory (26 bits). We could -+ * represent this window by setting our dmamasks to 26 bits but, in fact -+ * we're not going to use addresses outside this range (they're not in real -+ * memory) so we don't bother. -+ * -+ * In the future we might include code to use this IOMMU to remap other -+ * physical addresses onto VideoCore memory then the use of 32-bits would be -+ * more legitimate. -+ */ -+#define DMA_MASK_BITS_COMMON 32 -+ -+// use GPIO 4 for the one-wire GPIO pin, if enabled -+#define W1_GPIO 4 -+// ensure one-wire GPIO pullup is disabled by default -+#define W1_PULLUP -1 -+ -+/* command line parameters */ -+static unsigned boardrev, serial; -+static unsigned uart_clock = UART0_CLOCK; -+static unsigned disk_led_gpio = 16; -+static unsigned disk_led_active_low = 1; -+static unsigned reboot_part = 0; -+static unsigned w1_gpio_pin = W1_GPIO; -+static unsigned w1_gpio_pullup = W1_PULLUP; -+ -+static unsigned use_dt = 0; -+ -+static void __init bcm2709_init_led(void); -+ -+void __init bcm2709_init_irq(void) -+{ -+ armctrl_init(__io_address(ARMCTRL_IC_BASE), 0, 0, 0); -+} -+ -+static struct map_desc bcm2709_io_desc[] __initdata = { -+ { -+ .virtual = IO_ADDRESS(ARMCTRL_BASE), -+ .pfn = __phys_to_pfn(ARMCTRL_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(UART0_BASE), -+ .pfn = __phys_to_pfn(UART0_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(UART1_BASE), -+ .pfn = __phys_to_pfn(UART1_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(DMA_BASE), -+ .pfn = __phys_to_pfn(DMA_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(MCORE_BASE), -+ .pfn = __phys_to_pfn(MCORE_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(ST_BASE), -+ .pfn = __phys_to_pfn(ST_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(USB_BASE), -+ .pfn = __phys_to_pfn(USB_BASE), -+ .length = SZ_128K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(PM_BASE), -+ .pfn = __phys_to_pfn(PM_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(GPIO_BASE), -+ .pfn = __phys_to_pfn(GPIO_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(ARM_LOCAL_BASE), -+ .pfn = __phys_to_pfn(ARM_LOCAL_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+}; -+ -+void __init bcm2709_map_io(void) -+{ -+ iotable_init(bcm2709_io_desc, ARRAY_SIZE(bcm2709_io_desc)); -+} -+ -+#ifdef SYSTEM_TIMER -+ -+/* The STC is a free running counter that increments at the rate of 1MHz */ -+#define STC_FREQ_HZ 1000000 -+ -+static inline uint32_t timer_read(void) -+{ -+ /* STC: a free running counter that increments at the rate of 1MHz */ -+ return readl(__io_address(ST_BASE + 0x04)); -+} -+ -+static unsigned long bcm2709_read_current_timer(void) -+{ -+ return timer_read(); -+} -+ -+static u64 notrace bcm2709_read_sched_clock(void) -+{ -+ return timer_read(); -+} -+ -+static cycle_t clksrc_read(struct clocksource *cs) -+{ -+ return timer_read(); -+} -+ -+static struct clocksource clocksource_stc = { -+ .name = "stc", -+ .rating = 300, -+ .read = clksrc_read, -+ .mask = CLOCKSOURCE_MASK(32), -+ .flags = CLOCK_SOURCE_IS_CONTINUOUS, -+}; -+ -+unsigned long frc_clock_ticks32(void) -+{ -+ return timer_read(); -+} -+ -+static void __init bcm2709_clocksource_init(void) -+{ -+ if (clocksource_register_hz(&clocksource_stc, STC_FREQ_HZ)) { -+ printk(KERN_ERR "timer: failed to initialize clock " -+ "source %s\n", clocksource_stc.name); -+ } -+} -+#endif -+ -+struct clk __init *bcm2709_clk_register(const char *name, unsigned long fixed_rate) -+{ -+ struct clk *clk; -+ -+ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, -+ fixed_rate); -+ if (IS_ERR(clk)) -+ pr_err("%s not registered\n", name); -+ -+ return clk; -+} -+ -+void __init bcm2709_register_clkdev(struct clk *clk, const char *name) -+{ -+ int ret; -+ -+ ret = clk_register_clkdev(clk, NULL, name); -+ if (ret) -+ pr_err("%s alias not registered\n", name); -+} -+ -+void __init bcm2709_init_clocks(void) -+{ -+ struct clk *clk; -+ -+ clk = bcm2709_clk_register("uart0_clk", uart_clock); -+ bcm2709_register_clkdev(clk, "dev:f1"); -+ -+ clk = bcm2709_clk_register("sdhost_clk", 250000000); -+ bcm2709_register_clkdev(clk, "bcm2708_spi.0"); -+ bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); -+ bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); -+} -+ -+#define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } -+#define UART0_DMA { 15, 14 } -+ -+AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); -+ -+static struct amba_device *amba_devs[] __initdata = { -+ &uart0_device, -+}; -+ -+static struct resource bcm2708_dmaman_resources[] = { -+ { -+ .start = DMA_BASE, -+ .end = DMA_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_dmaman_device = { -+ .name = BCM_DMAMAN_DRIVER_NAME, -+ .id = 0, /* first bcm2708_dma */ -+ .resource = bcm2708_dmaman_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), -+}; -+ -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+static struct w1_gpio_platform_data w1_gpio_pdata = { -+ .pin = W1_GPIO, -+ .ext_pullup_enable_pin = W1_PULLUP, -+ .is_open_drain = 0, -+}; -+ -+static struct platform_device w1_device = { -+ .name = "w1-gpio", -+ .id = -1, -+ .dev.platform_data = &w1_gpio_pdata, -+}; -+#endif -+ -+static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_fb_device = { -+ .name = "bcm2708_fb", -+ .id = -1, /* only one bcm2708_fb */ -+ .resource = NULL, -+ .num_resources = 0, -+ .dev = { -+ .dma_mask = &fb_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { -+ { -+ .mapbase = UART1_BASE + 0x40, -+ .irq = IRQ_AUX, -+ .uartclk = 125000000, -+ .regshift = 2, -+ .iotype = UPIO_MEM, -+ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, -+ .type = PORT_8250, -+ }, -+ {}, -+}; -+ -+static struct platform_device bcm2708_uart1_device = { -+ .name = "serial8250", -+ .id = PLAT8250_DEV_PLATFORM, -+ .dev = { -+ .platform_data = bcm2708_uart1_platform_data, -+ }, -+}; -+ -+static struct resource bcm2708_usb_resources[] = { -+ [0] = { -+ .start = USB_BASE, -+ .end = USB_BASE + SZ_128K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = MPHI_BASE, -+ .end = MPHI_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [2] = { -+ .start = IRQ_HOSTPORT, -+ .end = IRQ_HOSTPORT, -+ .flags = IORESOURCE_IRQ, -+ }, -+ [3] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+ -+static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_usb_device = { -+ .name = "bcm2708_usb", -+ .id = -1, /* only one bcm2708_usb */ -+ .resource = bcm2708_usb_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), -+ .dev = { -+ .dma_mask = &usb_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+static struct resource bcm2708_vcio_resources[] = { -+ [0] = { /* mailbox/semaphore/doorbell access */ -+ .start = MCORE_BASE, -+ .end = MCORE_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_vcio_device = { -+ .name = BCM_VCIO_DRIVER_NAME, -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_vcio_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), -+ .dev = { -+ .dma_mask = &vcio_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+#ifdef CONFIG_BCM2708_GPIO -+#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" -+ -+static struct resource bcm2708_gpio_resources[] = { -+ [0] = { /* general purpose I/O */ -+ .start = GPIO_BASE, -+ .end = GPIO_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_gpio_device = { -+ .name = BCM_GPIO_DRIVER_NAME, -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_gpio_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), -+ .dev = { -+ .dma_mask = &gpio_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+#endif -+ -+#ifdef SYSTEM_TIMER -+static struct resource bcm2708_systemtimer_resources[] = { -+ [0] = { /* system timer access */ -+ .start = ST_BASE, -+ .end = ST_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = IRQ_TIMER3, -+ .end = IRQ_TIMER3, -+ .flags = IORESOURCE_IRQ, -+ } -+ -+}; -+ -+static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_systemtimer_device = { -+ .name = "bcm2708_systemtimer", -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_systemtimer_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), -+ .dev = { -+ .dma_mask = &systemtimer_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+#endif -+ -+#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ -+static struct resource bcm2835_emmc_resources[] = { -+ [0] = { -+ .start = EMMC_BASE, -+ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ -+ /* the memory map actually makes SZ_4K available */ -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_ARASANSDIO, -+ .end = IRQ_ARASANSDIO, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+static u64 bcm2835_emmc_dmamask = 0xffffffffUL; -+ -+struct platform_device bcm2835_emmc_device = { -+ .name = "mmc-bcm2835", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), -+ .resource = bcm2835_emmc_resources, -+ .dev = { -+ .dma_mask = &bcm2835_emmc_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+#endif /* CONFIG_MMC_BCM2835 */ -+ -+static struct resource bcm2708_powerman_resources[] = { -+ [0] = { -+ .start = PM_BASE, -+ .end = PM_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+struct platform_device bcm2708_powerman_device = { -+ .name = "bcm2708_powerman", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), -+ .resource = bcm2708_powerman_resources, -+ .dev = { -+ .dma_mask = &powerman_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+ -+ -+static struct platform_device bcm2708_alsa_devices[] = { -+ [0] = { -+ .name = "bcm2835_AUD0", -+ .id = 0, /* first audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [1] = { -+ .name = "bcm2835_AUD1", -+ .id = 1, /* second audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [2] = { -+ .name = "bcm2835_AUD2", -+ .id = 2, /* third audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [3] = { -+ .name = "bcm2835_AUD3", -+ .id = 3, /* forth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [4] = { -+ .name = "bcm2835_AUD4", -+ .id = 4, /* fifth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [5] = { -+ .name = "bcm2835_AUD5", -+ .id = 5, /* sixth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [6] = { -+ .name = "bcm2835_AUD6", -+ .id = 6, /* seventh audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [7] = { -+ .name = "bcm2835_AUD7", -+ .id = 7, /* eighth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+}; -+ -+static struct resource bcm2708_spi_resources[] = { -+ { -+ .start = SPI0_BASE, -+ .end = SPI0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = IRQ_SPI, -+ .end = IRQ_SPI, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+ -+static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+static struct platform_device bcm2708_spi_device = { -+ .name = "bcm2708_spi", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), -+ .resource = bcm2708_spi_resources, -+ .dev = { -+ .dma_mask = &bcm2708_spi_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, -+}; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+static struct spi_board_info bcm2708_spi_devices[] = { -+#ifdef CONFIG_SPI_SPIDEV -+ { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 0, -+ .mode = SPI_MODE_0, -+ }, { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 1, -+ .mode = SPI_MODE_0, -+ } -+#endif -+}; -+#endif -+ -+static struct resource bcm2708_bsc0_resources[] = { -+ { -+ .start = BSC0_BASE, -+ .end = BSC0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc0_device = { -+ .name = "bcm2708_i2c", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), -+ .resource = bcm2708_bsc0_resources, -+}; -+ -+ -+static struct resource bcm2708_bsc1_resources[] = { -+ { -+ .start = BSC1_BASE, -+ .end = BSC1_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc1_device = { -+ .name = "bcm2708_i2c", -+ .id = 1, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), -+ .resource = bcm2708_bsc1_resources, -+}; -+ -+static struct platform_device bcm2835_hwmon_device = { -+ .name = "bcm2835_hwmon", -+}; -+ -+static struct platform_device bcm2835_thermal_device = { -+ .name = "bcm2835_thermal", -+}; -+ -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+static struct resource bcm2708_i2s_resources[] = { -+ { -+ .start = I2S_BASE, -+ .end = I2S_BASE + 0x20, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = PCM_CLOCK_BASE, -+ .end = PCM_CLOCK_BASE + 0x02, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_i2s_device = { -+ .name = "bcm2708-i2s", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), -+ .resource = bcm2708_i2s_resources, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+static struct platform_device snd_hifiberry_dac_device = { -+ .name = "snd-hifiberry-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm5102a_codec_device = { -+ .name = "pcm5102a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+static struct platform_device snd_rpi_hifiberry_dacplus_device = { -+ .name = "snd-rpi-hifiberry-dacplus", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_pcm512x_hbdacplus_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4d) -+ }, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+static struct platform_device snd_hifiberry_digi_device = { -+ .name = "snd-hifiberry-digi", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("wm8804", 0x3b) -+ }, -+}; -+ -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+static struct platform_device snd_hifiberry_amp_device = { -+ .name = "snd-hifiberry-amp", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_tas5713_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("tas5713", 0x1b) -+ }, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+static struct platform_device snd_rpi_dac_device = { -+ .name = "snd-rpi-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm1794a_codec_device = { -+ .name = "pcm1794a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+static struct platform_device snd_rpi_iqaudio_dac_device = { -+ .name = "snd-rpi-iqaudio-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+// Use the actual device name rather than generic driver name -+static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4c) -+ }, -+}; -+#endif -+ -+int __init bcm_register_device(struct platform_device *pdev) -+{ -+ int ret; -+ -+ ret = platform_device_register(pdev); -+ if (ret) -+ pr_debug("Unable to register platform device '%s': %d\n", -+ pdev->name, ret); -+ -+ return ret; -+} -+ -+/* -+ * Use these macros for platform and i2c devices that are present in the -+ * Device Tree. This way the devices are only added on non-DT systems. -+ */ -+#define bcm_register_device_dt(pdev) \ -+ if (!use_dt) bcm_register_device(pdev) -+ -+#define i2c_register_board_info_dt(busnum, info, n) \ -+ if (!use_dt) i2c_register_board_info(busnum, info, n) -+ -+int calc_rsts(int partition) -+{ -+ return PM_PASSWORD | -+ ((partition & (1 << 0)) << 0) | -+ ((partition & (1 << 1)) << 1) | -+ ((partition & (1 << 2)) << 2) | -+ ((partition & (1 << 3)) << 3) | -+ ((partition & (1 << 4)) << 4) | -+ ((partition & (1 << 5)) << 5); -+} -+ -+static void bcm2709_restart(enum reboot_mode mode, const char *cmd) -+{ -+ extern char bcm2708_reboot_mode; -+ uint32_t pm_rstc, pm_wdog; -+ uint32_t timeout = 10; -+ uint32_t pm_rsts = 0; -+ -+ if(bcm2708_reboot_mode == 'q') -+ { -+ // NOOBS < 1.3 booting with reboot=q -+ pm_rsts = readl(__io_address(PM_RSTS)); -+ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRQ_SET; -+ } -+ else if(bcm2708_reboot_mode == 'p') -+ { -+ // NOOBS < 1.3 halting -+ pm_rsts = readl(__io_address(PM_RSTS)); -+ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRH_SET; -+ } -+ else -+ { -+ pm_rsts = calc_rsts(reboot_part); -+ } -+ -+ writel(pm_rsts, __io_address(PM_RSTS)); -+ -+ /* Setup watchdog for reset */ -+ pm_rstc = readl(__io_address(PM_RSTC)); -+ -+ pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0) -+ pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; -+ -+ writel(pm_wdog, __io_address(PM_WDOG)); -+ writel(pm_rstc, __io_address(PM_RSTC)); -+} -+ -+/* We can't really power off, but if we do the normal reset scheme, and indicate to bootcode.bin not to reboot, then most of the chip will be powered off */ -+static void bcm2709_power_off(void) -+{ -+ extern char bcm2708_reboot_mode; -+ if(bcm2708_reboot_mode == 'q') -+ { -+ // NOOBS < v1.3 -+ bcm2709_restart('p', ""); -+ } -+ else -+ { -+ /* partition 63 is special code for HALT the bootloader knows not to boot*/ -+ reboot_part = 63; -+ /* continue with normal reset mechanism */ -+ bcm2709_restart(0, ""); -+ } -+} -+ -+#ifdef CONFIG_OF -+static void __init bcm2709_dt_init(void) -+{ -+ int ret; -+ -+ of_clk_init(NULL); -+ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -+ if (ret) { -+ pr_err("of_platform_populate failed: %d\n", ret); -+ /* Proceed as if CONFIG_OF was not defined */ -+ } else { -+ use_dt = 1; -+ } -+} -+#else -+static void __init bcm2709_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ -+void __init bcm2709_init(void) -+{ -+ int i; -+ -+#if defined(CONFIG_BCM_VC_CMA) -+ vc_cma_early_init(); -+#endif -+ printk("bcm2709.uart_clock = %d\n", uart_clock); -+ pm_power_off = bcm2709_power_off; -+ -+ bcm2709_init_clocks(); -+ bcm2709_dt_init(); -+ -+ bcm_register_device(&bcm2708_dmaman_device); -+ bcm_register_device(&bcm2708_vcio_device); -+#ifdef CONFIG_BCM2708_GPIO -+ bcm_register_device_dt(&bcm2708_gpio_device); -+#endif -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+ w1_gpio_pdata.pin = w1_gpio_pin; -+ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -+ bcm_register_device_dt(&w1_device); -+#endif -+#ifdef SYSTEM_TIMER -+ bcm_register_device(&bcm2708_systemtimer_device); -+#endif -+ bcm_register_device(&bcm2708_fb_device); -+ bcm_register_device(&bcm2708_usb_device); -+ bcm_register_device(&bcm2708_uart1_device); -+ bcm_register_device(&bcm2708_powerman_device); -+ -+#ifdef CONFIG_MMC_BCM2835 -+ bcm_register_device(&bcm2835_emmc_device); -+#endif -+ bcm2709_init_led(); -+ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) -+ bcm_register_device(&bcm2708_alsa_devices[i]); -+ -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ -+ bcm_register_device_dt(&bcm2708_spi_device); -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+ bcm_register_device_dt(&bcm2708_i2s_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_dac_device); -+ bcm_register_device_dt(&snd_pcm5102a_codec_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_digi_device); -+ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_amp_device); -+ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+ bcm_register_device_dt(&snd_rpi_dac_device); -+ bcm_register_device_dt(&snd_pcm1794a_codec_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+#endif -+ -+ -+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { -+ struct amba_device *d = amba_devs[i]; -+ amba_device_register(d, &iomem_resource); -+ } -+ system_rev = boardrev; -+ system_serial_low = serial; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+ if (!use_dt) -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); -+#endif -+} -+ -+#ifdef SYSTEM_TIMER -+static void timer_set_mode(enum clock_event_mode mode, -+ struct clock_event_device *clk) -+{ -+ switch (mode) { -+ case CLOCK_EVT_MODE_ONESHOT: /* Leave the timer disabled, .set_next_event will enable it */ -+ case CLOCK_EVT_MODE_SHUTDOWN: -+ break; -+ case CLOCK_EVT_MODE_PERIODIC: -+ -+ case CLOCK_EVT_MODE_UNUSED: -+ case CLOCK_EVT_MODE_RESUME: -+ -+ default: -+ printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", -+ (int)mode); -+ break; -+ } -+ -+} -+ -+static int timer_set_next_event(unsigned long cycles, -+ struct clock_event_device *unused) -+{ -+ unsigned long stc; -+ do { -+ stc = readl(__io_address(ST_BASE + 0x04)); -+ /* We could take a FIQ here, which may push ST above STC3 */ -+ writel(stc + cycles, __io_address(ST_BASE + 0x18)); -+ } while ((signed long) cycles >= 0 && -+ (signed long) (readl(__io_address(ST_BASE + 0x04)) - stc) -+ >= (signed long) cycles); -+ return 0; -+} -+ -+static struct clock_event_device timer0_clockevent = { -+ .name = "timer0", -+ .shift = 32, -+ .features = CLOCK_EVT_FEAT_ONESHOT, -+ .set_mode = timer_set_mode, -+ .set_next_event = timer_set_next_event, -+}; -+ -+/* -+ * IRQ handler for the timer -+ */ -+static irqreturn_t bcm2709_timer_interrupt(int irq, void *dev_id) -+{ -+ struct clock_event_device *evt = &timer0_clockevent; -+ -+ writel(1 << 3, __io_address(ST_BASE + 0x00)); /* stcs clear timer int */ -+ -+ evt->event_handler(evt); -+ -+ return IRQ_HANDLED; -+} -+ -+static struct irqaction bcm2709_timer_irq = { -+ .name = "BCM2709 Timer Tick", -+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, -+ .handler = bcm2709_timer_interrupt, -+}; -+ -+/* -+ * Set up timer interrupt, and return the current time in seconds. -+ */ -+ -+static struct delay_timer bcm2709_delay_timer = { -+ .read_current_timer = bcm2709_read_current_timer, -+ .freq = STC_FREQ_HZ, -+}; -+ -+static void __init bcm2709_timer_init(void) -+{ -+ /* init high res timer */ -+ bcm2709_clocksource_init(); -+ -+ /* -+ * Make irqs happen for the system timer -+ */ -+ setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); -+ -+ sched_clock_register(bcm2709_read_sched_clock, 32, STC_FREQ_HZ); -+ -+ timer0_clockevent.mult = -+ div_sc(STC_FREQ_HZ, NSEC_PER_SEC, timer0_clockevent.shift); -+ timer0_clockevent.max_delta_ns = -+ clockevent_delta2ns(0xffffffff, &timer0_clockevent); -+ timer0_clockevent.min_delta_ns = -+ clockevent_delta2ns(0xf, &timer0_clockevent); -+ -+ timer0_clockevent.cpumask = cpumask_of(0); -+ clockevents_register_device(&timer0_clockevent); -+ -+ register_current_timer_delay(&bcm2708_delay_timer); -+} -+ -+#else -+ -+static void __init bcm2709_timer_init(void) -+{ -+ extern void dc4_arch_timer_init(void); -+ // timer control -+ writel(0, __io_address(ARM_LOCAL_CONTROL)); -+ // timer pre_scaler -+ writel(0x80000000, __io_address(ARM_LOCAL_PRESCALER)); // 19.2MHz -+ //writel(0x06AAAAAB, __io_address(ARM_LOCAL_PRESCALER)); // 1MHz -+ -+ if (use_dt) -+ { -+ of_clk_init(NULL); -+ clocksource_of_init(); -+ } -+ else -+ dc4_arch_timer_init(); -+} -+ -+#endif -+ -+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) -+#include -+ -+static struct gpio_led bcm2709_leds[] = { -+ [0] = { -+ .gpio = 16, -+ .name = "led0", -+ .default_trigger = "mmc0", -+ .active_low = 1, -+ }, -+}; -+ -+static struct gpio_led_platform_data bcm2709_led_pdata = { -+ .num_leds = ARRAY_SIZE(bcm2709_leds), -+ .leds = bcm2709_leds, -+}; -+ -+static struct platform_device bcm2709_led_device = { -+ .name = "leds-gpio", -+ .id = -1, -+ .dev = { -+ .platform_data = &bcm2709_led_pdata, -+ }, -+}; -+ -+static void __init bcm2709_init_led(void) -+{ -+ bcm2709_leds[0].gpio = disk_led_gpio; -+ bcm2709_leds[0].active_low = disk_led_active_low; -+ bcm_register_device_dt(&bcm2709_led_device); -+} -+#else -+static inline void bcm2709_init_led(void) -+{ -+} -+#endif -+ -+void __init bcm2709_init_early(void) -+{ -+ /* -+ * Some devices allocate their coherent buffers from atomic -+ * context. Increase size of atomic coherent pool to make sure such -+ * the allocations won't fail. -+ */ -+ init_dma_coherent_pool_size(SZ_4M); -+} -+ -+static void __init board_reserve(void) -+{ -+#if defined(CONFIG_BCM_VC_CMA) -+ vc_cma_reserve(); -+#endif -+} -+ -+ -+#include -+ -+#include -+#include -+#include -+int dc4=0; -+//void dc4_log(unsigned x) { if (dc4) writel((x), __io_address(ST_BASE+10 + raw_smp_processor_id()*4)); } -+void dc4_log_dead(unsigned x) { if (dc4) writel((readl(__io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)) & 0xffff) | ((x)<<16), __io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)); } -+ -+static void bcm2835_send_doorbell(const struct cpumask *mask, unsigned int irq) -+{ -+ int cpu; -+ /* -+ * Ensure that stores to Normal memory are visible to the -+ * other CPUs before issuing the IPI. -+ */ -+ dsb(); -+ -+ /* Convert our logical CPU mask into a physical one. */ -+ for_each_cpu(cpu, mask) -+ { -+ /* submit softirq */ -+ writel(1<%x)\n", __FUNCTION__, (unsigned)virt_to_phys((void *)secondary_startup), (unsigned)__io_address(ST_BASE + 0x10)); -+ printk("[%s] ncores=%d\n", __FUNCTION__, ncores); -+ -+ for (i = 0; i < ncores; i++) { -+ set_cpu_possible(i, true); -+ /* enable IRQ (not FIQ) */ -+ writel(0x1, __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i)); -+ //writel(0xf, __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 0x4 * i)); -+ } -+ set_smp_cross_call(bcm2835_send_doorbell); -+} -+ -+/* -+ * for arch/arm/kernel/smp.c:smp_prepare_cpus(unsigned int max_cpus) -+ */ -+void __init bcm2709_smp_prepare_cpus(unsigned int max_cpus) -+{ -+ //void __iomem *scu_base; -+ -+ printk("[%s] enter\n", __FUNCTION__); -+ //scu_base = scu_base_addr(); -+ //scu_enable(scu_base); -+} -+ -+/* -+ * for linux/arch/arm/kernel/smp.c:secondary_start_kernel(void) -+ */ -+void __cpuinit bcm2709_secondary_init(unsigned int cpu) -+{ -+ printk("[%s] enter cpu:%d\n", __FUNCTION__, cpu); -+ //gic_secondary_init(0); -+} -+ -+/* -+ * for linux/arch/arm/kernel/smp.c:__cpu_up(..) -+ */ -+int __cpuinit bcm2709_boot_secondary(unsigned int cpu, struct task_struct *idle) -+{ -+ void secondary_startup(void); -+ void *mbox_set = __io_address(ARM_LOCAL_MAILBOX3_SET0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); -+ void *mbox_clr = __io_address(ARM_LOCAL_MAILBOX3_CLR0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); -+ unsigned secondary_boot = (unsigned)virt_to_phys((void *)secondary_startup); -+ int timeout=20; -+ unsigned t = -1; -+ //printk("[%s] enter cpu:%d (%x->%p) %x\n", __FUNCTION__, cpu, secondary_boot, wake, readl(wake)); -+ -+ dsb(); -+ BUG_ON(readl(mbox_clr) != 0); -+ writel(secondary_boot, mbox_set); -+ -+ while (--timeout > 0) { -+ t = readl(mbox_clr); -+ if (t == 0) break; -+ cpu_relax(); -+ } -+ if (timeout==0) -+ printk("[%s] cpu:%d failed to start (%x)\n", __FUNCTION__, cpu, t); -+ else -+ printk("[%s] cpu:%d started (%x) %d\n", __FUNCTION__, cpu, t, timeout); -+ -+ return 0; -+} -+ -+ -+struct smp_operations bcm2709_smp_ops __initdata = { -+ .smp_init_cpus = bcm2709_smp_init_cpus, -+ .smp_prepare_cpus = bcm2709_smp_prepare_cpus, -+ .smp_secondary_init = bcm2709_secondary_init, -+ .smp_boot_secondary = bcm2709_boot_secondary, -+}; -+ -+static const char * const bcm2709_compat[] = { -+ "brcm,bcm2709", -+ "brcm,bcm2708", /* Could use bcm2708 in a pinch */ -+ NULL -+}; -+ -+MACHINE_START(BCM2709, "BCM2709") -+ /* Maintainer: Broadcom Europe Ltd. */ -+ .smp = smp_ops(bcm2709_smp_ops), -+ .map_io = bcm2709_map_io, -+ .init_irq = bcm2709_init_irq, -+ .init_time = bcm2709_timer_init, -+ .init_machine = bcm2709_init, -+ .init_early = bcm2709_init_early, -+ .reserve = board_reserve, -+ .restart = bcm2709_restart, -+ .dt_compat = bcm2709_compat, -+MACHINE_END -+ -+module_param(boardrev, uint, 0644); -+module_param(serial, uint, 0644); -+module_param(uart_clock, uint, 0644); -+module_param(disk_led_gpio, uint, 0644); -+module_param(disk_led_active_low, uint, 0644); -+module_param(reboot_part, uint, 0644); -+module_param(w1_gpio_pin, uint, 0644); -+module_param(w1_gpio_pullup, uint, 0644); -diff --git a/arch/arm/mach-bcm2709/bcm2709.h b/arch/arm/mach-bcm2709/bcm2709.h -new file mode 100644 -index 0000000..e339a93 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2709.h -@@ -0,0 +1,49 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/bcm2708.h -+ * -+ * BCM2708 machine support header -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_BCM2708_H -+#define __BCM2708_BCM2708_H -+ -+#include -+ -+extern void __init bcm2708_init(void); -+extern void __init bcm2708_init_irq(void); -+extern void __init bcm2708_map_io(void); -+extern struct sys_timer bcm2708_timer; -+extern unsigned int mmc_status(struct device *dev); -+ -+#define AMBA_DEVICE(name, busid, base, plat) \ -+static struct amba_device name##_device = { \ -+ .dev = { \ -+ .coherent_dma_mask = ~0, \ -+ .init_name = busid, \ -+ .platform_data = plat, \ -+ }, \ -+ .res = { \ -+ .start = base##_BASE, \ -+ .end = (base##_BASE) + SZ_4K - 1,\ -+ .flags = IORESOURCE_MEM, \ -+ }, \ -+ .irq = base##_IRQ, \ -+} -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/clock.c b/arch/arm/mach-bcm2709/clock.c -new file mode 100644 -index 0000000..4fc556e ---- /dev/null -+++ b/arch/arm/mach-bcm2709/clock.c -@@ -0,0 +1,61 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/clock.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "clock.h" -+ -+int clk_enable(struct clk *clk) -+{ -+ return 0; -+} -+EXPORT_SYMBOL(clk_enable); -+ -+void clk_disable(struct clk *clk) -+{ -+} -+EXPORT_SYMBOL(clk_disable); -+ -+unsigned long clk_get_rate(struct clk *clk) -+{ -+ return clk->rate; -+} -+EXPORT_SYMBOL(clk_get_rate); -+ -+long clk_round_rate(struct clk *clk, unsigned long rate) -+{ -+ return clk->rate; -+} -+EXPORT_SYMBOL(clk_round_rate); -+ -+int clk_set_rate(struct clk *clk, unsigned long rate) -+{ -+ return -EIO; -+} -+EXPORT_SYMBOL(clk_set_rate); -diff --git a/arch/arm/mach-bcm2709/clock.h b/arch/arm/mach-bcm2709/clock.h -new file mode 100644 -index 0000000..5f9d725 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/clock.h -@@ -0,0 +1,24 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/clock.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+struct module; -+ -+struct clk { -+ unsigned long rate; -+}; -diff --git a/arch/arm/mach-bcm2709/delay.S b/arch/arm/mach-bcm2709/delay.S -new file mode 100644 -index 0000000..06f4780 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/delay.S -@@ -0,0 +1,21 @@ -+/* -+ * linux/arch/arm/lib/delay.S -+ * -+ * Copyright (C) 1995, 1996 Russell King -+ * -+ * 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 -+#include -+ -+ .text -+.align 3 @ 8 byte alignment seems to be needed to avoid fetching stalls -+@ Delay routine -+ENTRY(bcm2708_delay) -+ subs r0, r0, #1 -+ bhi bcm2708_delay -+ mov pc, lr -+ENDPROC(bcm2708_delay) -diff --git a/arch/arm/mach-bcm2709/dma.c b/arch/arm/mach-bcm2709/dma.c -new file mode 100644 -index 0000000..a5e58d1 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/dma.c -@@ -0,0 +1,409 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/dma.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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 -+#include -+#include -+#include -+ -+#include -+#include -+ -+/*****************************************************************************\ -+ * * -+ * Configuration * -+ * * -+\*****************************************************************************/ -+ -+#define CACHE_LINE_MASK 31 -+#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME -+#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ -+ -+/* valid only for channels 0 - 14, 15 has its own base address */ -+#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */ -+#define BCM2708_DMA_CHANIO(dma_base, n) \ -+ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n))) -+ -+ -+/*****************************************************************************\ -+ * * -+ * DMA Auxilliary Functions * -+ * * -+\*****************************************************************************/ -+ -+/* A DMA buffer on an arbitrary boundary may separate a cache line into a -+ section inside the DMA buffer and another section outside it. -+ Even if we flush DMA buffers from the cache there is always the chance that -+ during a DMA someone will access the part of a cache line that is outside -+ the DMA buffer - which will then bring in unwelcome data. -+ Without being able to dictate our own buffer pools we must insist that -+ DMA buffers consist of a whole number of cache lines. -+*/ -+ -+extern int -+bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) -+{ -+ int i; -+ -+ for (i = 0; i < sg_len; i++) { -+ if (sg_ptr[i].offset & CACHE_LINE_MASK || -+ sg_ptr[i].length & CACHE_LINE_MASK) -+ return 0; -+ } -+ -+ return 1; -+} -+EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); -+ -+extern void -+bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block) -+{ -+ dsb(); /* ARM data synchronization (push) operation */ -+ -+ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); -+ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); -+} -+ -+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) -+{ -+ dsb(); -+ -+ /* ugly busy wait only option for now */ -+ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE) -+ cpu_relax(); -+} -+ -+EXPORT_SYMBOL_GPL(bcm_dma_start); -+ -+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base) -+{ -+ dsb(); -+ -+ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_is_busy); -+ -+/* Complete an ongoing DMA (assuming its results are to be ignored) -+ Does nothing if there is no DMA in progress. -+ This routine waits for the current AXI transfer to complete before -+ terminating the current DMA. If the current transfer is hung on a DREQ used -+ by an uncooperative peripheral the AXI transfer may never complete. In this -+ case the routine times out and return a non-zero error code. -+ Use of this routine doesn't guarantee that the ongoing or aborted DMA -+ does not produce an interrupt. -+*/ -+extern int -+bcm_dma_abort(void __iomem *dma_chan_base) -+{ -+ unsigned long int cs; -+ int rc = 0; -+ -+ cs = readl(dma_chan_base + BCM2708_DMA_CS); -+ -+ if (BCM2708_DMA_ACTIVE & cs) { -+ long int timeout = 10000; -+ -+ /* write 0 to the active bit - pause the DMA */ -+ writel(0, dma_chan_base + BCM2708_DMA_CS); -+ -+ /* wait for any current AXI transfer to complete */ -+ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) -+ cs = readl(dma_chan_base + BCM2708_DMA_CS); -+ -+ if (0 != (cs & BCM2708_DMA_ISPAUSED)) { -+ /* we'll un-pause when we set of our next DMA */ -+ rc = -ETIMEDOUT; -+ -+ } else if (BCM2708_DMA_ACTIVE & cs) { -+ /* terminate the control block chain */ -+ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); -+ -+ /* abort the whole DMA */ -+ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, -+ dma_chan_base + BCM2708_DMA_CS); -+ } -+ } -+ -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_abort); -+ -+ -+/***************************************************************************** \ -+ * * -+ * DMA Manager Device Methods * -+ * * -+\*****************************************************************************/ -+ -+struct vc_dmaman { -+ void __iomem *dma_base; -+ u32 chan_available; /* bitmap of available channels */ -+ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ -+}; -+ -+static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, -+ u32 chans_available) -+{ -+ dmaman->dma_base = dma_base; -+ dmaman->chan_available = chans_available; -+ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ -+} -+ -+static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, -+ unsigned preferred_feature_set) -+{ -+ u32 chans; -+ int feature; -+ -+ chans = dmaman->chan_available; -+ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) -+ /* select the subset of available channels with the desired -+ feature so long as some of the candidate channels have that -+ feature */ -+ if ((preferred_feature_set & (1 << feature)) && -+ (chans & dmaman->has_feature[feature])) -+ chans &= dmaman->has_feature[feature]; -+ -+ if (chans) { -+ int chan = 0; -+ /* return the ordinal of the first channel in the bitmap */ -+ while (chans != 0 && (chans & 1) == 0) { -+ chans >>= 1; -+ chan++; -+ } -+ /* claim the channel */ -+ dmaman->chan_available &= ~(1 << chan); -+ return chan; -+ } else -+ return -ENOMEM; -+} -+ -+static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) -+{ -+ if (chan < 0) -+ return -EINVAL; -+ else if ((1 << chan) & dmaman->chan_available) -+ return -EIDRM; -+ else { -+ dmaman->chan_available |= (1 << chan); -+ return 0; -+ } -+} -+ -+/*****************************************************************************\ -+ * * -+ * DMA IRQs * -+ * * -+\*****************************************************************************/ -+ -+static unsigned char bcm_dma_irqs[] = { -+ IRQ_DMA0, -+ IRQ_DMA1, -+ IRQ_DMA2, -+ IRQ_DMA3, -+ IRQ_DMA4, -+ IRQ_DMA5, -+ IRQ_DMA6, -+ IRQ_DMA7, -+ IRQ_DMA8, -+ IRQ_DMA9, -+ IRQ_DMA10, -+ IRQ_DMA11, -+ IRQ_DMA12 -+}; -+ -+ -+/***************************************************************************** \ -+ * * -+ * DMA Manager Monitor * -+ * * -+\*****************************************************************************/ -+ -+static struct device *dmaman_dev; /* we assume there's only one! */ -+ -+extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, -+ void __iomem **out_dma_base, int *out_dma_irq) -+{ -+ if (!dmaman_dev) -+ return -ENODEV; -+ else { -+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); -+ int rc; -+ -+ device_lock(dmaman_dev); -+ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); -+ if (rc >= 0) { -+ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, -+ rc); -+ *out_dma_irq = bcm_dma_irqs[rc]; -+ } -+ device_unlock(dmaman_dev); -+ -+ return rc; -+ } -+} -+EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); -+ -+extern int bcm_dma_chan_free(int channel) -+{ -+ if (dmaman_dev) { -+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); -+ int rc; -+ -+ device_lock(dmaman_dev); -+ rc = vc_dmaman_chan_free(dmaman, channel); -+ device_unlock(dmaman_dev); -+ -+ return rc; -+ } else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_chan_free); -+ -+static int dev_dmaman_register(const char *dev_name, struct device *dev) -+{ -+ int rc = dmaman_dev ? -EINVAL : 0; -+ dmaman_dev = dev; -+ return rc; -+} -+ -+static void dev_dmaman_deregister(const char *dev_name, struct device *dev) -+{ -+ dmaman_dev = NULL; -+} -+ -+/*****************************************************************************\ -+ * * -+ * DMA Device * -+ * * -+\*****************************************************************************/ -+ -+static int dmachans = -1; /* module parameter */ -+ -+static int bcm_dmaman_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ struct vc_dmaman *dmaman; -+ struct resource *dma_res = NULL; -+ void __iomem *dma_base = NULL; -+ int have_dma_region = 0; -+ -+ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); -+ if (NULL == dmaman) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "DMA management memory\n"); -+ ret = -ENOMEM; -+ } else { -+ -+ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (dma_res == NULL) { -+ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " -+ "resource\n"); -+ ret = -ENODEV; -+ } else if (!request_mem_region(dma_res->start, -+ resource_size(dma_res), -+ DRIVER_NAME)) { -+ dev_err(&pdev->dev, "cannot obtain DMA region\n"); -+ ret = -EBUSY; -+ } else { -+ have_dma_region = 1; -+ dma_base = ioremap(dma_res->start, -+ resource_size(dma_res)); -+ if (!dma_base) { -+ dev_err(&pdev->dev, "cannot map DMA region\n"); -+ ret = -ENOMEM; -+ } else { -+ /* use module parameter if one was provided */ -+ if (dmachans > 0) -+ vc_dmaman_init(dmaman, dma_base, -+ dmachans); -+ else -+ vc_dmaman_init(dmaman, dma_base, -+ DEFAULT_DMACHAN_BITMAP); -+ -+ platform_set_drvdata(pdev, dmaman); -+ dev_dmaman_register(DRIVER_NAME, &pdev->dev); -+ -+ printk(KERN_INFO DRIVER_NAME ": DMA manager " -+ "at %p\n", dma_base); -+ } -+ } -+ } -+ if (ret != 0) { -+ if (dma_base) -+ iounmap(dma_base); -+ if (dma_res && have_dma_region) -+ release_mem_region(dma_res->start, -+ resource_size(dma_res)); -+ if (dmaman) -+ kfree(dmaman); -+ } -+ return ret; -+} -+ -+static int bcm_dmaman_remove(struct platform_device *pdev) -+{ -+ struct vc_dmaman *dmaman = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); -+ kfree(dmaman); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm_dmaman_driver = { -+ .probe = bcm_dmaman_probe, -+ .remove = bcm_dmaman_remove, -+ -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+/*****************************************************************************\ -+ * * -+ * Driver init/exit * -+ * * -+\*****************************************************************************/ -+ -+static int __init bcm_dmaman_drv_init(void) -+{ -+ int ret; -+ -+ ret = platform_driver_register(&bcm_dmaman_driver); -+ if (ret != 0) { -+ printk(KERN_ERR DRIVER_NAME ": failed to register " -+ "on platform\n"); -+ } -+ -+ return ret; -+} -+ -+static void __exit bcm_dmaman_drv_exit(void) -+{ -+ platform_driver_unregister(&bcm_dmaman_driver); -+} -+ -+module_init(bcm_dmaman_drv_init); -+module_exit(bcm_dmaman_drv_exit); -+ -+module_param(dmachans, int, 0644); -+ -+MODULE_AUTHOR("Gray Girling "); -+MODULE_DESCRIPTION("DMA channel manager driver"); -+MODULE_LICENSE("GPL"); -+ -+MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); -diff --git a/arch/arm/mach-bcm2709/dmaer.c b/arch/arm/mach-bcm2709/dmaer.c -new file mode 100755 -index 0000000..5b0f0ff ---- /dev/null -+++ b/arch/arm/mach-bcm2709/dmaer.c -@@ -0,0 +1,886 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#ifdef ECLIPSE_IGNORE -+ -+#define __user -+#define __init -+#define __exit -+#define __iomem -+#define KERN_DEBUG -+#define KERN_ERR -+#define KERN_WARNING -+#define KERN_INFO -+#define _IOWR(a, b, c) b -+#define _IOW(a, b, c) b -+#define _IO(a, b) b -+ -+#endif -+ -+//#define inline -+ -+#define PRINTK(args...) printk(args) -+//#define PRINTK_VERBOSE(args...) printk(args) -+//#define PRINTK(args...) -+#define PRINTK_VERBOSE(args...) -+ -+/***** TYPES ****/ -+#define PAGES_PER_LIST 500 -+struct PageList -+{ -+ struct page *m_pPages[PAGES_PER_LIST]; -+ unsigned int m_used; -+ struct PageList *m_pNext; -+}; -+ -+struct VmaPageList -+{ -+ //each vma has a linked list of pages associated with it -+ struct PageList *m_pPageHead; -+ struct PageList *m_pPageTail; -+ unsigned int m_refCount; -+}; -+ -+struct DmaControlBlock -+{ -+ unsigned int m_transferInfo; -+ void __user *m_pSourceAddr; -+ void __user *m_pDestAddr; -+ unsigned int m_xferLen; -+ unsigned int m_tdStride; -+ struct DmaControlBlock *m_pNext; -+ unsigned int m_blank1, m_blank2; -+}; -+ -+/***** DEFINES ******/ -+//magic number defining the module -+#define DMA_MAGIC 0xdd -+ -+//do user virtual to physical translation of the CB chain -+#define DMA_PREPARE _IOWR(DMA_MAGIC, 0, struct DmaControlBlock *) -+ -+//kick the pre-prepared CB chain -+#define DMA_KICK _IOW(DMA_MAGIC, 1, struct DmaControlBlock *) -+ -+//prepare it, kick it, wait for it -+#define DMA_PREPARE_KICK_WAIT _IOWR(DMA_MAGIC, 2, struct DmaControlBlock *) -+ -+//prepare it, kick it, don't wait for it -+#define DMA_PREPARE_KICK _IOWR(DMA_MAGIC, 3, struct DmaControlBlock *) -+ -+//not currently implemented -+#define DMA_WAIT_ONE _IO(DMA_MAGIC, 4, struct DmaControlBlock *) -+ -+//wait on all kicked CB chains -+#define DMA_WAIT_ALL _IO(DMA_MAGIC, 5) -+ -+//in order to discover the largest AXI burst that should be programmed into the transfer params -+#define DMA_MAX_BURST _IO(DMA_MAGIC, 6) -+ -+//set the address range through which the user address is assumed to already by a physical address -+#define DMA_SET_MIN_PHYS _IOW(DMA_MAGIC, 7, unsigned long) -+#define DMA_SET_MAX_PHYS _IOW(DMA_MAGIC, 8, unsigned long) -+#define DMA_SET_PHYS_OFFSET _IOW(DMA_MAGIC, 9, unsigned long) -+ -+//used to define the size for the CMA-based allocation *in pages*, can only be done once once the file is opened -+#define DMA_CMA_SET_SIZE _IOW(DMA_MAGIC, 10, unsigned long) -+ -+//used to get the version of the module, to test for a capability -+#define DMA_GET_VERSION _IO(DMA_MAGIC, 99) -+ -+#define VERSION_NUMBER 1 -+ -+#define VIRT_TO_BUS_CACHE_SIZE 8 -+ -+/***** FILE OPS *****/ -+static int Open(struct inode *pInode, struct file *pFile); -+static int Release(struct inode *pInode, struct file *pFile); -+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg); -+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp); -+static int Mmap(struct file *pFile, struct vm_area_struct *pVma); -+ -+/***** VMA OPS ****/ -+static void VmaOpen4k(struct vm_area_struct *pVma); -+static void VmaClose4k(struct vm_area_struct *pVma); -+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf); -+ -+/**** DMA PROTOTYPES */ -+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError); -+static int DmaKick(struct DmaControlBlock __user *pUserCB); -+static void DmaWaitAll(void); -+ -+/**** GENERIC ****/ -+static int __init dmaer_init(void); -+static void __exit dmaer_exit(void); -+ -+/*** OPS ***/ -+static struct vm_operations_struct g_vmOps4k = { -+ .open = VmaOpen4k, -+ .close = VmaClose4k, -+ .fault = VmaFault4k, -+}; -+ -+static struct file_operations g_fOps = { -+ .owner = THIS_MODULE, -+ .llseek = 0, -+ .read = Read, -+ .write = 0, -+ .unlocked_ioctl = Ioctl, -+ .open = Open, -+ .release = Release, -+ .mmap = Mmap, -+}; -+ -+/***** GLOBALS ******/ -+static dev_t g_majorMinor; -+ -+//tracking usage of the two files -+static atomic_t g_oneLock4k = ATOMIC_INIT(1); -+ -+//device operations -+static struct cdev g_cDev; -+static int g_trackedPages = 0; -+ -+//dma control -+static unsigned int *g_pDmaChanBase; -+static int g_dmaIrq; -+static int g_dmaChan; -+ -+//cma allocation -+static int g_cmaHandle; -+ -+//user virtual to bus address translation acceleration -+static unsigned long g_virtAddr[VIRT_TO_BUS_CACHE_SIZE]; -+static unsigned long g_busAddr[VIRT_TO_BUS_CACHE_SIZE]; -+static unsigned long g_cbVirtAddr; -+static unsigned long g_cbBusAddr; -+static int g_cacheInsertAt; -+static int g_cacheHit, g_cacheMiss; -+ -+//off by default -+static void __user *g_pMinPhys; -+static void __user *g_pMaxPhys; -+static unsigned long g_physOffset; -+ -+/****** CACHE OPERATIONS ********/ -+static inline void FlushAddrCache(void) -+{ -+ int count = 0; -+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) -+ g_virtAddr[count] = 0xffffffff; //never going to match as we always chop the bottom bits anyway -+ -+ g_cbVirtAddr = 0xffffffff; -+ -+ g_cacheInsertAt = 0; -+} -+ -+//translate from a user virtual address to a bus address by mapping the page -+//NB this won't lock a page in memory, so to avoid potential paging issues using kernel logical addresses -+static inline void __iomem *UserVirtualToBus(void __user *pUser) -+{ -+ int mapped; -+ struct page *pPage; -+ void *phys; -+ -+ //map it (requiring that the pointer points to something that does not hang off the page boundary) -+ mapped = get_user_pages(current, current->mm, -+ (unsigned long)pUser, 1, -+ 1, 0, -+ &pPage, -+ 0); -+ -+ if (mapped <= 0) //error -+ return 0; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "user virtual %p arm phys %p bus %p\n", -+ pUser, page_address(pPage), (void __iomem *)__virt_to_bus(page_address(pPage))); -+ -+ //get the arm physical address -+ phys = page_address(pPage) + offset_in_page(pUser); -+ page_cache_release(pPage); -+ -+ //and now the bus address -+ return (void __iomem *)__virt_to_bus(phys); -+} -+ -+static inline void __iomem *UserVirtualToBusViaCbCache(void __user *pUser) -+{ -+ unsigned long virtual_page = (unsigned long)pUser & ~4095; -+ unsigned long page_offset = (unsigned long)pUser & 4095; -+ unsigned long bus_addr; -+ -+ if (g_cbVirtAddr == virtual_page) -+ { -+ bus_addr = g_cbBusAddr + page_offset; -+ g_cacheHit++; -+ return (void __iomem *)bus_addr; -+ } -+ else -+ { -+ bus_addr = (unsigned long)UserVirtualToBus(pUser); -+ -+ if (!bus_addr) -+ return 0; -+ -+ g_cbVirtAddr = virtual_page; -+ g_cbBusAddr = bus_addr & ~4095; -+ g_cacheMiss++; -+ -+ return (void __iomem *)bus_addr; -+ } -+} -+ -+//do the same as above, by query our virt->bus cache -+static inline void __iomem *UserVirtualToBusViaCache(void __user *pUser) -+{ -+ int count; -+ //get the page and its offset -+ unsigned long virtual_page = (unsigned long)pUser & ~4095; -+ unsigned long page_offset = (unsigned long)pUser & 4095; -+ unsigned long bus_addr; -+ -+ if (pUser >= g_pMinPhys && pUser < g_pMaxPhys) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "user->phys passthrough on %p\n", pUser); -+ return (void __iomem *)((unsigned long)pUser + g_physOffset); -+ } -+ -+ //check the cache for our entry -+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) -+ if (g_virtAddr[count] == virtual_page) -+ { -+ bus_addr = g_busAddr[count] + page_offset; -+ g_cacheHit++; -+ return (void __iomem *)bus_addr; -+ } -+ -+ //not found, look up manually and then insert its page address -+ bus_addr = (unsigned long)UserVirtualToBus(pUser); -+ -+ if (!bus_addr) -+ return 0; -+ -+ g_virtAddr[g_cacheInsertAt] = virtual_page; -+ g_busAddr[g_cacheInsertAt] = bus_addr & ~4095; -+ -+ //round robin -+ g_cacheInsertAt++; -+ if (g_cacheInsertAt == VIRT_TO_BUS_CACHE_SIZE) -+ g_cacheInsertAt = 0; -+ -+ g_cacheMiss++; -+ -+ return (void __iomem *)bus_addr; -+} -+ -+/***** FILE OPERATIONS ****/ -+static int Open(struct inode *pInode, struct file *pFile) -+{ -+ PRINTK(KERN_DEBUG "file opening: %d/%d\n", imajor(pInode), iminor(pInode)); -+ -+ //check which device we are -+ if (iminor(pInode) == 0) //4k -+ { -+ //only one at a time -+ if (!atomic_dec_and_test(&g_oneLock4k)) -+ { -+ atomic_inc(&g_oneLock4k); -+ return -EBUSY; -+ } -+ } -+ else -+ return -EINVAL; -+ -+ //todo there will be trouble if two different processes open the files -+ -+ //reset after any file is opened -+ g_pMinPhys = (void __user *)-1; -+ g_pMaxPhys = (void __user *)0; -+ g_physOffset = 0; -+ g_cmaHandle = 0; -+ -+ return 0; -+} -+ -+static int Release(struct inode *pInode, struct file *pFile) -+{ -+ PRINTK(KERN_DEBUG "file closing, %d pages tracked\n", g_trackedPages); -+ if (g_trackedPages) -+ PRINTK(KERN_ERR "we\'re leaking memory!\n"); -+ -+ //wait for any dmas to finish -+ DmaWaitAll(); -+ -+ //free this memory on the application closing the file or it crashing (implicitly closing the file) -+ if (g_cmaHandle) -+ { -+ PRINTK(KERN_DEBUG "unlocking vc memory\n"); -+ if (UnlockVcMemory(g_cmaHandle)) -+ PRINTK(KERN_ERR "uh-oh, unable to unlock vc memory!\n"); -+ PRINTK(KERN_DEBUG "releasing vc memory\n"); -+ if (ReleaseVcMemory(g_cmaHandle)) -+ PRINTK(KERN_ERR "uh-oh, unable to release vc memory!\n"); -+ } -+ -+ if (iminor(pInode) == 0) -+ atomic_inc(&g_oneLock4k); -+ else -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError) -+{ -+ struct DmaControlBlock kernCB; -+ struct DmaControlBlock __user *pUNext; -+ void __iomem *pSourceBus, __iomem *pDestBus; -+ -+ //get the control block into kernel memory so we can work on it -+ if (copy_from_user(&kernCB, pUserCB, sizeof(struct DmaControlBlock)) != 0) -+ { -+ PRINTK(KERN_ERR "copy_from_user failed for user cb %p\n", pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ if (kernCB.m_pSourceAddr == 0 || kernCB.m_pDestAddr == 0) -+ { -+ PRINTK(KERN_ERR "faulty source (%p) dest (%p) addresses for user cb %p\n", -+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ pSourceBus = UserVirtualToBusViaCache(kernCB.m_pSourceAddr); -+ pDestBus = UserVirtualToBusViaCache(kernCB.m_pDestAddr); -+ -+ if (!pSourceBus || !pDestBus) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for source/dest %p/%p->%p/%p\n", -+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, -+ pSourceBus, pDestBus); -+ *pError = 1; -+ return 0; -+ } -+ -+ //update the user structure with the new bus addresses -+ kernCB.m_pSourceAddr = pSourceBus; -+ kernCB.m_pDestAddr = pDestBus; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "final source %p dest %p\n", kernCB.m_pSourceAddr, kernCB.m_pDestAddr); -+ -+ //sort out the bus address for the next block -+ pUNext = kernCB.m_pNext; -+ -+ if (kernCB.m_pNext) -+ { -+ void __iomem *pNextBus; -+ pNextBus = UserVirtualToBusViaCbCache(kernCB.m_pNext); -+ -+ if (!pNextBus) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for m_pNext\n"); -+ *pError = 1; -+ return 0; -+ } -+ -+ //update the pointer with the bus address -+ kernCB.m_pNext = pNextBus; -+ } -+ -+ //write it back to user space -+ if (copy_to_user(pUserCB, &kernCB, sizeof(struct DmaControlBlock)) != 0) -+ { -+ PRINTK(KERN_ERR "copy_to_user failed for cb %p\n", pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ __cpuc_flush_dcache_area(pUserCB, 32); -+ -+ *pError = 0; -+ return pUNext; -+} -+ -+static int DmaKick(struct DmaControlBlock __user *pUserCB) -+{ -+ void __iomem *pBusCB; -+ -+ pBusCB = UserVirtualToBusViaCbCache(pUserCB); -+ if (!pBusCB) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for cb\n"); -+ return 1; -+ } -+ -+ //flush_cache_all(); -+ -+ bcm_dma_start(g_pDmaChanBase, (dma_addr_t)pBusCB); -+ -+ return 0; -+} -+ -+static void DmaWaitAll(void) -+{ -+ int counter = 0; -+ volatile int inner_count; -+ volatile unsigned int cs; -+ unsigned long time_before, time_after; -+ -+ time_before = jiffies; -+ //bcm_dma_wait_idle(g_pDmaChanBase); -+ dsb(); -+ -+ cs = readl(g_pDmaChanBase); -+ -+ while ((cs & 1) == 1) -+ { -+ cs = readl(g_pDmaChanBase); -+ counter++; -+ -+ for (inner_count = 0; inner_count < 32; inner_count++); -+ -+ asm volatile ("MCR p15,0,r0,c7,c0,4 \n"); -+ //cpu_do_idle(); -+ if (counter >= 1000000) -+ { -+ PRINTK(KERN_WARNING "DMA failed to finish in a timely fashion\n"); -+ break; -+ } -+ } -+ time_after = jiffies; -+ PRINTK_VERBOSE(KERN_DEBUG "done, counter %d, cs %08x", counter, cs); -+ PRINTK_VERBOSE(KERN_DEBUG "took %ld jiffies, %d HZ\n", time_after - time_before, HZ); -+} -+ -+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg) -+{ -+ int error = 0; -+ PRINTK_VERBOSE(KERN_DEBUG "ioctl cmd %x arg %lx\n", cmd, arg); -+ -+ switch (cmd) -+ { -+ case DMA_PREPARE: -+ case DMA_PREPARE_KICK: -+ case DMA_PREPARE_KICK_WAIT: -+ { -+ struct DmaControlBlock __user *pUCB = (struct DmaControlBlock *)arg; -+ int steps = 0; -+ unsigned long start_time = jiffies; -+ (void)start_time; -+ -+ //flush our address cache -+ FlushAddrCache(); -+ -+ PRINTK_VERBOSE(KERN_DEBUG "dma prepare\n"); -+ -+ //do virtual to bus translation for each entry -+ do -+ { -+ pUCB = DmaPrepare(pUCB, &error); -+ } while (error == 0 && ++steps && pUCB); -+ PRINTK_VERBOSE(KERN_DEBUG "prepare done in %d steps, %ld\n", steps, jiffies - start_time); -+ -+ //carry straight on if we want to kick too -+ if (cmd == DMA_PREPARE || error) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "falling out\n"); -+ return error ? -EINVAL : 0; -+ } -+ } -+ case DMA_KICK: -+ PRINTK_VERBOSE(KERN_DEBUG "dma begin\n"); -+ -+ if (cmd == DMA_KICK) -+ FlushAddrCache(); -+ -+ DmaKick((struct DmaControlBlock __user *)arg); -+ -+ if (cmd != DMA_PREPARE_KICK_WAIT) -+ break; -+/* case DMA_WAIT_ONE: -+ //PRINTK(KERN_DEBUG "dma wait one\n"); -+ break;*/ -+ case DMA_WAIT_ALL: -+ //PRINTK(KERN_DEBUG "dma wait all\n"); -+ DmaWaitAll(); -+ break; -+ case DMA_MAX_BURST: -+ if (g_dmaChan == 0) -+ return 10; -+ else -+ return 5; -+ case DMA_SET_MIN_PHYS: -+ g_pMinPhys = (void __user *)arg; -+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); -+ break; -+ case DMA_SET_MAX_PHYS: -+ g_pMaxPhys = (void __user *)arg; -+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); -+ break; -+ case DMA_SET_PHYS_OFFSET: -+ g_physOffset = arg; -+ PRINTK(KERN_DEBUG "user/phys bypass offset set to %ld\n", g_physOffset); -+ break; -+ case DMA_CMA_SET_SIZE: -+ { -+ unsigned int pBusAddr; -+ -+ if (g_cmaHandle) -+ { -+ PRINTK(KERN_ERR "memory has already been allocated (handle %d)\n", g_cmaHandle); -+ return -EINVAL; -+ } -+ -+ PRINTK(KERN_INFO "allocating %ld bytes of VC memory\n", arg * 4096); -+ -+ //get the memory -+ if (AllocateVcMemory(&g_cmaHandle, arg * 4096, 4096, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_NO_INIT | MEM_FLAG_HINT_PERMALOCK)) -+ { -+ PRINTK(KERN_ERR "failed to allocate %ld bytes of VC memory\n", arg * 4096); -+ g_cmaHandle = 0; -+ return -EINVAL; -+ } -+ -+ //get an address for it -+ PRINTK(KERN_INFO "trying to map VC memory\n"); -+ -+ if (LockVcMemory(&pBusAddr, g_cmaHandle)) -+ { -+ PRINTK(KERN_ERR "failed to map CMA handle %d, releasing memory\n", g_cmaHandle); -+ ReleaseVcMemory(g_cmaHandle); -+ g_cmaHandle = 0; -+ } -+ -+ PRINTK(KERN_INFO "bus address for CMA memory is %x\n", pBusAddr); -+ return pBusAddr; -+ } -+ case DMA_GET_VERSION: -+ PRINTK(KERN_DEBUG "returning version number, %d\n", VERSION_NUMBER); -+ return VERSION_NUMBER; -+ default: -+ PRINTK(KERN_DEBUG "unknown ioctl: %d\n", cmd); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp) -+{ -+ return -EIO; -+} -+ -+static int Mmap(struct file *pFile, struct vm_area_struct *pVma) -+{ -+ struct PageList *pPages; -+ struct VmaPageList *pVmaList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "MMAP vma %p, length %ld (%s %d)\n", -+ pVma, pVma->vm_end - pVma->vm_start, -+ current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_DEBUG "MMAP %p %d (tracked %d)\n", pVma, current->pid, g_trackedPages); -+ -+ //make a new page list -+ pPages = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); -+ if (!pPages) -+ { -+ PRINTK(KERN_ERR "couldn\'t allocate a new page list (%s %d)\n", -+ current->comm, current->pid); -+ return -ENOMEM; -+ } -+ -+ //clear the page list -+ pPages->m_used = 0; -+ pPages->m_pNext = 0; -+ -+ //insert our vma and new page list somewhere -+ if (!pVma->vm_private_data) -+ { -+ struct VmaPageList *pList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "new vma list, making new one (%s %d)\n", -+ current->comm, current->pid); -+ -+ //make a new vma list -+ pList = (struct VmaPageList *)kmalloc(sizeof(struct VmaPageList), GFP_KERNEL); -+ if (!pList) -+ { -+ PRINTK(KERN_ERR "couldn\'t allocate vma page list (%s %d)\n", -+ current->comm, current->pid); -+ kfree(pPages); -+ return -ENOMEM; -+ } -+ -+ //clear this list -+ pVma->vm_private_data = (void *)pList; -+ pList->m_refCount = 0; -+ } -+ -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ //add it to the vma list -+ pVmaList->m_pPageHead = pPages; -+ pVmaList->m_pPageTail = pPages; -+ -+ pVma->vm_ops = &g_vmOps4k; -+ pVma->vm_flags |= VM_IO; -+ -+ VmaOpen4k(pVma); -+ -+ return 0; -+} -+ -+/****** VMA OPERATIONS ******/ -+ -+static void VmaOpen4k(struct vm_area_struct *pVma) -+{ -+ struct VmaPageList *pVmaList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "vma open %p private %p (%s %d), %d live pages\n", pVma, pVma->vm_private_data, current->comm, current->pid, g_trackedPages); -+ PRINTK_VERBOSE(KERN_DEBUG "OPEN %p %d %ld pages (tracked pages %d)\n", -+ pVma, current->pid, (pVma->vm_end - pVma->vm_start) >> 12, -+ g_trackedPages); -+ -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ if (pVmaList) -+ { -+ pVmaList->m_refCount++; -+ PRINTK_VERBOSE(KERN_DEBUG "ref count is now %d\n", pVmaList->m_refCount); -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "err, open but no vma page list\n"); -+ } -+} -+ -+static void VmaClose4k(struct vm_area_struct *pVma) -+{ -+ struct VmaPageList *pVmaList; -+ int freed = 0; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "vma close %p private %p (%s %d)\n", pVma, pVma->vm_private_data, current->comm, current->pid); -+ -+ //wait for any dmas to finish -+ DmaWaitAll(); -+ -+ //find our vma in the list -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ //may be a fork -+ if (pVmaList) -+ { -+ struct PageList *pPages; -+ -+ pVmaList->m_refCount--; -+ -+ if (pVmaList->m_refCount == 0) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "found vma, freeing pages (%s %d)\n", -+ current->comm, current->pid); -+ -+ pPages = pVmaList->m_pPageHead; -+ -+ if (!pPages) -+ { -+ PRINTK(KERN_ERR "no page list (%s %d)!\n", -+ current->comm, current->pid); -+ return; -+ } -+ -+ while (pPages) -+ { -+ struct PageList *next; -+ int count; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "page list (%s %d)\n", -+ current->comm, current->pid); -+ -+ next = pPages->m_pNext; -+ for (count = 0; count < pPages->m_used; count++) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "freeing page %p (%s %d)\n", -+ pPages->m_pPages[count], -+ current->comm, current->pid); -+ __free_pages(pPages->m_pPages[count], 0); -+ g_trackedPages--; -+ freed++; -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "freeing page list (%s %d)\n", -+ current->comm, current->pid); -+ kfree(pPages); -+ pPages = next; -+ } -+ -+ //remove our vma from the list -+ kfree(pVmaList); -+ pVma->vm_private_data = 0; -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "ref count is %d, not closing\n", pVmaList->m_refCount); -+ } -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_ERR "uh-oh, vma %p not found (%s %d)!\n", pVma, current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_ERR "CLOSE ERR\n"); -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "CLOSE %p %d %d pages (tracked pages %d)", -+ pVma, current->pid, freed, g_trackedPages); -+ -+ PRINTK_VERBOSE(KERN_DEBUG "%d pages open\n", g_trackedPages); -+} -+ -+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf) -+{ -+ PRINTK_VERBOSE(KERN_DEBUG "vma fault for vma %p private %p at offset %ld (%s %d)\n", pVma, pVma->vm_private_data, pVmf->pgoff, -+ current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_DEBUG "FAULT\n"); -+ pVmf->page = alloc_page(GFP_KERNEL); -+ -+ if (pVmf->page) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "alloc page virtual %p\n", page_address(pVmf->page)); -+ } -+ -+ if (!pVmf->page) -+ { -+ PRINTK(KERN_ERR "vma fault oom (%s %d)\n", current->comm, current->pid); -+ return VM_FAULT_OOM; -+ } -+ else -+ { -+ struct VmaPageList *pVmaList; -+ -+ get_page(pVmf->page); -+ g_trackedPages++; -+ -+ //find our vma in the list -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ if (pVmaList) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "vma found (%s %d)\n", current->comm, current->pid); -+ -+ if (pVmaList->m_pPageTail->m_used == PAGES_PER_LIST) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "making new page list (%s %d)\n", current->comm, current->pid); -+ //making a new page list -+ pVmaList->m_pPageTail->m_pNext = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); -+ if (!pVmaList->m_pPageTail->m_pNext) -+ return -ENOMEM; -+ -+ //update the tail pointer -+ pVmaList->m_pPageTail = pVmaList->m_pPageTail->m_pNext; -+ pVmaList->m_pPageTail->m_used = 0; -+ pVmaList->m_pPageTail->m_pNext = 0; -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "adding page to list (%s %d)\n", current->comm, current->pid); -+ -+ pVmaList->m_pPageTail->m_pPages[pVmaList->m_pPageTail->m_used] = pVmf->page; -+ pVmaList->m_pPageTail->m_used++; -+ } -+ else -+ PRINTK(KERN_ERR "returned page for vma we don\'t know %p (%s %d)\n", pVma, current->comm, current->pid); -+ -+ return 0; -+ } -+} -+ -+/****** GENERIC FUNCTIONS ******/ -+static int __init dmaer_init(void) -+{ -+ int result = alloc_chrdev_region(&g_majorMinor, 0, 1, "dmaer"); -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "unable to get major device number\n"); -+ return result; -+ } -+ else -+ PRINTK(KERN_DEBUG "major device number %d\n", MAJOR(g_majorMinor)); -+ -+ PRINTK(KERN_DEBUG "vma list size %d, page list size %d, page size %ld\n", -+ sizeof(struct VmaPageList), sizeof(struct PageList), PAGE_SIZE); -+ -+ //get a dma channel to work with -+ result = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, (void **)&g_pDmaChanBase, &g_dmaIrq); -+ -+ //uncomment to force to channel 0 -+ //result = 0; -+ //g_pDmaChanBase = 0xce808000; -+ -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "failed to allocate dma channel\n"); -+ cdev_del(&g_cDev); -+ unregister_chrdev_region(g_majorMinor, 1); -+ } -+ -+ //reset the channel -+ PRINTK(KERN_DEBUG "allocated dma channel %d (%p), initial state %08x\n", result, g_pDmaChanBase, *g_pDmaChanBase); -+ *g_pDmaChanBase = 1 << 31; -+ PRINTK(KERN_DEBUG "post-reset %08x\n", *g_pDmaChanBase); -+ -+ g_dmaChan = result; -+ -+ //clear the cache stats -+ g_cacheHit = 0; -+ g_cacheMiss = 0; -+ -+ //register our device - after this we are go go go -+ cdev_init(&g_cDev, &g_fOps); -+ g_cDev.owner = THIS_MODULE; -+ g_cDev.ops = &g_fOps; -+ -+ result = cdev_add(&g_cDev, g_majorMinor, 1); -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "failed to add character device\n"); -+ unregister_chrdev_region(g_majorMinor, 1); -+ bcm_dma_chan_free(g_dmaChan); -+ return result; -+ } -+ -+ return 0; -+} -+ -+static void __exit dmaer_exit(void) -+{ -+ PRINTK(KERN_INFO "closing dmaer device, cache stats: %d hits %d misses\n", g_cacheHit, g_cacheMiss); -+ //unregister the device -+ cdev_del(&g_cDev); -+ unregister_chrdev_region(g_majorMinor, 1); -+ //free the dma channel -+ bcm_dma_chan_free(g_dmaChan); -+} -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Simon Hall"); -+module_init(dmaer_init); -+module_exit(dmaer_exit); -diff --git a/arch/arm/mach-bcm2709/include/mach/arm_control.h b/arch/arm/mach-bcm2709/include/mach/arm_control.h -new file mode 100644 -index 0000000..e346caf ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/arm_control.h -@@ -0,0 +1,493 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/arm_control.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_ARM_CONTROL_H -+#define __BCM2708_ARM_CONTROL_H -+ -+/* -+ * Definitions and addresses for the ARM CONTROL logic -+ * This file is manually generated. -+ */ -+ -+#define ARM_BASE 0x7E00B000 -+ -+/* Basic configuration */ -+#define ARM_CONTROL0 HW_REGISTER_RW(ARM_BASE+0x000) -+#define ARM_C0_SIZ128M 0x00000000 -+#define ARM_C0_SIZ256M 0x00000001 -+#define ARM_C0_SIZ512M 0x00000002 -+#define ARM_C0_SIZ1G 0x00000003 -+#define ARM_C0_BRESP0 0x00000000 -+#define ARM_C0_BRESP1 0x00000004 -+#define ARM_C0_BRESP2 0x00000008 -+#define ARM_C0_BOOTHI 0x00000010 -+#define ARM_C0_UNUSED05 0x00000020 /* free */ -+#define ARM_C0_FULLPERI 0x00000040 -+#define ARM_C0_UNUSED78 0x00000180 /* free */ -+#define ARM_C0_JTAGMASK 0x00000E00 -+#define ARM_C0_JTAGOFF 0x00000000 -+#define ARM_C0_JTAGBASH 0x00000800 /* Debug on GPIO off */ -+#define ARM_C0_JTAGGPIO 0x00000C00 /* Debug on GPIO on */ -+#define ARM_C0_APROTMSK 0x0000F000 -+#define ARM_C0_DBG0SYNC 0x00010000 /* VPU0 halt sync */ -+#define ARM_C0_DBG1SYNC 0x00020000 /* VPU1 halt sync */ -+#define ARM_C0_SWDBGREQ 0x00040000 /* HW debug request */ -+#define ARM_C0_PASSHALT 0x00080000 /* ARM halt passed to debugger */ -+#define ARM_C0_PRIO_PER 0x00F00000 /* per priority mask */ -+#define ARM_C0_PRIO_L2 0x0F000000 -+#define ARM_C0_PRIO_UC 0xF0000000 -+ -+#define ARM_C0_APROTPASS 0x0000A000 /* Translate 1:1 */ -+#define ARM_C0_APROTUSER 0x00000000 /* Only user mode */ -+#define ARM_C0_APROTSYST 0x0000F000 /* Only system mode */ -+ -+ -+#define ARM_CONTROL1 HW_REGISTER_RW(ARM_BASE+0x440) -+#define ARM_C1_TIMER 0x00000001 /* re-route timer IRQ to VC */ -+#define ARM_C1_MAIL 0x00000002 /* re-route Mail IRQ to VC */ -+#define ARM_C1_BELL0 0x00000004 /* re-route Doorbell 0 to VC */ -+#define ARM_C1_BELL1 0x00000008 /* re-route Doorbell 1 to VC */ -+#define ARM_C1_PERSON 0x00000100 /* peripherals on */ -+#define ARM_C1_REQSTOP 0x00000200 /* ASYNC bridge request stop */ -+ -+#define ARM_STATUS HW_REGISTER_RW(ARM_BASE+0x444) -+#define ARM_S_ACKSTOP 0x80000000 /* Bridge stopped */ -+#define ARM_S_READPEND 0x000003FF /* pending reads counter */ -+#define ARM_S_WRITPEND 0x000FFC00 /* pending writes counter */ -+ -+#define ARM_ERRHALT HW_REGISTER_RW(ARM_BASE+0x448) -+#define ARM_EH_PERIBURST 0x00000001 /* Burst write seen on peri bus */ -+#define ARM_EH_ILLADDRS1 0x00000002 /* Address bits 25-27 error */ -+#define ARM_EH_ILLADDRS2 0x00000004 /* Address bits 31-28 error */ -+#define ARM_EH_VPU0HALT 0x00000008 /* VPU0 halted & in debug mode */ -+#define ARM_EH_VPU1HALT 0x00000010 /* VPU1 halted & in debug mode */ -+#define ARM_EH_ARMHALT 0x00000020 /* ARM in halted debug mode */ -+ -+#define ARM_ID_SECURE HW_REGISTER_RW(ARM_BASE+0x00C) -+#define ARM_ID HW_REGISTER_RW(ARM_BASE+0x44C) -+#define ARM_IDVAL 0x364D5241 -+ -+/* Translation memory */ -+#define ARM_TRANSLATE HW_REGISTER_RW(ARM_BASE+0x100) -+/* 32 locations: 0x100.. 0x17F */ -+/* 32 spare means we CAN go to 64 pages.... */ -+ -+ -+/* Interrupts */ -+#define ARM_IRQ_PEND0 HW_REGISTER_RW(ARM_BASE+0x200) /* Top IRQ bits */ -+#define ARM_I0_TIMER 0x00000001 /* timer IRQ */ -+#define ARM_I0_MAIL 0x00000002 /* Mail IRQ */ -+#define ARM_I0_BELL0 0x00000004 /* Doorbell 0 */ -+#define ARM_I0_BELL1 0x00000008 /* Doorbell 1 */ -+#define ARM_I0_BANK1 0x00000100 /* Bank1 IRQ */ -+#define ARM_I0_BANK2 0x00000200 /* Bank2 IRQ */ -+ -+#define ARM_IRQ_PEND1 HW_REGISTER_RW(ARM_BASE+0x204) /* All bank1 IRQ bits */ -+/* todo: all I1_interrupt sources */ -+#define ARM_IRQ_PEND2 HW_REGISTER_RW(ARM_BASE+0x208) /* All bank2 IRQ bits */ -+/* todo: all I2_interrupt sources */ -+ -+#define ARM_IRQ_FAST HW_REGISTER_RW(ARM_BASE+0x20C) /* FIQ control */ -+#define ARM_IF_INDEX 0x0000007F /* FIQ select */ -+#define ARM_IF_ENABLE 0x00000080 /* FIQ enable */ -+#define ARM_IF_VCMASK 0x0000003F /* FIQ = (index from VC source) */ -+#define ARM_IF_TIMER 0x00000040 /* FIQ = ARM timer */ -+#define ARM_IF_MAIL 0x00000041 /* FIQ = ARM Mail */ -+#define ARM_IF_BELL0 0x00000042 /* FIQ = ARM Doorbell 0 */ -+#define ARM_IF_BELL1 0x00000043 /* FIQ = ARM Doorbell 1 */ -+#define ARM_IF_VP0HALT 0x00000044 /* FIQ = VPU0 Halt seen */ -+#define ARM_IF_VP1HALT 0x00000045 /* FIQ = VPU1 Halt seen */ -+#define ARM_IF_ILLEGAL 0x00000046 /* FIQ = Illegal access seen */ -+ -+#define ARM_IRQ_ENBL1 HW_REGISTER_RW(ARM_BASE+0x210) /* Bank1 enable bits */ -+#define ARM_IRQ_ENBL2 HW_REGISTER_RW(ARM_BASE+0x214) /* Bank2 enable bits */ -+#define ARM_IRQ_ENBL3 HW_REGISTER_RW(ARM_BASE+0x218) /* ARM irqs enable bits */ -+#define ARM_IRQ_DIBL1 HW_REGISTER_RW(ARM_BASE+0x21C) /* Bank1 disable bits */ -+#define ARM_IRQ_DIBL2 HW_REGISTER_RW(ARM_BASE+0x220) /* Bank2 disable bits */ -+#define ARM_IRQ_DIBL3 HW_REGISTER_RW(ARM_BASE+0x224) /* ARM irqs disable bits */ -+#define ARM_IE_TIMER 0x00000001 /* Timer IRQ */ -+#define ARM_IE_MAIL 0x00000002 /* Mail IRQ */ -+#define ARM_IE_BELL0 0x00000004 /* Doorbell 0 */ -+#define ARM_IE_BELL1 0x00000008 /* Doorbell 1 */ -+#define ARM_IE_VP0HALT 0x00000010 /* VPU0 Halt */ -+#define ARM_IE_VP1HALT 0x00000020 /* VPU1 Halt */ -+#define ARM_IE_ILLEGAL 0x00000040 /* Illegal access seen */ -+ -+/* Timer */ -+/* For reg. fields see sp804 spec. */ -+#define ARM_T_LOAD HW_REGISTER_RW(ARM_BASE+0x400) -+#define ARM_T_VALUE HW_REGISTER_RW(ARM_BASE+0x404) -+#define ARM_T_CONTROL HW_REGISTER_RW(ARM_BASE+0x408) -+#define ARM_T_IRQCNTL HW_REGISTER_RW(ARM_BASE+0x40C) -+#define ARM_T_RAWIRQ HW_REGISTER_RW(ARM_BASE+0x410) -+#define ARM_T_MSKIRQ HW_REGISTER_RW(ARM_BASE+0x414) -+#define ARM_T_RELOAD HW_REGISTER_RW(ARM_BASE+0x418) -+#define ARM_T_PREDIV HW_REGISTER_RW(ARM_BASE+0x41c) -+#define ARM_T_FREECNT HW_REGISTER_RW(ARM_BASE+0x420) -+ -+#define TIMER_CTRL_ONESHOT (1 << 0) -+#define TIMER_CTRL_32BIT (1 << 1) -+#define TIMER_CTRL_DIV1 (0 << 2) -+#define TIMER_CTRL_DIV16 (1 << 2) -+#define TIMER_CTRL_DIV256 (2 << 2) -+#define TIMER_CTRL_IE (1 << 5) -+#define TIMER_CTRL_PERIODIC (1 << 6) -+#define TIMER_CTRL_ENABLE (1 << 7) -+#define TIMER_CTRL_DBGHALT (1 << 8) -+#define TIMER_CTRL_ENAFREE (1 << 9) -+#define TIMER_CTRL_FREEDIV_SHIFT 16) -+#define TIMER_CTRL_FREEDIV_MASK 0xff -+ -+/* Semaphores, Doorbells, Mailboxes */ -+#define ARM_SBM_OWN0 (ARM_BASE+0x800) -+#define ARM_SBM_OWN1 (ARM_BASE+0x900) -+#define ARM_SBM_OWN2 (ARM_BASE+0xA00) -+#define ARM_SBM_OWN3 (ARM_BASE+0xB00) -+ -+/* MAILBOXES -+ * Register flags are common across all -+ * owner registers. See end of this section -+ * -+ * Semaphores, Doorbells, Mailboxes Owner 0 -+ * -+ */ -+ -+#define ARM_0_SEMS HW_REGISTER_RW(ARM_SBM_OWN0+0x00) -+#define ARM_0_SEM0 HW_REGISTER_RW(ARM_SBM_OWN0+0x00) -+#define ARM_0_SEM1 HW_REGISTER_RW(ARM_SBM_OWN0+0x04) -+#define ARM_0_SEM2 HW_REGISTER_RW(ARM_SBM_OWN0+0x08) -+#define ARM_0_SEM3 HW_REGISTER_RW(ARM_SBM_OWN0+0x0C) -+#define ARM_0_SEM4 HW_REGISTER_RW(ARM_SBM_OWN0+0x10) -+#define ARM_0_SEM5 HW_REGISTER_RW(ARM_SBM_OWN0+0x14) -+#define ARM_0_SEM6 HW_REGISTER_RW(ARM_SBM_OWN0+0x18) -+#define ARM_0_SEM7 HW_REGISTER_RW(ARM_SBM_OWN0+0x1C) -+#define ARM_0_BELL0 HW_REGISTER_RW(ARM_SBM_OWN0+0x40) -+#define ARM_0_BELL1 HW_REGISTER_RW(ARM_SBM_OWN0+0x44) -+#define ARM_0_BELL2 HW_REGISTER_RW(ARM_SBM_OWN0+0x48) -+#define ARM_0_BELL3 HW_REGISTER_RW(ARM_SBM_OWN0+0x4C) -+/* MAILBOX 0 access in Owner 0 area */ -+/* Some addresses should ONLY be used by owner 0 */ -+#define ARM_0_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) */ -+#define ARM_0_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) Normal read */ -+#define ARM_0_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN0+0x90) /* none-pop read */ -+#define ARM_0_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN0+0x94) /* Sender read (only LS 2 bits) */ -+#define ARM_0_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN0+0x98) /* Status read */ -+#define ARM_0_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0x9C) /* Config read/write */ -+/* MAILBOX 1 access in Owner 0 area */ -+/* Owner 0 should only WRITE to this mailbox */ -+#define ARM_0_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_0_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_0_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN0+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_0_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN0+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_0_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN0+0xB8) /* Status read */ -+/*#define ARM_0_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_0_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE0) /* semaphore clear/debug register */ -+#define ARM_0_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE4) /* Doorbells clear/debug register */ -+#define ARM_0_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xF8) /* ALL interrupts */ -+#define ARM_0_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xFC) /* IRQS pending for owner 0 */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 1 */ -+#define ARM_1_SEMS HW_REGISTER_RW(ARM_SBM_OWN1+0x00) -+#define ARM_1_SEM0 HW_REGISTER_RW(ARM_SBM_OWN1+0x00) -+#define ARM_1_SEM1 HW_REGISTER_RW(ARM_SBM_OWN1+0x04) -+#define ARM_1_SEM2 HW_REGISTER_RW(ARM_SBM_OWN1+0x08) -+#define ARM_1_SEM3 HW_REGISTER_RW(ARM_SBM_OWN1+0x0C) -+#define ARM_1_SEM4 HW_REGISTER_RW(ARM_SBM_OWN1+0x10) -+#define ARM_1_SEM5 HW_REGISTER_RW(ARM_SBM_OWN1+0x14) -+#define ARM_1_SEM6 HW_REGISTER_RW(ARM_SBM_OWN1+0x18) -+#define ARM_1_SEM7 HW_REGISTER_RW(ARM_SBM_OWN1+0x1C) -+#define ARM_1_BELL0 HW_REGISTER_RW(ARM_SBM_OWN1+0x40) -+#define ARM_1_BELL1 HW_REGISTER_RW(ARM_SBM_OWN1+0x44) -+#define ARM_1_BELL2 HW_REGISTER_RW(ARM_SBM_OWN1+0x48) -+#define ARM_1_BELL3 HW_REGISTER_RW(ARM_SBM_OWN1+0x4C) -+/* MAILBOX 0 access in Owner 0 area */ -+/* Owner 1 should only WRITE to this mailbox */ -+#define ARM_1_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_1_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN1+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_1_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN1+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_1_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN1+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_1_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN1+0x98) /* Status read */ -+/*#define ARM_1_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 0 area */ -+#define ARM_1_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) */ -+#define ARM_1_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) Normal read */ -+#define ARM_1_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN1+0xB0) /* none-pop read */ -+#define ARM_1_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN1+0xB4) /* Sender read (only LS 2 bits) */ -+#define ARM_1_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN1+0xB8) /* Status read */ -+#define ARM_1_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0xBC) -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_1_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE0) /* semaphore clear/debug register */ -+#define ARM_1_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE4) /* Doorbells clear/debug register */ -+#define ARM_1_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xFC) /* IRQS pending for owner 1 */ -+#define ARM_1_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xF8) /* ALL interrupts */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 2 */ -+#define ARM_2_SEMS HW_REGISTER_RW(ARM_SBM_OWN2+0x00) -+#define ARM_2_SEM0 HW_REGISTER_RW(ARM_SBM_OWN2+0x00) -+#define ARM_2_SEM1 HW_REGISTER_RW(ARM_SBM_OWN2+0x04) -+#define ARM_2_SEM2 HW_REGISTER_RW(ARM_SBM_OWN2+0x08) -+#define ARM_2_SEM3 HW_REGISTER_RW(ARM_SBM_OWN2+0x0C) -+#define ARM_2_SEM4 HW_REGISTER_RW(ARM_SBM_OWN2+0x10) -+#define ARM_2_SEM5 HW_REGISTER_RW(ARM_SBM_OWN2+0x14) -+#define ARM_2_SEM6 HW_REGISTER_RW(ARM_SBM_OWN2+0x18) -+#define ARM_2_SEM7 HW_REGISTER_RW(ARM_SBM_OWN2+0x1C) -+#define ARM_2_BELL0 HW_REGISTER_RW(ARM_SBM_OWN2+0x40) -+#define ARM_2_BELL1 HW_REGISTER_RW(ARM_SBM_OWN2+0x44) -+#define ARM_2_BELL2 HW_REGISTER_RW(ARM_SBM_OWN2+0x48) -+#define ARM_2_BELL3 HW_REGISTER_RW(ARM_SBM_OWN2+0x4C) -+/* MAILBOX 0 access in Owner 2 area */ -+/* Owner 2 should only WRITE to this mailbox */ -+#define ARM_2_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_2_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN2+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN2+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN2+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_2_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN2+0x98) /* Status read */ -+/*#define ARM_2_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 2 area */ -+/* Owner 2 should only WRITE to this mailbox */ -+#define ARM_2_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_2_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN2+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN2+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_2_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN2+0xB8) /* Status read */ -+/*#define ARM_2_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_2_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE0) /* semaphore clear/debug register */ -+#define ARM_2_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE4) /* Doorbells clear/debug register */ -+#define ARM_2_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xFC) /* IRQS pending for owner 2 */ -+#define ARM_2_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xF8) /* ALL interrupts */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 3 */ -+#define ARM_3_SEMS HW_REGISTER_RW(ARM_SBM_OWN3+0x00) -+#define ARM_3_SEM0 HW_REGISTER_RW(ARM_SBM_OWN3+0x00) -+#define ARM_3_SEM1 HW_REGISTER_RW(ARM_SBM_OWN3+0x04) -+#define ARM_3_SEM2 HW_REGISTER_RW(ARM_SBM_OWN3+0x08) -+#define ARM_3_SEM3 HW_REGISTER_RW(ARM_SBM_OWN3+0x0C) -+#define ARM_3_SEM4 HW_REGISTER_RW(ARM_SBM_OWN3+0x10) -+#define ARM_3_SEM5 HW_REGISTER_RW(ARM_SBM_OWN3+0x14) -+#define ARM_3_SEM6 HW_REGISTER_RW(ARM_SBM_OWN3+0x18) -+#define ARM_3_SEM7 HW_REGISTER_RW(ARM_SBM_OWN3+0x1C) -+#define ARM_3_BELL0 HW_REGISTER_RW(ARM_SBM_OWN3+0x40) -+#define ARM_3_BELL1 HW_REGISTER_RW(ARM_SBM_OWN3+0x44) -+#define ARM_3_BELL2 HW_REGISTER_RW(ARM_SBM_OWN3+0x48) -+#define ARM_3_BELL3 HW_REGISTER_RW(ARM_SBM_OWN3+0x4C) -+/* MAILBOX 0 access in Owner 3 area */ -+/* Owner 3 should only WRITE to this mailbox */ -+#define ARM_3_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_3_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN3+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN3+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN3+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_3_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN3+0x98) /* Status read */ -+/*#define ARM_3_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 3 area */ -+/* Owner 3 should only WRITE to this mailbox */ -+#define ARM_3_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_3_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN3+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN3+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_3_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN3+0xB8) /* Status read */ -+/*#define ARM_3_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_3_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE0) /* semaphore clear/debug register */ -+#define ARM_3_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE4) /* Doorbells clear/debug register */ -+#define ARM_3_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xFC) /* IRQS pending for owner 3 */ -+#define ARM_3_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xF8) /* ALL interrupts */ -+ -+ -+ -+/* Mailbox flags. Valid for all owners */ -+ -+/* Mailbox status register (...0x98) */ -+#define ARM_MS_FULL 0x80000000 -+#define ARM_MS_EMPTY 0x40000000 -+#define ARM_MS_LEVEL 0x400000FF /* Max. value depdnds on mailbox depth parameter */ -+ -+/* MAILBOX config/status register (...0x9C) */ -+/* ANY write to this register clears the error bits! */ -+#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mailbox irq enable: has data */ -+#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mailbox irq enable: has space */ -+#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mailbox irq enable: Opp. is empty */ -+#define ARM_MC_MAIL_CLEAR 0x00000008 /* mailbox clear write 1, then 0 */ -+#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mailbox irq pending: has space */ -+#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mailbox irq pending: Opp. is empty */ -+#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mailbox irq pending */ -+/* Bit 7 is unused */ -+#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ -+#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ -+#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ -+ -+/* Semaphore clear/debug register (...0xE0) */ -+#define ARM_SD_OWN0 0x00000003 /* Owner of sem 0 */ -+#define ARM_SD_OWN1 0x0000000C /* Owner of sem 1 */ -+#define ARM_SD_OWN2 0x00000030 /* Owner of sem 2 */ -+#define ARM_SD_OWN3 0x000000C0 /* Owner of sem 3 */ -+#define ARM_SD_OWN4 0x00000300 /* Owner of sem 4 */ -+#define ARM_SD_OWN5 0x00000C00 /* Owner of sem 5 */ -+#define ARM_SD_OWN6 0x00003000 /* Owner of sem 6 */ -+#define ARM_SD_OWN7 0x0000C000 /* Owner of sem 7 */ -+#define ARM_SD_SEM0 0x00010000 /* Status of sem 0 */ -+#define ARM_SD_SEM1 0x00020000 /* Status of sem 1 */ -+#define ARM_SD_SEM2 0x00040000 /* Status of sem 2 */ -+#define ARM_SD_SEM3 0x00080000 /* Status of sem 3 */ -+#define ARM_SD_SEM4 0x00100000 /* Status of sem 4 */ -+#define ARM_SD_SEM5 0x00200000 /* Status of sem 5 */ -+#define ARM_SD_SEM6 0x00400000 /* Status of sem 6 */ -+#define ARM_SD_SEM7 0x00800000 /* Status of sem 7 */ -+ -+/* Doorbells clear/debug register (...0xE4) */ -+#define ARM_BD_OWN0 0x00000003 /* Owner of doorbell 0 */ -+#define ARM_BD_OWN1 0x0000000C /* Owner of doorbell 1 */ -+#define ARM_BD_OWN2 0x00000030 /* Owner of doorbell 2 */ -+#define ARM_BD_OWN3 0x000000C0 /* Owner of doorbell 3 */ -+#define ARM_BD_BELL0 0x00000100 /* Status of doorbell 0 */ -+#define ARM_BD_BELL1 0x00000200 /* Status of doorbell 1 */ -+#define ARM_BD_BELL2 0x00000400 /* Status of doorbell 2 */ -+#define ARM_BD_BELL3 0x00000800 /* Status of doorbell 3 */ -+ -+/* MY IRQS register (...0xF8) */ -+#define ARM_MYIRQ_BELL 0x00000001 /* This owner has a doorbell IRQ */ -+#define ARM_MYIRQ_MAIL 0x00000002 /* This owner has a mailbox IRQ */ -+ -+/* ALL IRQS register (...0xF8) */ -+#define ARM_AIS_BELL0 0x00000001 /* Doorbell 0 IRQ pending */ -+#define ARM_AIS_BELL1 0x00000002 /* Doorbell 1 IRQ pending */ -+#define ARM_AIS_BELL2 0x00000004 /* Doorbell 2 IRQ pending */ -+#define ARM_AIS_BELL3 0x00000008 /* Doorbell 3 IRQ pending */ -+#define ARM_AIS0_HAVEDATA 0x00000010 /* MAIL 0 has data IRQ pending */ -+#define ARM_AIS0_HAVESPAC 0x00000020 /* MAIL 0 has space IRQ pending */ -+#define ARM_AIS0_OPPEMPTY 0x00000040 /* MAIL 0 opposite is empty IRQ */ -+#define ARM_AIS1_HAVEDATA 0x00000080 /* MAIL 1 has data IRQ pending */ -+#define ARM_AIS1_HAVESPAC 0x00000100 /* MAIL 1 has space IRQ pending */ -+#define ARM_AIS1_OPPEMPTY 0x00000200 /* MAIL 1 opposite is empty IRQ */ -+/* Note that bell-0, bell-1 and MAIL0 IRQ go only to the ARM */ -+/* Whilst that bell-2, bell-3 and MAIL1 IRQ go only to the VC */ -+/* */ -+/* ARM JTAG BASH */ -+/* */ -+#define AJB_BASE 0x7e2000c0 -+ -+#define AJBCONF HW_REGISTER_RW(AJB_BASE+0x00) -+#define AJB_BITS0 0x000000 -+#define AJB_BITS4 0x000004 -+#define AJB_BITS8 0x000008 -+#define AJB_BITS12 0x00000C -+#define AJB_BITS16 0x000010 -+#define AJB_BITS20 0x000014 -+#define AJB_BITS24 0x000018 -+#define AJB_BITS28 0x00001C -+#define AJB_BITS32 0x000020 -+#define AJB_BITS34 0x000022 -+#define AJB_OUT_MS 0x000040 -+#define AJB_OUT_LS 0x000000 -+#define AJB_INV_CLK 0x000080 -+#define AJB_D0_RISE 0x000100 -+#define AJB_D0_FALL 0x000000 -+#define AJB_D1_RISE 0x000200 -+#define AJB_D1_FALL 0x000000 -+#define AJB_IN_RISE 0x000400 -+#define AJB_IN_FALL 0x000000 -+#define AJB_ENABLE 0x000800 -+#define AJB_HOLD0 0x000000 -+#define AJB_HOLD1 0x001000 -+#define AJB_HOLD2 0x002000 -+#define AJB_HOLD3 0x003000 -+#define AJB_RESETN 0x004000 -+#define AJB_CLKSHFT 16 -+#define AJB_BUSY 0x80000000 -+#define AJBTMS HW_REGISTER_RW(AJB_BASE+0x04) -+#define AJBTDI HW_REGISTER_RW(AJB_BASE+0x08) -+#define AJBTDO HW_REGISTER_RW(AJB_BASE+0x0c) -+ -+#define ARM_LOCAL_BASE 0x40000000 -+#define ARM_LOCAL_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x000) -+#define ARM_LOCAL_PRESCALER HW_REGISTER_RW(ARM_LOCAL_BASE+0x008) -+#define ARM_LOCAL_GPU_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x00C) -+#define ARM_LOCAL_PM_ROUTING_SET HW_REGISTER_RW(ARM_LOCAL_BASE+0x010) -+#define ARM_LOCAL_PM_ROUTING_CLR HW_REGISTER_RW(ARM_LOCAL_BASE+0x014) -+#define ARM_LOCAL_TIMER_LS HW_REGISTER_RW(ARM_LOCAL_BASE+0x01C) -+#define ARM_LOCAL_TIMER_MS HW_REGISTER_RW(ARM_LOCAL_BASE+0x020) -+#define ARM_LOCAL_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x024) -+#define ARM_LOCAL_AXI_COUNT HW_REGISTER_RW(ARM_LOCAL_BASE+0x02C) -+#define ARM_LOCAL_AXI_IRQ HW_REGISTER_RW(ARM_LOCAL_BASE+0x030) -+#define ARM_LOCAL_TIMER_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x034) -+#define ARM_LOCAL_TIMER_WRITE HW_REGISTER_RW(ARM_LOCAL_BASE+0x038) -+ -+#define ARM_LOCAL_TIMER_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x040) -+#define ARM_LOCAL_TIMER_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x044) -+#define ARM_LOCAL_TIMER_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x048) -+#define ARM_LOCAL_TIMER_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x04C) -+ -+#define ARM_LOCAL_MAILBOX_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x050) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x054) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x058) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x05C) -+ -+#define ARM_LOCAL_IRQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x060) -+#define ARM_LOCAL_IRQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x064) -+#define ARM_LOCAL_IRQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x068) -+#define ARM_LOCAL_IRQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x06C) -+ -+#define ARM_LOCAL_FIQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x070) -+#define ARM_LOCAL_FIQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x074) -+#define ARM_LOCAL_FIQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x078) -+#define ARM_LOCAL_FIQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x07C) -+ -+#define ARM_LOCAL_MAILBOX0_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x080) -+#define ARM_LOCAL_MAILBOX1_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x084) -+#define ARM_LOCAL_MAILBOX2_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x088) -+#define ARM_LOCAL_MAILBOX3_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x08C) -+ -+#define ARM_LOCAL_MAILBOX0_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x090) -+#define ARM_LOCAL_MAILBOX1_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x094) -+#define ARM_LOCAL_MAILBOX2_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x098) -+#define ARM_LOCAL_MAILBOX3_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x09C) -+ -+#define ARM_LOCAL_MAILBOX0_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A0) -+#define ARM_LOCAL_MAILBOX1_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A4) -+#define ARM_LOCAL_MAILBOX2_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A8) -+#define ARM_LOCAL_MAILBOX3_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0AC) -+ -+#define ARM_LOCAL_MAILBOX0_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B0) -+#define ARM_LOCAL_MAILBOX1_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B4) -+#define ARM_LOCAL_MAILBOX2_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B8) -+#define ARM_LOCAL_MAILBOX3_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0BC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C0) -+#define ARM_LOCAL_MAILBOX1_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C4) -+#define ARM_LOCAL_MAILBOX2_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C8) -+#define ARM_LOCAL_MAILBOX3_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0CC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D0) -+#define ARM_LOCAL_MAILBOX1_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D4) -+#define ARM_LOCAL_MAILBOX2_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D8) -+#define ARM_LOCAL_MAILBOX3_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0DC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E0) -+#define ARM_LOCAL_MAILBOX1_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E4) -+#define ARM_LOCAL_MAILBOX2_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E8) -+#define ARM_LOCAL_MAILBOX3_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0EC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F0) -+#define ARM_LOCAL_MAILBOX1_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F4) -+#define ARM_LOCAL_MAILBOX2_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F8) -+#define ARM_LOCAL_MAILBOX3_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0FC) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/arm_power.h b/arch/arm/mach-bcm2709/include/mach/arm_power.h -new file mode 100644 -index 0000000..d3bf245 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/arm_power.h -@@ -0,0 +1,62 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/include/mach/arm_power.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _ARM_POWER_H -+#define _ARM_POWER_H -+ -+/* Use meaningful names on each side */ -+#ifdef __VIDEOCORE__ -+#define PREFIX(x) ARM_##x -+#else -+#define PREFIX(x) BCM_##x -+#endif -+ -+enum { -+ PREFIX(POWER_SDCARD_BIT), -+ PREFIX(POWER_UART_BIT), -+ PREFIX(POWER_MINIUART_BIT), -+ PREFIX(POWER_USB_BIT), -+ PREFIX(POWER_I2C0_BIT), -+ PREFIX(POWER_I2C1_BIT), -+ PREFIX(POWER_I2C2_BIT), -+ PREFIX(POWER_SPI_BIT), -+ PREFIX(POWER_CCP2TX_BIT), -+ PREFIX(POWER_DSI_BIT), -+ -+ PREFIX(POWER_MAX) -+}; -+ -+enum { -+ PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)), -+ PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)), -+ PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)), -+ PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)), -+ PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)), -+ PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)), -+ PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)), -+ PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)), -+ PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)), -+ PREFIX(POWER_DSI) = (1 << PREFIX(POWER_DSI_BIT)), -+ -+ PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1, -+ PREFIX(POWER_NONE) = 0 -+}; -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/barriers.h b/arch/arm/mach-bcm2709/include/mach/barriers.h -new file mode 100644 -index 0000000..723cdad ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/barriers.h -@@ -0,0 +1,3 @@ -+#define mb() dsb() -+#define rmb() dsb() -+#define wmb() mb() -diff --git a/arch/arm/mach-bcm2709/include/mach/clkdev.h b/arch/arm/mach-bcm2709/include/mach/clkdev.h -new file mode 100644 -index 0000000..04b37a8 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/clkdev.h -@@ -0,0 +1,7 @@ -+#ifndef __ASM_MACH_CLKDEV_H -+#define __ASM_MACH_CLKDEV_H -+ -+#define __clk_get(clk) ({ 1; }) -+#define __clk_put(clk) do { } while (0) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/debug-macro.S b/arch/arm/mach-bcm2709/include/mach/debug-macro.S -new file mode 100644 -index 0000000..b24304a ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/debug-macro.S -@@ -0,0 +1,22 @@ -+/* arch/arm/mach-bcm2708/include/mach/debug-macro.S -+ * -+ * Debugging macro include header -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 1994-1999 Russell King -+ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks -+ * -+ * 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 -+ -+ .macro addruart, rp, rv, tmp -+ ldr \rp, =UART0_BASE -+ ldr \rv, =IO_ADDRESS(UART0_BASE) -+ .endm -+ -+#include -diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h -new file mode 100644 -index 0000000..d03e7b5 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/dma.h -@@ -0,0 +1,94 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/include/mach/dma.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ */ -+ -+ -+#ifndef _MACH_BCM2708_DMA_H -+#define _MACH_BCM2708_DMA_H -+ -+#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" -+ -+/* DMA CS Control and Status bits */ -+#define BCM2708_DMA_ACTIVE (1 << 0) -+#define BCM2708_DMA_INT (1 << 2) -+#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ -+#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ -+#define BCM2708_DMA_ERR (1 << 8) -+#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ -+#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ -+ -+/* DMA control block "info" field bits */ -+#define BCM2708_DMA_INT_EN (1 << 0) -+#define BCM2708_DMA_TDMODE (1 << 1) -+#define BCM2708_DMA_WAIT_RESP (1 << 3) -+#define BCM2708_DMA_D_INC (1 << 4) -+#define BCM2708_DMA_D_WIDTH (1 << 5) -+#define BCM2708_DMA_D_DREQ (1 << 6) -+#define BCM2708_DMA_S_INC (1 << 8) -+#define BCM2708_DMA_S_WIDTH (1 << 9) -+#define BCM2708_DMA_S_DREQ (1 << 10) -+ -+#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) -+#define BCM2708_DMA_PER_MAP(x) ((x) << 16) -+#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) -+ -+#define BCM2708_DMA_DREQ_EMMC 11 -+#define BCM2708_DMA_DREQ_SDHOST 13 -+ -+#define BCM2708_DMA_CS 0x00 /* Control and Status */ -+#define BCM2708_DMA_ADDR 0x04 -+/* the current control block appears in the following registers - read only */ -+#define BCM2708_DMA_INFO 0x08 -+#define BCM2708_DMA_SOURCE_AD 0x0c -+#define BCM2708_DMA_DEST_AD 0x10 -+#define BCM2708_DMA_NEXTCB 0x1C -+#define BCM2708_DMA_DEBUG 0x20 -+ -+#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) -+#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) -+ -+#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) -+ -+struct bcm2708_dma_cb { -+ unsigned long info; -+ unsigned long src; -+ unsigned long dst; -+ unsigned long length; -+ unsigned long stride; -+ unsigned long next; -+ unsigned long pad[2]; -+}; -+struct scatterlist; -+ -+extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); -+extern void bcm_dma_start(void __iomem *dma_chan_base, -+ dma_addr_t control_block); -+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); -+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); -+extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); -+ -+/* When listing features we can ask for when allocating DMA channels give -+ those with higher priority smaller ordinal numbers */ -+#define BCM_DMA_FEATURE_FAST_ORD 0 -+#define BCM_DMA_FEATURE_BULK_ORD 1 -+#define BCM_DMA_FEATURE_NORMAL_ORD 2 -+#define BCM_DMA_FEATURE_LITE_ORD 3 -+#define BCM_DMA_FEATURE_FAST (1< -+#include -+ -+ .macro disable_fiq -+ .endm -+ -+ .macro get_irqnr_preamble, base, tmp -+ ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE) -+ .endm -+ -+ .macro arch_ret_to_user, tmp1, tmp2 -+ .endm -+ -+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ -+ /* get core's local interrupt controller */ -+ ldr \irqstat, = __io_address(ARM_LOCAL_IRQ_PENDING0) @ local interrupt source -+ add \irqstat, \irqstat, \tmp, lsl #2 -+ ldr \tmp, [\irqstat] -+ /* ignore gpu interrupt */ -+ bic \tmp, #0x100 -+ /* ignore mailbox interrupts */ -+ bics \tmp, #0xf0 -+ beq 1005f -+ -+ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) -+ @ N.B. CLZ is an ARM5 instruction. -+ mov \irqnr, #(ARM_IRQ_LOCAL_BASE + 31) -+ sub \irqstat, \tmp, #1 -+ eor \irqstat, \irqstat, \tmp -+ clz \tmp, \irqstat -+ sub \irqnr, \tmp -+ b 1020f -+1005: -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ -+ cmp \tmp, #1 -+ beq 1020f -+ cmp \tmp, #2 -+ beq 1020f -+ cmp \tmp, #3 -+ beq 1020f -+ -+ /* get masked status */ -+ ldr \irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] -+ mov \irqnr, #(ARM_IRQ0_BASE + 31) -+ and \tmp, \irqstat, #0x300 @ save bits 8 and 9 -+ /* clear bits 8 and 9, and test */ -+ bics \irqstat, \irqstat, #0x300 -+ bne 1010f -+ -+ tst \tmp, #0x100 -+ ldrne \irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)] -+ movne \irqnr, #(ARM_IRQ1_BASE + 31) -+ @ Mask out the interrupts also present in PEND0 - see SW-5809 -+ bicne \irqstat, #((1<<7) | (1<<9) | (1<<10)) -+ bicne \irqstat, #((1<<18) | (1<<19)) -+ bne 1010f -+ -+ tst \tmp, #0x200 -+ ldrne \irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)] -+ movne \irqnr, #(ARM_IRQ2_BASE + 31) -+ @ Mask out the interrupts also present in PEND0 - see SW-5809 -+ bicne \irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25)) -+ bicne \irqstat, #((1<<30)) -+ beq 1020f -+ -+1010: -+ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) -+ @ N.B. CLZ is an ARM5 instruction. -+ sub \tmp, \irqstat, #1 -+ eor \irqstat, \irqstat, \tmp -+ clz \tmp, \irqstat -+ sub \irqnr, \tmp -+ -+1020: @ EQ will be set if no irqs pending -+ -+ .endm -+ -+ .macro test_for_ipi, irqnr, irqstat, base, tmp -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ /* get core's mailbox interrupt control */ -+ ldr \irqstat, = __io_address(ARM_LOCAL_MAILBOX0_CLR0) @ mbox_clr -+ add \irqstat, \irqstat, \tmp, lsl #4 -+ ldr \tmp, [\irqstat] -+ cmp \tmp, #0 -+ beq 1030f -+ clz \tmp, \tmp -+ rsb \irqnr, \tmp, #31 -+ mov \tmp, #1 -+ lsl \tmp, \irqnr -+ str \tmp, [\irqstat] @ clear interrupt source -+ dsb -+1030: @ EQ will be set if no irqs pending -+ .endm -diff --git a/arch/arm/mach-bcm2709/include/mach/frc.h b/arch/arm/mach-bcm2709/include/mach/frc.h -new file mode 100644 -index 0000000..dd51e07 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/frc.h -@@ -0,0 +1,38 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/timex.h -+ * -+ * BCM2708 free running counter (timer) -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _MACH_FRC_H -+#define _MACH_FRC_H -+ -+#define FRC_TICK_RATE (1000000) -+ -+/*! Free running counter incrementing at the CLOCK_TICK_RATE -+ (slightly faster than frc_clock_ticks63() -+ */ -+extern unsigned long frc_clock_ticks32(void); -+ -+/*! Free running counter incrementing at the CLOCK_TICK_RATE -+ * Note - top bit should be ignored (see cnt32_to_63) -+ */ -+extern unsigned long long frc_clock_ticks63(void); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/gpio.h b/arch/arm/mach-bcm2709/include/mach/gpio.h -new file mode 100644 -index 0000000..7965a97 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/gpio.h -@@ -0,0 +1,17 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/gpio.h -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#ifndef __ASM_ARCH_GPIO_H -+#define __ASM_ARCH_GPIO_H -+ -+#define BCM2708_NR_GPIOS 54 // number of gpio lines -+ -+#define gpio_to_irq(x) ((x) + GPIO_IRQ_START) -+#define irq_to_gpio(x) ((x) - GPIO_IRQ_START) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/hardware.h b/arch/arm/mach-bcm2709/include/mach/hardware.h -new file mode 100644 -index 0000000..c2954e8 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/hardware.h -@@ -0,0 +1,28 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/hardware.h -+ * -+ * This file contains the hardware definitions of the BCM2708 devices. -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_HARDWARE_H -+#define __ASM_ARCH_HARDWARE_H -+ -+#include -+#include -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/io.h b/arch/arm/mach-bcm2709/include/mach/io.h -new file mode 100644 -index 0000000..e6eb84d ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/io.h -@@ -0,0 +1,27 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/io.h -+ * -+ * Copyright (C) 2003 ARM Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARM_ARCH_IO_H -+#define __ASM_ARM_ARCH_IO_H -+ -+#define IO_SPACE_LIMIT 0xffffffff -+ -+#define __io(a) __typesafe_io(a) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/irqs.h b/arch/arm/mach-bcm2709/include/mach/irqs.h -new file mode 100644 -index 0000000..d301f06c ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/irqs.h -@@ -0,0 +1,225 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/irqs.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * Copyright (C) 2000 Deep Blue Solutions Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _BCM2708_IRQS_H_ -+#define _BCM2708_IRQS_H_ -+ -+#include -+ -+/* -+ * IRQ interrupts definitions are the same as the INT definitions -+ * held within platform.h -+ */ -+#define IRQ_ARMCTRL_START 0 -+#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) -+#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) -+#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) -+#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) -+#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) -+#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) -+#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) -+#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) -+#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) -+#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) -+#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) -+#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) -+#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) -+#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) -+#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) -+#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) -+#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) -+#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) -+#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) -+#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) -+#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) -+#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) -+#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) -+#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) -+#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) -+#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) -+#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) -+#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) -+#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) -+#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) -+#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) -+#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) -+#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) -+#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) -+#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) -+#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) -+#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) -+#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) -+#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) -+#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) -+#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) -+#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) -+#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) -+#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) -+#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) -+#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) -+#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) -+#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) -+#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) -+#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) -+#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) -+#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) -+#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) -+#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) -+#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) -+#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) -+#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) -+#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) -+#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) -+#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) -+#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) -+#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) -+#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) -+#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) -+ -+#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) -+#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) -+#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) -+#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) -+#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) -+#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) -+#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) -+#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) -+#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) -+#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) -+ -+#define IRQ_ARM_LOCAL_CNTPSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) -+#define IRQ_ARM_LOCAL_CNTPNSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) -+#define IRQ_ARM_LOCAL_CNTHPIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) -+#define IRQ_ARM_LOCAL_CNTVIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) -+#define IRQ_ARM_LOCAL_MAILBOX0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) -+#define IRQ_ARM_LOCAL_MAILBOX1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) -+#define IRQ_ARM_LOCAL_MAILBOX2 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) -+#define IRQ_ARM_LOCAL_MAILBOX3 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) -+#define IRQ_ARM_LOCAL_GPU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) -+#define IRQ_ARM_LOCAL_PMU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) -+#define IRQ_ARM_LOCAL_ZERO (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) -+#define IRQ_ARM_LOCAL_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) -+ -+#define FIQ_START HARD_IRQS -+ -+/* -+ * FIQ interrupts definitions are the same as the INT definitions. -+ */ -+#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) -+#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) -+#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) -+#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) -+#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) -+#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) -+#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) -+#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) -+#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) -+#define FIQ_USB (FIQ_START+INTERRUPT_USB) -+#define FIQ_3D (FIQ_START+INTERRUPT_3D) -+#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) -+#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) -+#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) -+#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) -+#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) -+#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) -+#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) -+#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) -+#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) -+#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) -+#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) -+#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) -+#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) -+#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) -+#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) -+#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) -+#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) -+#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) -+#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) -+#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) -+#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) -+#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) -+#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) -+#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) -+#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) -+#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) -+#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) -+#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) -+#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) -+#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) -+#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) -+#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) -+#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) -+#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) -+#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) -+#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) -+#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) -+#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) -+#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) -+#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) -+#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) -+#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) -+#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) -+#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) -+#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) -+#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) -+#define FIQ_UART (FIQ_START+INTERRUPT_UART) -+#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) -+#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) -+#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) -+#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) -+#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) -+#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) -+ -+#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) -+#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) -+#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) -+#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) -+#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) -+#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) -+#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) -+#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) -+#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) -+#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) -+ -+#define FIQ_ARM_LOCAL_CNTPSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) -+#define FIQ_ARM_LOCAL_CNTPNSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) -+#define FIQ_ARM_LOCAL_CNTHPIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) -+#define FIQ_ARM_LOCAL_CNTVIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) -+#define FIQ_ARM_LOCAL_MAILBOX0 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) -+#define FIQ_ARM_LOCAL_MAILBOX1 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) -+#define FIQ_ARM_LOCAL_MAILBOX2 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) -+#define FIQ_ARM_LOCAL_MAILBOX3 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) -+#define FIQ_ARM_LOCAL_GPU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) -+#define FIQ_ARM_LOCAL_PMU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) -+#define FIQ_ARM_LOCAL_ZERO (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) -+#define FIQ_ARM_LOCAL_TIMER (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) -+ -+#define HARD_IRQS (128) -+#define FIQ_IRQS (128) -+#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) -+#define GPIO_IRQS (32*5) -+#define SPARE_ALLOC_IRQS 64 -+#define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) -+#define FREE_IRQS 128 -+#define NR_IRQS (BCM2708_ALLOC_IRQS+FREE_IRQS) -+ -+#endif /* _BCM2708_IRQS_H_ */ -diff --git a/arch/arm/mach-bcm2709/include/mach/memory.h b/arch/arm/mach-bcm2709/include/mach/memory.h -new file mode 100644 -index 0000000..7548a52 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/memory.h -@@ -0,0 +1,57 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/memory.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_MEMORY_H -+#define __ASM_ARCH_MEMORY_H -+ -+/* Memory overview: -+ -+ [ARMcore] <--virtual addr--> -+ [ARMmmu] <--physical addr--> -+ [GERTmap] <--bus add--> -+ [VCperiph] -+ -+*/ -+ -+/* -+ * Physical DRAM offset. -+ */ -+#define BCM_PLAT_PHYS_OFFSET UL(0x00000000) -+#define VC_ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ -+ -+#ifdef CONFIG_BCM2708_NOL2CACHE -+ #define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ -+#else -+ #define _REAL_BUS_OFFSET UL(0x40000000) /* use L2 cache */ -+#endif -+ -+/* We're using the memory at 64M in the VideoCore for Linux - this adjustment -+ * will provide the offset into this area as well as setting the bits that -+ * stop the L1 and L2 cache from being used -+ * -+ * WARNING: this only works because the ARM is given memory at a fixed location -+ * (ARMMEM_OFFSET) -+ */ -+#define BUS_OFFSET (VC_ARMMEM_OFFSET + _REAL_BUS_OFFSET) -+#define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) -+#define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) -+#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) -+#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/platform.h b/arch/arm/mach-bcm2709/include/mach/platform.h -new file mode 100644 -index 0000000..7157f38 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/platform.h -@@ -0,0 +1,225 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/platform.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _BCM2708_PLATFORM_H -+#define _BCM2708_PLATFORM_H -+ -+ -+/* macros to get at IO space when running virtually */ -+#define IO_ADDRESS(x) (((x) & 0x00ffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) -+ -+#define __io_address(n) IOMEM(IO_ADDRESS(n)) -+ -+ -+/* -+ * SDRAM -+ */ -+#define BCM2708_SDRAM_BASE 0x00000000 -+ -+/* -+ * Logic expansion modules -+ * -+ */ -+ -+ -+/* ------------------------------------------------------------------------ -+ * BCM2708 ARMCTRL Registers -+ * ------------------------------------------------------------------------ -+ */ -+ -+#define HW_REGISTER_RW(addr) (addr) -+#define HW_REGISTER_RO(addr) (addr) -+ -+#include "arm_control.h" -+#undef ARM_BASE -+ -+/* -+ * Definitions and addresses for the ARM CONTROL logic -+ * This file is manually generated. -+ */ -+ -+#define BCM2708_PERI_BASE 0x3F000000 -+#define IC0_BASE (BCM2708_PERI_BASE + 0x2000) -+#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ -+#define MPHI_BASE (BCM2708_PERI_BASE + 0x6000) /* Message -based Parallel Host Interface */ -+#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ -+#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ -+#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ -+#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ -+#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ -+#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ -+#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ -+#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ -+#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */ -+#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */ -+#define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */ -+#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ -+#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ -+#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ -+#define BSC1_BASE (BCM2708_PERI_BASE + 0x804000) /* BSC1 I2C/TWI */ -+#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ -+#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device (actually the multicore sync block*/ -+ -+#define ARMCTRL_BASE (ARM_BASE + 0x000) -+#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ -+#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ -+#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ -+ -+ -+/* -+ * Interrupt assignments -+ */ -+ -+#define ARM_IRQ1_BASE 0 -+#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) -+#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) -+#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) -+#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) -+#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) -+#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) -+#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) -+#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) -+#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) -+#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) -+#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) -+#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) -+#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) -+#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) -+#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) -+#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) -+#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) -+#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) -+#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) -+#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) -+#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) -+#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) -+#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) -+#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) -+#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) -+#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) -+#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) -+#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) -+#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) -+#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) -+#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) -+#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) -+ -+#define ARM_IRQ2_BASE 32 -+#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) -+#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) -+#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) -+#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) -+#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) -+#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) -+#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) -+#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) -+#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) -+#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) -+#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) -+#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) -+#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) -+#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) -+#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) -+#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) -+#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) -+#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) -+#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) -+#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) -+#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) -+#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) -+#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) -+#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) -+#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) -+#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) -+#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) -+#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) -+#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) -+#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) -+#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) -+#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) -+ -+#define ARM_IRQ0_BASE 64 -+#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) -+#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) -+#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) -+#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) -+#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) -+#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) -+#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) -+#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) -+#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) -+#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) -+#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) -+#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) -+#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) -+#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) -+#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) -+#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) -+#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) -+#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) -+#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) -+#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) -+#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) -+ -+#define ARM_IRQ_LOCAL_BASE 96 -+#define INTERRUPT_ARM_LOCAL_CNTPSIRQ (ARM_IRQ_LOCAL_BASE + 0) -+#define INTERRUPT_ARM_LOCAL_CNTPNSIRQ (ARM_IRQ_LOCAL_BASE + 1) -+#define INTERRUPT_ARM_LOCAL_CNTHPIRQ (ARM_IRQ_LOCAL_BASE + 2) -+#define INTERRUPT_ARM_LOCAL_CNTVIRQ (ARM_IRQ_LOCAL_BASE + 3) -+#define INTERRUPT_ARM_LOCAL_MAILBOX0 (ARM_IRQ_LOCAL_BASE + 4) -+#define INTERRUPT_ARM_LOCAL_MAILBOX1 (ARM_IRQ_LOCAL_BASE + 5) -+#define INTERRUPT_ARM_LOCAL_MAILBOX2 (ARM_IRQ_LOCAL_BASE + 6) -+#define INTERRUPT_ARM_LOCAL_MAILBOX3 (ARM_IRQ_LOCAL_BASE + 7) -+#define INTERRUPT_ARM_LOCAL_GPU_FAST (ARM_IRQ_LOCAL_BASE + 8) -+#define INTERRUPT_ARM_LOCAL_PMU_FAST (ARM_IRQ_LOCAL_BASE + 9) -+#define INTERRUPT_ARM_LOCAL_ZERO (ARM_IRQ_LOCAL_BASE + 10) -+#define INTERRUPT_ARM_LOCAL_TIMER (ARM_IRQ_LOCAL_BASE + 11) -+ -+/* -+ * Watchdog -+ */ -+#define PM_RSTC (PM_BASE+0x1c) -+#define PM_RSTS (PM_BASE+0x20) -+#define PM_WDOG (PM_BASE+0x24) -+ -+#define PM_WDOG_RESET 0000000000 -+#define PM_PASSWORD 0x5a000000 -+#define PM_WDOG_TIME_SET 0x000fffff -+#define PM_RSTC_WRCFG_CLR 0xffffffcf -+#define PM_RSTC_WRCFG_SET 0x00000030 -+#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -+#define PM_RSTC_RESET 0x00000102 -+ -+#define PM_RSTS_HADPOR_SET 0x00001000 -+#define PM_RSTS_HADSRH_SET 0x00000400 -+#define PM_RSTS_HADSRF_SET 0x00000200 -+#define PM_RSTS_HADSRQ_SET 0x00000100 -+#define PM_RSTS_HADWRH_SET 0x00000040 -+#define PM_RSTS_HADWRF_SET 0x00000020 -+#define PM_RSTS_HADWRQ_SET 0x00000010 -+#define PM_RSTS_HADDRH_SET 0x00000004 -+#define PM_RSTS_HADDRF_SET 0x00000002 -+#define PM_RSTS_HADDRQ_SET 0x00000001 -+ -+#define UART0_CLOCK 3000000 -+ -+#endif -+ -+/* END */ -diff --git a/arch/arm/mach-bcm2709/include/mach/power.h b/arch/arm/mach-bcm2709/include/mach/power.h -new file mode 100644 -index 0000000..52b3b02 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/power.h -@@ -0,0 +1,26 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/power.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for controlling the power to -+ * VideoCore subsystems. -+ */ -+ -+#ifndef _MACH_BCM2708_POWER_H -+#define _MACH_BCM2708_POWER_H -+ -+#include -+#include -+ -+typedef unsigned int BCM_POWER_HANDLE_T; -+ -+extern int bcm_power_open(BCM_POWER_HANDLE_T *handle); -+extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request); -+extern int bcm_power_close(BCM_POWER_HANDLE_T handle); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/system.h b/arch/arm/mach-bcm2709/include/mach/system.h -new file mode 100644 -index 0000000..2d0b821 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/system.h -@@ -0,0 +1,38 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/system.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * Copyright (C) 2000 Deep Blue Solutions Ltd -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_SYSTEM_H -+#define __ASM_ARCH_SYSTEM_H -+ -+#include -+#include -+#include -+ -+static inline void arch_idle(void) -+{ -+ /* -+ * This should do all the clock switching -+ * and wait for interrupt tricks -+ */ -+ cpu_do_idle(); -+} -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/timex.h b/arch/arm/mach-bcm2709/include/mach/timex.h -new file mode 100644 -index 0000000..64a660c ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/timex.h -@@ -0,0 +1,23 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/timex.h -+ * -+ * BCM2708 sysem clock frequency -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#define CLOCK_TICK_RATE (1000000) -diff --git a/arch/arm/mach-bcm2709/include/mach/uncompress.h b/arch/arm/mach-bcm2709/include/mach/uncompress.h -new file mode 100644 -index 0000000..d634813 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/uncompress.h -@@ -0,0 +1,84 @@ -+/* -+ * arch/arm/mach-bcn2708/include/mach/uncompress.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include -+#include -+#include -+ -+#define UART_BAUD 115200 -+ -+#define BCM2708_UART_DR __io(UART0_BASE + UART01x_DR) -+#define BCM2708_UART_FR __io(UART0_BASE + UART01x_FR) -+#define BCM2708_UART_IBRD __io(UART0_BASE + UART011_IBRD) -+#define BCM2708_UART_FBRD __io(UART0_BASE + UART011_FBRD) -+#define BCM2708_UART_LCRH __io(UART0_BASE + UART011_LCRH) -+#define BCM2708_UART_CR __io(UART0_BASE + UART011_CR) -+ -+/* -+ * This does not append a newline -+ */ -+static inline void putc(int c) -+{ -+ while (__raw_readl(BCM2708_UART_FR) & UART01x_FR_TXFF) -+ barrier(); -+ -+ __raw_writel(c, BCM2708_UART_DR); -+} -+ -+static inline void flush(void) -+{ -+ int fr; -+ -+ do { -+ fr = __raw_readl(BCM2708_UART_FR); -+ barrier(); -+ } while ((fr & (UART011_FR_TXFE | UART01x_FR_BUSY)) != UART011_FR_TXFE); -+} -+ -+static inline void arch_decomp_setup(void) -+{ -+ int temp, div, rem, frac; -+ -+ temp = 16 * UART_BAUD; -+ div = UART0_CLOCK / temp; -+ rem = UART0_CLOCK % temp; -+ temp = (8 * rem) / UART_BAUD; -+ frac = (temp >> 1) + (temp & 1); -+ -+ /* Make sure the UART is disabled before we start */ -+ __raw_writel(0, BCM2708_UART_CR); -+ -+ /* Set the baud rate */ -+ __raw_writel(div, BCM2708_UART_IBRD); -+ __raw_writel(frac, BCM2708_UART_FBRD); -+ -+ /* Set the UART to 8n1, FIFO enabled */ -+ __raw_writel(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, BCM2708_UART_LCRH); -+ -+ /* Enable the UART */ -+ __raw_writel(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, -+ BCM2708_UART_CR); -+} -+ -+/* -+ * nothing to do -+ */ -+#define arch_decomp_wdog() -diff --git a/arch/arm/mach-bcm2709/include/mach/vc_mem.h b/arch/arm/mach-bcm2709/include/mach/vc_mem.h -new file mode 100644 -index 0000000..4a4a338 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vc_mem.h -@@ -0,0 +1,35 @@ -+/***************************************************************************** -+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#if !defined( VC_MEM_H ) -+#define VC_MEM_H -+ -+#include -+ -+#define VC_MEM_IOC_MAGIC 'v' -+ -+#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) -+#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) -+#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int ) -+#define VC_MEM_IOC_MEM_LOAD _IOR( VC_MEM_IOC_MAGIC, 3, unsigned int ) -+ -+#if defined( __KERNEL__ ) -+#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF -+ -+extern unsigned long mm_vc_mem_phys_addr; -+extern unsigned int mm_vc_mem_size; -+extern int vc_mem_get_current_size( void ); -+#endif -+ -+#endif /* VC_MEM_H */ -diff --git a/arch/arm/mach-bcm2709/include/mach/vc_support.h b/arch/arm/mach-bcm2709/include/mach/vc_support.h -new file mode 100755 -index 0000000..70e809f ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vc_support.h -@@ -0,0 +1,69 @@ -+#ifndef _VC_SUPPORT_H_ -+#define _VC_SUPPORT_H_ -+ -+/* -+ * vc_support.h -+ * -+ * Created on: 25 Nov 2012 -+ * Author: Simon -+ */ -+ -+enum { -+/* -+ If a MEM_HANDLE_T is discardable, the memory manager may resize it to size -+ 0 at any time when it is not locked or retained. -+ */ -+ MEM_FLAG_DISCARDABLE = 1 << 0, -+ -+ /* -+ If a MEM_HANDLE_T is allocating (or normal), its block of memory will be -+ accessed in an allocating fashion through the cache. -+ */ -+ MEM_FLAG_NORMAL = 0 << 2, -+ MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, -+ -+ /* -+ If a MEM_HANDLE_T is direct, its block of memory will be accessed -+ directly, bypassing the cache. -+ */ -+ MEM_FLAG_DIRECT = 1 << 2, -+ -+ /* -+ If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a -+ non-allocating fashion through the cache. -+ */ -+ MEM_FLAG_COHERENT = 2 << 2, -+ -+ /* -+ If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by -+ the VPU in a fashion which is allocating in L2, but only coherent in L1. -+ */ -+ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), -+ -+ /* -+ If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than -+ MEM_HANDLE_INVALID on allocation and resize up. -+ */ -+ MEM_FLAG_ZERO = 1 << 4, -+ -+ /* -+ If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value -+ (either zero, or all 1's) on allocation. -+ */ -+ MEM_FLAG_NO_INIT = 1 << 5, -+ -+ /* -+ Hints. -+ */ -+ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ -+}; -+ -+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags); -+unsigned int ReleaseVcMemory(unsigned int handle); -+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle); -+unsigned int UnlockVcMemory(unsigned int handle); -+ -+unsigned int ExecuteVcCode(unsigned int code, -+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/vcio.h b/arch/arm/mach-bcm2709/include/mach/vcio.h -new file mode 100644 -index 0000000..8e11d67 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vcio.h -@@ -0,0 +1,165 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/vcio.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef _MACH_BCM2708_VCIO_H -+#define _MACH_BCM2708_VCIO_H -+ -+/* Routines to handle I/O via the VideoCore "ARM control" registers -+ * (semaphores, doorbells, mailboxes) -+ */ -+ -+#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio" -+ -+/* Constants shared with the ARM identifying separate mailbox channels */ -+#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ -+#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ -+#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ -+#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ -+#define MBOX_CHAN_COUNT 9 -+ -+enum { -+ VCMSG_PROCESS_REQUEST = 0x00000000 -+}; -+enum { -+ VCMSG_REQUEST_SUCCESSFUL = 0x80000000, -+ VCMSG_REQUEST_FAILED = 0x80000001 -+}; -+/* Mailbox property tags */ -+enum { -+ VCMSG_PROPERTY_END = 0x00000000, -+ VCMSG_GET_FIRMWARE_REVISION = 0x00000001, -+ VCMSG_GET_BOARD_MODEL = 0x00010001, -+ VCMSG_GET_BOARD_REVISION = 0x00010002, -+ VCMSG_GET_BOARD_MAC_ADDRESS = 0x00010003, -+ VCMSG_GET_BOARD_SERIAL = 0x00010004, -+ VCMSG_GET_ARM_MEMORY = 0x00010005, -+ VCMSG_GET_VC_MEMORY = 0x00010006, -+ VCMSG_GET_CLOCKS = 0x00010007, -+ VCMSG_GET_COMMAND_LINE = 0x00050001, -+ VCMSG_GET_DMA_CHANNELS = 0x00060001, -+ VCMSG_GET_POWER_STATE = 0x00020001, -+ VCMSG_GET_TIMING = 0x00020002, -+ VCMSG_SET_POWER_STATE = 0x00028001, -+ VCMSG_GET_CLOCK_STATE = 0x00030001, -+ VCMSG_SET_CLOCK_STATE = 0x00038001, -+ VCMSG_GET_CLOCK_RATE = 0x00030002, -+ VCMSG_SET_CLOCK_RATE = 0x00038002, -+ VCMSG_GET_VOLTAGE = 0x00030003, -+ VCMSG_SET_VOLTAGE = 0x00038003, -+ VCMSG_GET_MAX_CLOCK = 0x00030004, -+ VCMSG_GET_MAX_VOLTAGE = 0x00030005, -+ VCMSG_GET_TEMPERATURE = 0x00030006, -+ VCMSG_GET_MIN_CLOCK = 0x00030007, -+ VCMSG_GET_MIN_VOLTAGE = 0x00030008, -+ VCMSG_GET_TURBO = 0x00030009, -+ VCMSG_GET_MAX_TEMPERATURE = 0x0003000a, -+ VCMSG_GET_STC = 0x0003000b, -+ VCMSG_SET_TURBO = 0x00038009, -+ VCMSG_SET_ALLOCATE_MEM = 0x0003000c, -+ VCMSG_SET_LOCK_MEM = 0x0003000d, -+ VCMSG_SET_UNLOCK_MEM = 0x0003000e, -+ VCMSG_SET_RELEASE_MEM = 0x0003000f, -+ VCMSG_SET_EXECUTE_CODE = 0x00030010, -+ VCMSG_SET_EXECUTE_QPU = 0x00030011, -+ VCMSG_SET_ENABLE_QPU = 0x00030012, -+ VCMSG_GET_RESOURCE_HANDLE = 0x00030014, -+ VCMSG_GET_EDID_BLOCK = 0x00030020, -+ VCMSG_GET_CUSTOMER_OTP = 0x00030021, -+ VCMSG_SET_CUSTOMER_OTP = 0x00038021, -+ VCMSG_SET_ALLOCATE_BUFFER = 0x00040001, -+ VCMSG_SET_RELEASE_BUFFER = 0x00048001, -+ VCMSG_SET_BLANK_SCREEN = 0x00040002, -+ VCMSG_TST_BLANK_SCREEN = 0x00044002, -+ VCMSG_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, -+ VCMSG_TST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, -+ VCMSG_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, -+ VCMSG_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, -+ VCMSG_TST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, -+ VCMSG_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, -+ VCMSG_GET_DEPTH = 0x00040005, -+ VCMSG_TST_DEPTH = 0x00044005, -+ VCMSG_SET_DEPTH = 0x00048005, -+ VCMSG_GET_PIXEL_ORDER = 0x00040006, -+ VCMSG_TST_PIXEL_ORDER = 0x00044006, -+ VCMSG_SET_PIXEL_ORDER = 0x00048006, -+ VCMSG_GET_ALPHA_MODE = 0x00040007, -+ VCMSG_TST_ALPHA_MODE = 0x00044007, -+ VCMSG_SET_ALPHA_MODE = 0x00048007, -+ VCMSG_GET_PITCH = 0x00040008, -+ VCMSG_TST_PITCH = 0x00044008, -+ VCMSG_SET_PITCH = 0x00048008, -+ VCMSG_GET_VIRTUAL_OFFSET = 0x00040009, -+ VCMSG_TST_VIRTUAL_OFFSET = 0x00044009, -+ VCMSG_SET_VIRTUAL_OFFSET = 0x00048009, -+ VCMSG_GET_OVERSCAN = 0x0004000a, -+ VCMSG_TST_OVERSCAN = 0x0004400a, -+ VCMSG_SET_OVERSCAN = 0x0004800a, -+ VCMSG_GET_PALETTE = 0x0004000b, -+ VCMSG_TST_PALETTE = 0x0004400b, -+ VCMSG_SET_PALETTE = 0x0004800b, -+ VCMSG_GET_LAYER = 0x0004000c, -+ VCMSG_TST_LAYER = 0x0004400c, -+ VCMSG_SET_LAYER = 0x0004800c, -+ VCMSG_GET_TRANSFORM = 0x0004000d, -+ VCMSG_TST_TRANSFORM = 0x0004400d, -+ VCMSG_SET_TRANSFORM = 0x0004800d, -+ VCMSG_TST_VSYNC = 0x0004400e, -+ VCMSG_SET_VSYNC = 0x0004800e, -+ VCMSG_SET_CURSOR_INFO = 0x00008010, -+ VCMSG_SET_CURSOR_STATE = 0x00008011, -+}; -+ -+extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28); -+extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28); -+extern int /*rc*/ bcm_mailbox_property(void *data, int size); -+ -+#include -+ -+/* -+ * The major device number. We can't rely on dynamic -+ * registration any more, because ioctls need to know -+ * it. -+ */ -+#define MAJOR_NUM 100 -+ -+/* -+ * Set the message of the device driver -+ */ -+#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) -+/* -+ * _IOWR means that we're creating an ioctl command -+ * number for passing information from a user process -+ * to the kernel module and from the kernel module to user process -+ * -+ * The first arguments, MAJOR_NUM, is the major device -+ * number we're using. -+ * -+ * The second argument is the number of the command -+ * (there could be several with different meanings). -+ * -+ * The third argument is the type we want to get from -+ * the process to the kernel. -+ */ -+ -+/* -+ * The name of the device file -+ */ -+#define DEVICE_FILE_NAME "vcio" -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/vmalloc.h b/arch/arm/mach-bcm2709/include/mach/vmalloc.h -new file mode 100644 -index 0000000..6aa6826 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vmalloc.h -@@ -0,0 +1,20 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/vmalloc.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#define VMALLOC_END (0xff000000) -diff --git a/arch/arm/mach-bcm2709/power.c b/arch/arm/mach-bcm2709/power.c -new file mode 100644 -index 0000000..3421057 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/power.c -@@ -0,0 +1,195 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/power.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for controlling the power to -+ * VideoCore subsystems. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "bcm2708_power" -+ -+#define BCM_POWER_MAXCLIENTS 4 -+#define BCM_POWER_NOCLIENT (1<<31) -+ -+/* Some drivers expect there devices to be permanently powered */ -+#ifdef CONFIG_USB -+#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB) -+#endif -+ -+#if 1 -+#define DPRINTK printk -+#else -+#define DPRINTK if (0) printk -+#endif -+ -+struct state_struct { -+ uint32_t global_request; -+ uint32_t client_request[BCM_POWER_MAXCLIENTS]; -+ struct semaphore client_mutex; -+ struct semaphore mutex; -+} g_state; -+ -+int bcm_power_open(BCM_POWER_HANDLE_T *handle) -+{ -+ BCM_POWER_HANDLE_T i; -+ int ret = -EBUSY; -+ -+ down(&g_state.client_mutex); -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { -+ if (g_state.client_request[i] == BCM_POWER_NOCLIENT) { -+ g_state.client_request[i] = BCM_POWER_NONE; -+ *handle = i; -+ ret = 0; -+ break; -+ } -+ } -+ -+ up(&g_state.client_mutex); -+ -+ DPRINTK("bcm_power_open() -> %d\n", *handle); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(bcm_power_open); -+ -+int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request) -+{ -+ int rc = 0; -+ -+ DPRINTK("bcm_power_request(%d, %x)\n", handle, request); -+ -+ if ((handle < BCM_POWER_MAXCLIENTS) && -+ (g_state.client_request[handle] != BCM_POWER_NOCLIENT)) { -+ if (down_interruptible(&g_state.mutex) != 0) { -+ DPRINTK("bcm_power_request -> interrupted\n"); -+ return -EINTR; -+ } -+ -+ if (request != g_state.client_request[handle]) { -+ uint32_t others_request = 0; -+ uint32_t global_request; -+ BCM_POWER_HANDLE_T i; -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { -+ if (i != handle) -+ others_request |= -+ g_state.client_request[i]; -+ } -+ others_request &= ~BCM_POWER_NOCLIENT; -+ -+ global_request = request | others_request; -+ if (global_request != g_state.global_request) { -+ uint32_t actual; -+ -+ /* Send a request to VideoCore */ -+ bcm_mailbox_write(MBOX_CHAN_POWER, -+ global_request << 4); -+ -+ /* Wait for a response during power-up */ -+ if (global_request & ~g_state.global_request) { -+ rc = bcm_mailbox_read(MBOX_CHAN_POWER, -+ &actual); -+ DPRINTK -+ ("bcm_mailbox_read -> %08x, %d\n", -+ actual, rc); -+ actual >>= 4; -+ } else { -+ rc = 0; -+ actual = global_request; -+ } -+ -+ if (rc == 0) { -+ if (actual != global_request) { -+ printk(KERN_ERR -+ "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", -+ __func__, -+ g_state.global_request, -+ global_request, actual, request, others_request); -+ /* A failure */ -+ BUG_ON((others_request & actual) -+ != others_request); -+ request &= actual; -+ rc = -EIO; -+ } -+ -+ g_state.global_request = actual; -+ g_state.client_request[handle] = -+ request; -+ } -+ } -+ } -+ up(&g_state.mutex); -+ } else { -+ rc = -EINVAL; -+ } -+ DPRINTK("bcm_power_request -> %d\n", rc); -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_power_request); -+ -+int bcm_power_close(BCM_POWER_HANDLE_T handle) -+{ -+ int rc; -+ -+ DPRINTK("bcm_power_close(%d)\n", handle); -+ -+ rc = bcm_power_request(handle, BCM_POWER_NONE); -+ if (rc == 0) -+ g_state.client_request[handle] = BCM_POWER_NOCLIENT; -+ -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_power_close); -+ -+static int __init bcm_power_init(void) -+{ -+#if defined(BCM_POWER_ALWAYS_ON) -+ BCM_POWER_HANDLE_T always_on_handle; -+#endif -+ int rc = 0; -+ int i; -+ -+ printk(KERN_INFO "bcm_power: Broadcom power driver\n"); -+ bcm_mailbox_write(MBOX_CHAN_POWER, 0); -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) -+ g_state.client_request[i] = BCM_POWER_NOCLIENT; -+ -+ sema_init(&g_state.client_mutex, 1); -+ sema_init(&g_state.mutex, 1); -+ -+ g_state.global_request = 0; -+#if defined(BCM_POWER_ALWAYS_ON) -+ if (BCM_POWER_ALWAYS_ON) { -+ bcm_power_open(&always_on_handle); -+ bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON); -+ } -+#endif -+ -+ return rc; -+} -+ -+static void __exit bcm_power_exit(void) -+{ -+ bcm_mailbox_write(MBOX_CHAN_POWER, 0); -+} -+ -+arch_initcall(bcm_power_init); /* Initialize early */ -+module_exit(bcm_power_exit); -+ -+MODULE_AUTHOR("Phil Elwell"); -+MODULE_DESCRIPTION("Interface to BCM2708 power management"); -+MODULE_LICENSE("GPL"); -diff --git a/arch/arm/mach-bcm2709/vc_mem.c b/arch/arm/mach-bcm2709/vc_mem.c -new file mode 100644 -index 0000000..ac578db ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vc_mem.c -@@ -0,0 +1,431 @@ -+/***************************************************************************** -+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_ARCH_KONA -+#include -+#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) -+#else -+#include -+#endif -+ -+#include "mach/vc_mem.h" -+#include -+ -+#define DRIVER_NAME "vc-mem" -+ -+// Device (/dev) related variables -+static dev_t vc_mem_devnum = 0; -+static struct class *vc_mem_class = NULL; -+static struct cdev vc_mem_cdev; -+static int vc_mem_inited = 0; -+ -+#ifdef CONFIG_DEBUG_FS -+static struct dentry *vc_mem_debugfs_entry; -+#endif -+ -+/* -+ * Videocore memory addresses and size -+ * -+ * Drivers that wish to know the videocore memory addresses and sizes should -+ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in -+ * headers. This allows the other drivers to not be tied down to a a certain -+ * address/size at compile time. -+ * -+ * In the future, the goal is to have the videocore memory virtual address and -+ * size be calculated at boot time rather than at compile time. The decision of -+ * where the videocore memory resides and its size would be in the hands of the -+ * bootloader (and/or kernel). When that happens, the values of these variables -+ * would be calculated and assigned in the init function. -+ */ -+// in the 2835 VC in mapped above ARM, but ARM has full access to VC space -+unsigned long mm_vc_mem_phys_addr = 0x00000000; -+unsigned int mm_vc_mem_size = 0; -+unsigned int mm_vc_mem_base = 0; -+ -+EXPORT_SYMBOL(mm_vc_mem_phys_addr); -+EXPORT_SYMBOL(mm_vc_mem_size); -+EXPORT_SYMBOL(mm_vc_mem_base); -+ -+static uint phys_addr = 0; -+static uint mem_size = 0; -+static uint mem_base = 0; -+ -+ -+/**************************************************************************** -+* -+* vc_mem_open -+* -+***************************************************************************/ -+ -+static int -+vc_mem_open(struct inode *inode, struct file *file) -+{ -+ (void) inode; -+ (void) file; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_release -+* -+***************************************************************************/ -+ -+static int -+vc_mem_release(struct inode *inode, struct file *file) -+{ -+ (void) inode; -+ (void) file; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_size -+* -+***************************************************************************/ -+ -+static void -+vc_mem_get_size(void) -+{ -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_base -+* -+***************************************************************************/ -+ -+static void -+vc_mem_get_base(void) -+{ -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_current_size -+* -+***************************************************************************/ -+ -+int -+vc_mem_get_current_size(void) -+{ -+ return mm_vc_mem_size; -+} -+ -+EXPORT_SYMBOL_GPL(vc_mem_get_current_size); -+ -+/**************************************************************************** -+* -+* vc_mem_ioctl -+* -+***************************************************************************/ -+ -+static long -+vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int rc = 0; -+ -+ (void) cmd; -+ (void) arg; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ switch (cmd) { -+ case VC_MEM_IOC_MEM_PHYS_ADDR: -+ { -+ pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n", -+ __func__, (void *) mm_vc_mem_phys_addr); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr, -+ sizeof (mm_vc_mem_phys_addr)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_SIZE: -+ { -+ // Get the videocore memory size first -+ vc_mem_get_size(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%u\n", __func__, -+ mm_vc_mem_size); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_size, -+ sizeof (mm_vc_mem_size)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_BASE: -+ { -+ // Get the videocore memory base -+ vc_mem_get_base(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_BASE=%u\n", __func__, -+ mm_vc_mem_base); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_base, -+ sizeof (mm_vc_mem_base)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_LOAD: -+ { -+ // Get the videocore memory base -+ vc_mem_get_base(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%u\n", __func__, -+ mm_vc_mem_base); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_base, -+ sizeof (mm_vc_mem_base)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ default: -+ { -+ return -ENOTTY; -+ } -+ } -+ pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc); -+ -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_mmap -+* -+***************************************************************************/ -+ -+static int -+vc_mem_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ int rc = 0; -+ unsigned long length = vma->vm_end - vma->vm_start; -+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; -+ -+ pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n", -+ __func__, (long) vma->vm_start, (long) vma->vm_end, -+ (long) vma->vm_pgoff); -+ -+ if (offset + length > mm_vc_mem_size) { -+ pr_err("%s: length %ld is too big\n", __func__, length); -+ return -EINVAL; -+ } -+ // Do not cache the memory map -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ -+ rc = remap_pfn_range(vma, vma->vm_start, -+ (mm_vc_mem_phys_addr >> PAGE_SHIFT) + -+ vma->vm_pgoff, length, vma->vm_page_prot); -+ if (rc != 0) { -+ pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc); -+ } -+ -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* File Operations for the driver. -+* -+***************************************************************************/ -+ -+static const struct file_operations vc_mem_fops = { -+ .owner = THIS_MODULE, -+ .open = vc_mem_open, -+ .release = vc_mem_release, -+ .unlocked_ioctl = vc_mem_ioctl, -+ .mmap = vc_mem_mmap, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static void vc_mem_debugfs_deinit(void) -+{ -+ debugfs_remove_recursive(vc_mem_debugfs_entry); -+ vc_mem_debugfs_entry = NULL; -+} -+ -+ -+static int vc_mem_debugfs_init( -+ struct device *dev) -+{ -+ vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL); -+ if (!vc_mem_debugfs_entry) { -+ dev_warn(dev, "could not create debugfs entry\n"); -+ return -EFAULT; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_phys_addr", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_phys_addr)) { -+ dev_warn(dev, "%s:could not create vc_mem_phys entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_size", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_size)) { -+ dev_warn(dev, "%s:could not create vc_mem_size entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_base", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_base)) { -+ dev_warn(dev, "%s:could not create vc_mem_base entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ vc_mem_debugfs_deinit(); -+ return -EFAULT; -+} -+ -+#endif /* CONFIG_DEBUG_FS */ -+ -+ -+/**************************************************************************** -+* -+* vc_mem_init -+* -+***************************************************************************/ -+ -+static int __init -+vc_mem_init(void) -+{ -+ int rc = -EFAULT; -+ struct device *dev; -+ -+ pr_debug("%s: called\n", __func__); -+ -+ mm_vc_mem_phys_addr = phys_addr; -+ mm_vc_mem_size = mem_size; -+ mm_vc_mem_base = mem_base; -+ -+ vc_mem_get_size(); -+ -+ pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n", -+ mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024)); -+ -+ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) { -+ pr_err("%s: alloc_chrdev_region failed (rc=%d)\n", -+ __func__, rc); -+ goto out_err; -+ } -+ -+ cdev_init(&vc_mem_cdev, &vc_mem_fops); -+ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) { -+ pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc); -+ goto out_unregister; -+ } -+ -+ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME); -+ if (IS_ERR(vc_mem_class)) { -+ rc = PTR_ERR(vc_mem_class); -+ pr_err("%s: class_create failed (rc=%d)\n", __func__, rc); -+ goto out_cdev_del; -+ } -+ -+ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL, -+ DRIVER_NAME); -+ if (IS_ERR(dev)) { -+ rc = PTR_ERR(dev); -+ pr_err("%s: device_create failed (rc=%d)\n", __func__, rc); -+ goto out_class_destroy; -+ } -+ -+#ifdef CONFIG_DEBUG_FS -+ /* don't fail if the debug entries cannot be created */ -+ vc_mem_debugfs_init(dev); -+#endif -+ -+ vc_mem_inited = 1; -+ return 0; -+ -+ device_destroy(vc_mem_class, vc_mem_devnum); -+ -+ out_class_destroy: -+ class_destroy(vc_mem_class); -+ vc_mem_class = NULL; -+ -+ out_cdev_del: -+ cdev_del(&vc_mem_cdev); -+ -+ out_unregister: -+ unregister_chrdev_region(vc_mem_devnum, 1); -+ -+ out_err: -+ return -1; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_exit -+* -+***************************************************************************/ -+ -+static void __exit -+vc_mem_exit(void) -+{ -+ pr_debug("%s: called\n", __func__); -+ -+ if (vc_mem_inited) { -+#if CONFIG_DEBUG_FS -+ vc_mem_debugfs_deinit(); -+#endif -+ device_destroy(vc_mem_class, vc_mem_devnum); -+ class_destroy(vc_mem_class); -+ cdev_del(&vc_mem_cdev); -+ unregister_chrdev_region(vc_mem_devnum, 1); -+ } -+} -+ -+module_init(vc_mem_init); -+module_exit(vc_mem_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); -+ -+module_param(phys_addr, uint, 0644); -+module_param(mem_size, uint, 0644); -+module_param(mem_base, uint, 0644); -diff --git a/arch/arm/mach-bcm2709/vc_support.c b/arch/arm/mach-bcm2709/vc_support.c -new file mode 100755 -index 0000000..0bc41c4 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vc_support.c -@@ -0,0 +1,318 @@ -+/* -+ * vc_support.c -+ * -+ * Created on: 25 Nov 2012 -+ * Author: Simon -+ */ -+ -+#include -+#include -+ -+#ifdef ECLIPSE_IGNORE -+ -+#define __user -+#define __init -+#define __exit -+#define __iomem -+#define KERN_DEBUG -+#define KERN_ERR -+#define KERN_WARNING -+#define KERN_INFO -+#define _IOWR(a, b, c) b -+#define _IOW(a, b, c) b -+#define _IO(a, b) b -+ -+#endif -+ -+/****** VC MAILBOX FUNCTIONALITY ******/ -+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_size; -+ unsigned int m_handle; -+ }; -+ unsigned int m_alignment; -+ unsigned int m_flags; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the allocation command -+ msg.m_tag.m_tagId = 0x3000c; -+ msg.m_tag.m_sendBufferSize = 12; -+ msg.m_tag.m_sendDataSize = 12; -+ -+ //fill in our args -+ msg.m_tag.m_args.m_size = size; -+ msg.m_tag.m_args.m_alignment = alignment; -+ msg.m_tag.m_args.m_flags = flags; -+ -+ //run the command -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ { -+ *pHandle = msg.m_tag.m_args.m_handle; -+ return 0; -+ } -+ else -+ { -+ printk(KERN_ERR "failed to allocate vc memory: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -+ -+unsigned int ReleaseVcMemory(unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_error; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the release command -+ msg.m_tag.m_tagId = 0x3000f; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) -+ return 0; -+ else -+ { -+ printk(KERN_ERR "failed to release vc memory: s=%d response=%08x recv data size=%08x error=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); -+ return 1; -+ } -+} -+ -+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_busAddress; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the lock command -+ msg.m_tag.m_tagId = 0x3000d; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ { -+ //pick out the bus address -+ *pBusAddress = msg.m_tag.m_args.m_busAddress; -+ return 0; -+ } -+ else -+ { -+ printk(KERN_ERR "failed to lock vc memory: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -+ -+unsigned int UnlockVcMemory(unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_error; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the unlock command -+ msg.m_tag.m_tagId = 0x3000e; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ //check the error code too -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) -+ return 0; -+ else -+ { -+ printk(KERN_ERR "failed to unlock vc memory: s=%d response=%08x recv data size=%08x error%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); -+ return 1; -+ } -+} -+ -+unsigned int ExecuteVcCode(unsigned int code, -+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_pCode; -+ unsigned int m_return; -+ }; -+ unsigned int m_r0; -+ unsigned int m_r1; -+ unsigned int m_r2; -+ unsigned int m_r3; -+ unsigned int m_r4; -+ unsigned int m_r5; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the unlock command -+ msg.m_tag.m_tagId = 0x30010; -+ msg.m_tag.m_sendBufferSize = 28; -+ msg.m_tag.m_sendDataSize = 28; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_pCode = code; -+ msg.m_tag.m_args.m_r0 = r0; -+ msg.m_tag.m_args.m_r1 = r1; -+ msg.m_tag.m_args.m_r2 = r2; -+ msg.m_tag.m_args.m_r3 = r3; -+ msg.m_tag.m_args.m_r4 = r4; -+ msg.m_tag.m_args.m_r5 = r5; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ //check the error code too -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ return msg.m_tag.m_args.m_return; -+ else -+ { -+ printk(KERN_ERR "failed to execute: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -diff --git a/arch/arm/mach-bcm2709/vcio.c b/arch/arm/mach-bcm2709/vcio.c -new file mode 100644 -index 0000000..5e43e85 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vcio.c -@@ -0,0 +1,474 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/vcio.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for writing to the mailboxes, -+ * semaphores, doorbells etc. that are shared between the ARM and the -+ * VideoCore processor -+ */ -+ -+#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -+#define SUPPORT_SYSRQ -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+ -+#define DRIVER_NAME BCM_VCIO_DRIVER_NAME -+ -+/* ---------------------------------------------------------------------- -+ * Mailbox -+ * -------------------------------------------------------------------- */ -+ -+/* offsets from a mail box base address */ -+#define MAIL_WRT 0x00 /* write - and next 4 words */ -+#define MAIL_RD 0x00 /* read - and next 4 words */ -+#define MAIL_POL 0x10 /* read without popping the fifo */ -+#define MAIL_SND 0x14 /* sender ID (bottom two bits) */ -+#define MAIL_STA 0x18 /* status */ -+#define MAIL_CNF 0x1C /* configuration */ -+ -+#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) -+#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) -+#define MBOX_CHAN(msg) ((msg) & 0xf) -+#define MBOX_DATA28(msg) ((msg) & ~0xf) -+#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) -+ -+#define MBOX_MAGIC 0xd0d0c0de -+ -+struct vc_mailbox { -+ struct device *dev; /* parent device */ -+ void __iomem *status; -+ void __iomem *config; -+ void __iomem *read; -+ void __iomem *write; -+ uint32_t msg[MBOX_CHAN_COUNT]; -+ struct semaphore sema[MBOX_CHAN_COUNT]; -+ uint32_t magic; -+}; -+ -+static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev, -+ uint32_t addr_mbox) -+{ -+ int i; -+ -+ mbox_out->dev = dev; -+ mbox_out->status = __io_address(addr_mbox + MAIL_STA); -+ mbox_out->config = __io_address(addr_mbox + MAIL_CNF); -+ mbox_out->read = __io_address(addr_mbox + MAIL_RD); -+ /* Write to the other mailbox */ -+ mbox_out->write = -+ __io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) + -+ MAIL_WRT); -+ -+ for (i = 0; i < MBOX_CHAN_COUNT; i++) { -+ mbox_out->msg[i] = 0; -+ sema_init(&mbox_out->sema[i], 0); -+ } -+ -+ /* Enable the interrupt on data reception */ -+ writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->config); -+ -+ mbox_out->magic = MBOX_MAGIC; -+} -+ -+static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) -+{ -+ int rc; -+ -+ if (mbox->magic != MBOX_MAGIC) -+ rc = -EINVAL; -+ else { -+ /* wait for the mailbox FIFO to have some space in it */ -+ while (0 != (readl(mbox->status) & ARM_MS_FULL)) -+ cpu_relax(); -+ -+ writel(MBOX_MSG(chan, data28), mbox->write); -+ rc = 0; -+ } -+ return rc; -+} -+ -+static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) -+{ -+ int rc; -+ -+ if (mbox->magic != MBOX_MAGIC) -+ rc = -EINVAL; -+ else { -+ down(&mbox->sema[chan]); -+ *data28 = MBOX_DATA28(mbox->msg[chan]); -+ mbox->msg[chan] = 0; -+ rc = 0; -+ } -+ return rc; -+} -+ -+static irqreturn_t mbox_irq(int irq, void *dev_id) -+{ -+ /* wait for the mailbox FIFO to have some data in it */ -+ struct vc_mailbox *mbox = (struct vc_mailbox *) dev_id; -+ int status = readl(mbox->status); -+ int ret = IRQ_NONE; -+ -+ while (!(status & ARM_MS_EMPTY)) { -+ uint32_t msg = readl(mbox->read); -+ int chan = MBOX_CHAN(msg); -+ if (chan < MBOX_CHAN_COUNT) { -+ if (mbox->msg[chan]) { -+ /* Overflow */ -+ printk(KERN_ERR DRIVER_NAME -+ ": mbox chan %d overflow - drop %08x\n", -+ chan, msg); -+ } else { -+ mbox->msg[chan] = (msg | 0xf); -+ up(&mbox->sema[chan]); -+ } -+ } else { -+ printk(KERN_ERR DRIVER_NAME -+ ": invalid channel selector (msg %08x)\n", msg); -+ } -+ ret = IRQ_HANDLED; -+ status = readl(mbox->status); -+ } -+ return ret; -+} -+ -+static struct irqaction mbox_irqaction = { -+ .name = "ARM Mailbox IRQ", -+ .flags = IRQF_DISABLED | IRQF_IRQPOLL, -+ .handler = mbox_irq, -+}; -+ -+/* ---------------------------------------------------------------------- -+ * Mailbox Methods -+ * -------------------------------------------------------------------- */ -+ -+static struct device *mbox_dev; /* we assume there's only one! */ -+ -+static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) -+{ -+ int rc; -+ -+ struct vc_mailbox *mailbox = dev_get_drvdata(dev); -+ device_lock(dev); -+ rc = mbox_write(mailbox, chan, data28); -+ device_unlock(dev); -+ -+ return rc; -+} -+ -+static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) -+{ -+ int rc; -+ -+ struct vc_mailbox *mailbox = dev_get_drvdata(dev); -+ device_lock(dev); -+ rc = mbox_read(mailbox, chan, data28); -+ device_unlock(dev); -+ -+ return rc; -+} -+ -+extern int bcm_mailbox_write(unsigned chan, uint32_t data28) -+{ -+ if (mbox_dev) -+ return dev_mbox_write(mbox_dev, chan, data28); -+ else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_write); -+ -+extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) -+{ -+ if (mbox_dev) -+ return dev_mbox_read(mbox_dev, chan, data28); -+ else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_read); -+ -+static void dev_mbox_register(const char *dev_name, struct device *dev) -+{ -+ mbox_dev = dev; -+} -+ -+static int mbox_copy_from_user(void *dst, const void *src, int size) -+{ -+ if ( (uint32_t)src < TASK_SIZE) -+ { -+ return copy_from_user(dst, src, size); -+ } -+ else -+ { -+ memcpy( dst, src, size ); -+ return 0; -+ } -+} -+ -+static int mbox_copy_to_user(void *dst, const void *src, int size) -+{ -+ if ( (uint32_t)dst < TASK_SIZE) -+ { -+ return copy_to_user(dst, src, size); -+ } -+ else -+ { -+ memcpy( dst, src, size ); -+ return 0; -+ } -+} -+ -+static DEFINE_MUTEX(mailbox_lock); -+extern int bcm_mailbox_property(void *data, int size) -+{ -+ uint32_t success; -+ dma_addr_t mem_bus; /* the memory address accessed from videocore */ -+ void *mem_kern; /* the memory address accessed from driver */ -+ int s = 0; -+ -+ mutex_lock(&mailbox_lock); -+ /* allocate some memory for the messages communicating with GPU */ -+ mem_kern = dma_alloc_coherent(NULL, PAGE_ALIGN(size), &mem_bus, GFP_ATOMIC); -+ if (mem_kern) { -+ /* create the message */ -+ mbox_copy_from_user(mem_kern, data, size); -+ -+ /* send the message */ -+ wmb(); -+ s = bcm_mailbox_write(MBOX_CHAN_PROPERTY, (uint32_t)mem_bus); -+ if (s == 0) { -+ s = bcm_mailbox_read(MBOX_CHAN_PROPERTY, &success); -+ } -+ if (s == 0) { -+ /* copy the response */ -+ rmb(); -+ mbox_copy_to_user(data, mem_kern, size); -+ } -+ dma_free_coherent(NULL, PAGE_ALIGN(size), mem_kern, mem_bus); -+ } else { -+ s = -ENOMEM; -+ } -+ if (s != 0) -+ printk(KERN_ERR DRIVER_NAME ": %s failed (%d)\n", __func__, s); -+ -+ mutex_unlock(&mailbox_lock); -+ return s; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_property); -+ -+/* ---------------------------------------------------------------------- -+ * Platform Device for Mailbox -+ * -------------------------------------------------------------------- */ -+ -+/* -+ * Is the device open right now? Used to prevent -+ * concurent access into the same device -+ */ -+static int Device_Open = 0; -+ -+/* -+ * This is called whenever a process attempts to open the device file -+ */ -+static int device_open(struct inode *inode, struct file *file) -+{ -+ /* -+ * We don't want to talk to two processes at the same time -+ */ -+ if (Device_Open) -+ return -EBUSY; -+ -+ Device_Open++; -+ /* -+ * Initialize the message -+ */ -+ try_module_get(THIS_MODULE); -+ return 0; -+} -+ -+static int device_release(struct inode *inode, struct file *file) -+{ -+ /* -+ * We're now ready for our next caller -+ */ -+ Device_Open--; -+ -+ module_put(THIS_MODULE); -+ return 0; -+} -+ -+/* -+ * This function is called whenever a process tries to do an ioctl on our -+ * device file. We get two extra parameters (additional to the inode and file -+ * structures, which all device functions get): the number of the ioctl called -+ * and the parameter given to the ioctl function. -+ * -+ * If the ioctl is write or read/write (meaning output is returned to the -+ * calling process), the ioctl call returns the output of this function. -+ * -+ */ -+static long device_ioctl(struct file *file, /* see include/linux/fs.h */ -+ unsigned int ioctl_num, /* number and param for ioctl */ -+ unsigned long ioctl_param) -+{ -+ unsigned size; -+ /* -+ * Switch according to the ioctl called -+ */ -+ switch (ioctl_num) { -+ case IOCTL_MBOX_PROPERTY: -+ /* -+ * Receive a pointer to a message (in user space) and set that -+ * to be the device's message. Get the parameter given to -+ * ioctl by the process. -+ */ -+ mbox_copy_from_user(&size, (void *)ioctl_param, sizeof size); -+ return bcm_mailbox_property((void *)ioctl_param, size); -+ break; -+ default: -+ printk(KERN_ERR DRIVER_NAME "unknown ioctl: %d\n", ioctl_num); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* Module Declarations */ -+ -+/* -+ * This structure will hold the functions to be called -+ * when a process does something to the device we -+ * created. Since a pointer to this structure is kept in -+ * the devices table, it can't be local to -+ * init_module. NULL is for unimplemented functios. -+ */ -+struct file_operations fops = { -+ .unlocked_ioctl = device_ioctl, -+ .open = device_open, -+ .release = device_release, /* a.k.a. close */ -+}; -+ -+static int bcm_vcio_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ struct vc_mailbox *mailbox; -+ -+ mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); -+ if (NULL == mailbox) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "mailbox memory\n"); -+ ret = -ENOMEM; -+ } else { -+ struct resource *res; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (res == NULL) { -+ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " -+ "resource\n"); -+ ret = -ENODEV; -+ kfree(mailbox); -+ } else { -+ /* should be based on the registers from res really */ -+ mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD); -+ -+ platform_set_drvdata(pdev, mailbox); -+ dev_mbox_register(DRIVER_NAME, &pdev->dev); -+ -+ mbox_irqaction.dev_id = mailbox; -+ setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction); -+ printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n", -+ __io_address(ARM_0_MAIL0_RD)); -+ } -+ } -+ -+ if (ret == 0) { -+ /* -+ * Register the character device -+ */ -+ ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops); -+ -+ /* -+ * Negative values signify an error -+ */ -+ if (ret < 0) { -+ printk(KERN_ERR DRIVER_NAME -+ "Failed registering the character device %d\n", ret); -+ return ret; -+ } -+ } -+ return ret; -+} -+ -+static int bcm_vcio_remove(struct platform_device *pdev) -+{ -+ struct vc_mailbox *mailbox = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ kfree(mailbox); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm_mbox_driver = { -+ .probe = bcm_vcio_probe, -+ .remove = bcm_vcio_remove, -+ -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init bcm_mbox_init(void) -+{ -+ int ret; -+ -+ printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n"); -+ -+ ret = platform_driver_register(&bcm_mbox_driver); -+ if (ret != 0) { -+ printk(KERN_ERR DRIVER_NAME ": failed to register " -+ "on platform\n"); -+ } -+ -+ return ret; -+} -+ -+static void __exit bcm_mbox_exit(void) -+{ -+ platform_driver_unregister(&bcm_mbox_driver); -+} -+ -+arch_initcall(bcm_mbox_init); /* Initialize early */ -+module_exit(bcm_mbox_exit); -+ -+MODULE_AUTHOR("Gray Girling"); -+MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:bcm-mbox"); -diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S -index 8b4ee5e..80fa74b 100644 ---- a/arch/arm/mm/proc-v7.S -+++ b/arch/arm/mm/proc-v7.S -@@ -441,6 +441,7 @@ __v7_setup: - orr r0, r0, r6 @ set them - THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions - ret lr @ return to head.S:__ret -+ .space 256 - ENDPROC(__v7_setup) - - .align 2 -diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types -index c9ddd87..bfc397c 100644 ---- a/arch/arm/tools/mach-types -+++ b/arch/arm/tools/mach-types -@@ -523,6 +523,7 @@ prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103 - paz00 MACH_PAZ00 PAZ00 3128 - acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129 - bcm2708 MACH_BCM2708 BCM2708 3138 -+bcm2709 MACH_BCM2709 BCM2709 3139 - ag5evm MACH_AG5EVM AG5EVM 3189 - ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 - wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 -diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig -index 2a24bec..1c97093 100644 ---- a/drivers/char/hw_random/Kconfig -+++ b/drivers/char/hw_random/Kconfig -@@ -322,7 +322,7 @@ config HW_RANDOM_TPM - - config HW_RANDOM_BCM2708 - tristate "BCM2708 generic true random number generator support" -- depends on HW_RANDOM && ARCH_BCM2708 -+ depends on HW_RANDOM && (ARCH_BCM2708 || ARCH_BCM2709) - ---help--- - This driver provides the kernel-side support for the BCM2708 hardware. - -diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c -index 095c177..47d96dd 100644 ---- a/drivers/clocksource/arm_arch_timer.c -+++ b/drivers/clocksource/arm_arch_timer.c -@@ -803,3 +803,39 @@ static void __init arch_timer_mem_init(struct device_node *np) - } - CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", - arch_timer_mem_init); -+ -+int __init dc4_arch_timer_init(void) -+{ -+ if (arch_timers_present & ARCH_CP15_TIMER) { -+ pr_warn("arch_timer: multiple nodes in dt, skipping\n"); -+ return -1; -+ } -+ -+ arch_timers_present |= ARCH_CP15_TIMER; -+ -+ /* Try to determine the frequency from the device tree or CNTFRQ */ -+ arch_timer_rate = 19200000; -+ -+ arch_timer_ppi[PHYS_SECURE_PPI] = IRQ_ARM_LOCAL_CNTPSIRQ; -+ arch_timer_ppi[PHYS_NONSECURE_PPI] = IRQ_ARM_LOCAL_CNTPNSIRQ; -+ arch_timer_ppi[VIRT_PPI] = IRQ_ARM_LOCAL_CNTVIRQ; -+ arch_timer_ppi[HYP_PPI] = IRQ_ARM_LOCAL_CNTHPIRQ; -+ -+ /* -+ * If HYP mode is available, we know that the physical timer -+ * has been configured to be accessible from PL1. Use it, so -+ * that a guest can use the virtual timer instead. -+ * -+ * If no interrupt provided for virtual timer, we'll have to -+ * stick to the physical timer. It'd better be accessible... -+ */ -+ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) { -+ arch_timer_use_virtual = false; -+ } -+ -+ arch_timer_c3stop = 0; -+ -+ arch_timer_register(); -+ arch_timer_common_init(); -+ return 0; -+} -diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig -index 5b9e196..b3d8b8e 100644 ---- a/drivers/dma/Kconfig -+++ b/drivers/dma/Kconfig -@@ -339,7 +339,7 @@ config DMA_BCM2835 - - config DMA_BCM2708 - tristate "BCM2708 DMA engine support" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 7b7ba6f..a77b009 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -362,7 +362,7 @@ config I2C_AXXIA - - config I2C_BCM2835 - tristate "Broadcom BCM2835 I2C controller" -- depends on ARCH_BCM2835 || ARCH_BCM2708 -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 - help - If you say yes to this option, support will be included for the - BCM2835 I2C controller. -@@ -374,7 +374,7 @@ config I2C_BCM2835 - - config I2C_BCM2708 - tristate "BCM2708 BSC" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - help - Enabling this option will add BSC (Broadcom Serial Controller) - support for the BCM2708. BSC is a Broadcom proprietary bus compatible -diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig -index a8fd172..2cb1a68 100644 ---- a/drivers/media/platform/bcm2835/Kconfig -+++ b/drivers/media/platform/bcm2835/Kconfig -@@ -2,7 +2,7 @@ - - config VIDEO_BCM2835 - bool "Broadcom BCM2835 camera interface driver" -- depends on VIDEO_V4L2 && ARCH_BCM2708 -+ depends on VIDEO_V4L2 && (ARCH_BCM2708 || ARCH_BCM2709) - ---help--- - Say Y here to enable camera host interface devices for - Broadcom BCM2835 SoC. This operates over the VCHIQ interface -diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig -index 2663933..b94e6cd 100644 ---- a/drivers/misc/vc04_services/Kconfig -+++ b/drivers/misc/vc04_services/Kconfig -@@ -1,6 +1,6 @@ - config BCM2708_VCHIQ - tristate "Videocore VCHIQ" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - default y - help - Kernel to VideoCore communication interface for the -diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile -index 0c82520..8d038fe 100644 ---- a/drivers/misc/vc04_services/Makefile -+++ b/drivers/misc/vc04_services/Makefile -@@ -1,5 +1,3 @@ --ifeq ($(CONFIG_MACH_BCM2708),y) -- - obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o - - vchiq-objs := \ -@@ -14,4 +12,3 @@ vchiq-objs := \ - - ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 - --endif -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index b6077b5..e2b73a4 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -304,7 +304,7 @@ config MMC_SDHCI_ST - - config MMC_BCM2835 - tristate "MMC support on BCM2835" -- depends on MACH_BCM2708 -+ depends on (MACH_BCM2708 || MACH_BCM2709) - help - This selects the MMC Interface on BCM2835. - -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 9f202dd..c18385c 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -77,7 +77,7 @@ config SPI_ATMEL - - config SPI_BCM2835 - tristate "BCM2835 SPI controller" -- depends on ARCH_BCM2835 || ARCH_BCM2708 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST - help - This selects a driver for the Broadcom BCM2835 SPI master. - -@@ -88,7 +88,7 @@ config SPI_BCM2835 - - config SPI_BCM2708 - tristate "BCM2708 SPI controller driver (SPI0)" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - help - This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This - driver is not compatible with the "Universal SPI Master" or the SPI slave -diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig -index 3bc50f2..cd504ee 100644 ---- a/drivers/watchdog/Kconfig -+++ b/drivers/watchdog/Kconfig -@@ -454,7 +454,7 @@ config RETU_WATCHDOG - - config BCM2708_WDT - tristate "BCM2708 Watchdog" -- depends on ARCH_BCM2708 -+ depends on ARCH_BCM2708 || ARCH_BCM2709 - help - Enables BCM2708 watchdog support. - -diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig -index f7ceafd..ada7ba2 100644 ---- a/sound/arm/Kconfig -+++ b/sound/arm/Kconfig -@@ -41,7 +41,7 @@ config SND_PXA2XX_AC97 - - config SND_BCM2835 - tristate "BCM2835 ALSA driver" -- depends on ARCH_BCM2708 && BCM2708_VCHIQ && SND -+ depends on (ARCH_BCM2708 || ARCH_BCM2709) && BCM2708_VCHIQ && SND - select SND_PCM - help - Say Y or M if you want to support BCM2835 Alsa pcm card driver -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index a562ddf..40d27c1 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -10,7 +10,7 @@ config SND_BCM2835_SOC_I2S - - config SND_BCM2708_SOC_I2S - tristate "SoC Audio support for the Broadcom BCM2708 I2S module" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - select REGMAP_MMIO - select SND_SOC_DMAENGINE_PCM - select SND_SOC_GENERIC_DMAENGINE_PCM - -From 3e5e6153b4da56ee47cb1becb41f40ad61d569b2 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 6 Feb 2015 13:50:57 +0000 -Subject: [PATCH 086/172] BCM270x_DT: Add pwr_led, and the required "input" - trigger - -The "input" trigger makes the associated GPIO an input. This is to support -the Raspberry Pi PWR LED, which is driven by external hardware in normal use. - -N.B. pwr_led is not available on Model A or B boards. ---- - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 18 ++++++++- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 +++- - arch/arm/boot/dts/bcm2708.dtsi | 5 --- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 18 ++++++++- - arch/arm/boot/dts/bcm2709.dtsi | 5 --- - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - drivers/leds/trigger/Kconfig | 7 ++++ - drivers/leds/trigger/Makefile | 1 + - drivers/leds/trigger/ledtrig-input.c | 65 ++++++++++++++++++++++++++++++++ - 10 files changed, 113 insertions(+), 16 deletions(-) - create mode 100644 drivers/leds/trigger/ledtrig-input.c - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -index 95f03ba..b409c2c 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -83,8 +83,18 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 47 0>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+ -+ pwr_led: pwr { -+ label = "led1"; -+ linux,default-trigger = "input"; -+ gpios = <&gpio 35 0>; -+ }; - }; - - / { -@@ -97,5 +107,9 @@ - act_led_gpio = <&act_led>,"gpios:4"; - act_led_activelow = <&act_led>,"gpios:8"; - act_led_trigger = <&act_led>,"linux,default-trigger"; + + pwr_led_gpio = <&pwr_led>,"gpios:4"; + pwr_led_activelow = <&pwr_led>,"gpios:8"; + pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; - }; - }; -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 0631f45..1ecd1a1 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -83,8 +83,12 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 16 1>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 16 1>; + }; - }; - - / { -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index af6b1d9..aeeabae 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -79,11 +79,6 @@ - - leds: leds { - compatible = "gpio-leds"; -- -- act_led: act { -- label = "led0"; -- linux,default-trigger = "mmc0"; -- }; - }; - }; - -diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -index 0cdff31..46f4908 100644 ---- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -83,8 +83,18 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 47 0>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+ -+ pwr_led: pwr { -+ label = "led1"; -+ linux,default-trigger = "input"; -+ gpios = <&gpio 35 0>; -+ }; - }; - - / { -@@ -97,5 +107,9 @@ - act_led_gpio = <&act_led>,"gpios:4"; - act_led_activelow = <&act_led>,"gpios:8"; - act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ pwr_led_gpio = <&pwr_led>,"gpios:4"; -+ pwr_led_activelow = <&pwr_led>,"gpios:8"; -+ pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; - }; - }; ++}; diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -index fee2053..6986455 100644 +index 15c7e65..34d2226 100644 --- a/arch/arm/boot/dts/bcm2709.dtsi +++ b/arch/arm/boot/dts/bcm2709.dtsi @@ -79,11 +79,6 @@ @@ -136665,32 +125466,8 @@ index fee2053..6986455 100644 - linux,default-trigger = "mmc0"; - }; }; - }; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index 62f33d6..d4c18fa 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -962,6 +962,7 @@ CONFIG_LEDS_TRIGGER_GPIO=y - CONFIG_LEDS_TRIGGER_DEFAULT_ON=y - CONFIG_LEDS_TRIGGER_TRANSIENT=m - CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_LEDS_TRIGGER_INPUT=y - CONFIG_RTC_CLASS=y - # CONFIG_RTC_HCTOSYS is not set - CONFIG_RTC_DRV_DS1307=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index c1621fa..898ebd2 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -955,6 +955,7 @@ CONFIG_LEDS_TRIGGER_GPIO=y - CONFIG_LEDS_TRIGGER_DEFAULT_ON=y - CONFIG_LEDS_TRIGGER_TRANSIENT=m - CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_LEDS_TRIGGER_INPUT=y - CONFIG_RTC_CLASS=y - # CONFIG_RTC_HCTOSYS is not set - CONFIG_RTC_DRV_DS1307=m + arm-pmu { diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 49794b4..640756b 100644 --- a/drivers/leds/trigger/Kconfig @@ -136788,10 +125565,10 @@ index 0000000..2ca2b98 +MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\""); +MODULE_LICENSE("GPL"); -From d3db7dad4705421430c8dc3ba6853038a3988f99 Mon Sep 17 00:00:00 2001 +From 97a041da533b36ce09dc0a7d7d49017a71d2ac76 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 20 Jun 2014 17:19:27 +0100 -Subject: [PATCH 087/172] bcm2709: Simplify and strip down IRQ handler +Subject: [PATCH 072/104] bcm2709: Simplify and strip down IRQ handler --- arch/arm/include/asm/entry-macro-multi.S | 2 + @@ -137008,823 +125785,10 @@ index d08591b..101d9f1 100644 +1: get_irqnr_and_base r0, r2, r6, lr + .endm -From b54978ca2faf286b4a3836a232139140f514362f Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 24 Sep 2014 11:57:51 +0100 -Subject: [PATCH 088/172] dwc_otg: FIQ support on SMP. Set up FIQ stack and - handler on Core 0 only. - ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 96 ++++++++++++++-------------- - 1 file changed, 49 insertions(+), 47 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index 98e1dc5..4d8dd95 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -397,7 +397,55 @@ static struct fiq_handler fh = { - .name = "usb_fiq", - }; - -+static void hcd_init_fiq(void *cookie) -+{ -+ dwc_otg_device_t *otg_dev = cookie; -+ dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; -+ struct pt_regs regs; -+ -+ if (claim_fiq(&fh)) { -+ DWC_ERROR("Can't claim FIQ"); -+ BUG(); -+ } -+ DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -+ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -+ memset(®s,0,sizeof(regs)); -+ -+ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -+ if (fiq_fsm_enable) { -+ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -+ //regs.ARM_r10 = dwc_otg_hcd->dma; -+ regs.ARM_fp = (long) dwc_otg_fiq_fsm; -+ } else { -+ regs.ARM_fp = (long) dwc_otg_fiq_nop; -+ } - -+ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -+ -+// __show_regs(®s); -+ set_fiq_regs(®s); -+ -+ //Set the mphi periph to the required registers -+ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -+ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -+ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -+ //Enable mphi peripheral -+ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -+ DWC_WARN("MPHI periph has been enabled"); -+ else -+ DWC_WARN("MPHI periph has NOT been enabled"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ local_fiq_enable(); -+} - - /** - * Initializes the HCD. This function allocates memory for and initializes the -@@ -412,7 +460,6 @@ int hcd_init(dwc_bus_dev_t *_dev) - dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); - int retval = 0; - u64 dmamask; -- struct pt_regs regs; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); - -@@ -464,52 +511,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - } - - if (fiq_enable) -- { -- if (claim_fiq(&fh)) { -- DWC_ERROR("Can't claim FIQ"); -- goto error2; -- } -- -- DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -- DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -- -- set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -- memset(®s,0,sizeof(regs)); -- -- regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -- if (fiq_fsm_enable) { -- regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -- //regs.ARM_r10 = dwc_otg_hcd->dma; -- regs.ARM_fp = (long) dwc_otg_fiq_fsm; -- } else { -- regs.ARM_fp = (long) dwc_otg_fiq_nop; -- } -- -- regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -- --// __show_regs(®s); -- set_fiq_regs(®s); -- -- //Set the mphi periph to the required registers -- dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -- dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -- dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -- dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -- dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -- dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -- DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -- //Enable mphi peripheral -- writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); --#ifdef DEBUG -- if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -- DWC_WARN("MPHI periph has been enabled"); -- else -- DWC_WARN("MPHI periph has NOT been enabled"); --#endif -- // Enable FIQ interrupt from USB peripheral -- enable_fiq(INTERRUPT_VC_USB); -- local_fiq_enable(); -- } -+ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); - - - otg_dev->hcd->otg_dev = otg_dev; - -From e012a834d4a47ffe38f00d1a48da854680d51be6 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 26 Sep 2014 11:32:09 +0100 -Subject: [PATCH 089/172] dwc_otg: introduce fiq_fsm_spin(un|)lock() - -SMP safety for the FIQ relies on register read-modify write cycles being -completed in the correct order. Several places in the DWC code modify -registers also touched by the FIQ. Protect these by a bare-bones lock -mechanism. - -This also makes it possible to run the FIQ and IRQ handlers on different -cores. ---- - .../usb/host/dwc_common_port/dwc_common_linux.c | 6 --- - drivers/usb/host/dwc_otg/dwc_otg_cil.c | 10 ----- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 46 +++++++++++++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 16 +++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 23 ++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 9 ++++- - 6 files changed, 88 insertions(+), 22 deletions(-) - -diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -index 5c50a8b..b802042 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c -+++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -580,13 +580,7 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) - - void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) - { -- unsigned long flags; -- -- local_irq_save(flags); -- local_fiq_disable(); - writel((readl(reg) & ~clear_mask) | set_mask, reg); -- local_fiq_enable(); -- local_irq_restore(flags); - } - - #if 0 -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil.c b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -index 6a32c5c..e40060f 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -@@ -2244,9 +2244,7 @@ void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if) - */ - void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) - { -- uint32_t intr_enable; - hcintmsk_data_t hc_intr_mask; -- gintmsk_data_t gintmsk = {.d32 = 0 }; - hcchar_data_t hcchar; - hcsplt_data_t hcsplt; - -@@ -2348,14 +2346,6 @@ void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) - } - DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32); - -- /* Enable the top level host channel interrupt. */ -- intr_enable = (1 << hc_num); -- DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable); -- -- /* Make sure host channel interrupts are enabled. */ -- gintmsk.b.hcintr = 1; -- DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -- - /* - * Program the HCCHARn register with the endpoint characteristics for - * the current transfer. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 284f902..84618a5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -75,6 +75,46 @@ void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state - } - - /** -+ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock -+ * Must be called with local interrupts and FIQ disabled. -+ */ -+inline void fiq_fsm_spin_lock(fiq_lock_t *lock) -+{ -+ unsigned long tmp; -+ uint32_t newval; -+ fiq_lock_t lockval; -+ smp_mb__before_spinlock(); -+ /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable -+ * will be sufficient. If we are on a different CPU, then the lock protects us. */ -+ prefetchw(&lock->slock); -+ asm volatile ( -+ "1: ldrex %0, [%3]\n" -+ " add %1, %0, %4\n" -+ " strex %2, %1, [%3]\n" -+ " teq %2, #0\n" -+ " bne 1b" -+ : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) -+ : "r" (&lock->slock), "I" (1 << 16) -+ : "cc"); -+ -+ while (lockval.tickets.next != lockval.tickets.owner) { -+ wfe(); -+ lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); -+ } -+ smp_mb(); -+} -+ -+/** -+ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock -+ */ -+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) -+{ -+ smp_mb(); -+ lock->tickets.owner++; -+ dsb_sev(); -+} -+ -+/** - * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction - * @channel: channel to re-enable - */ -@@ -1142,6 +1182,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - gintsts_handled.d32 = 0; - haint_handled.d32 = 0; - -+ fiq_fsm_spin_lock(&state->lock); - gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); - gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); - gintsts.d32 &= gintmsk.d32; -@@ -1231,7 +1272,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - - } - state->fiq_done++; -- mb(); -+ fiq_fsm_spin_unlock(&state->lock); - } - - -@@ -1253,6 +1294,7 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - gintmsk_data_t gintmsk; - hfnum_data_t hfnum; - -+ fiq_fsm_spin_lock(&state->lock); - hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); - gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); - gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -@@ -1290,5 +1332,5 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - - } - state->fiq_done++; -- mb(); -+ fiq_fsm_spin_unlock(&state->lock); - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -index 5c7707f..8455324 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -@@ -120,7 +120,6 @@ typedef struct { - volatile void* intstat; - } mphi_regs_t; - -- - enum fiq_debug_level { - FIQDBG_SCHED = (1 << 0), - FIQDBG_INT = (1 << 1), -@@ -128,6 +127,16 @@ enum fiq_debug_level { - FIQDBG_PORTHUB = (1 << 3), - }; - -+typedef struct { -+ union { -+ uint32_t slock; -+ struct _tickets { -+ uint16_t owner; -+ uint16_t next; -+ } tickets; -+ }; -+} fiq_lock_t; -+ - struct fiq_state; - - extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...); -@@ -324,6 +333,7 @@ struct fiq_channel_state { - * It contains top-level state information. - */ - struct fiq_state { -+ fiq_lock_t lock; - mphi_regs_t mphi_regs; - void *dwc_regs_base; - dma_addr_t dma_base; -@@ -342,6 +352,10 @@ struct fiq_state { - struct fiq_channel_state channel[0]; - }; - -+extern void fiq_fsm_spin_lock(fiq_lock_t *lock); -+ -+extern void fiq_fsm_spin_unlock(fiq_lock_t *lock); -+ - extern int fiq_fsm_too_late(struct fiq_state *st, int n); - - extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 68d4f3b..124ac16 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1184,6 +1184,9 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - dwc_otg_qtd_t *qtd; - dwc_otg_hcd_urb_t *urb; - void* ptr = NULL; -+ uint32_t intr_enable; -+ unsigned long flags; -+ gintmsk_data_t gintmsk = { .d32 = 0, }; - - qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - -@@ -1409,6 +1412,20 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->desc_list_addr = qh->desc_list_dma; - - dwc_otg_hc_init(hcd->core_if, hc); -+ -+ local_irq_save(flags); -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ /* Enable the top level host channel interrupt. */ -+ intr_enable = (1 << hc->hc_num); -+ DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable); -+ -+ /* Make sure host channel interrupts are enabled. */ -+ gintmsk.b.hcintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ local_irq_restore(flags); - hc->qh = qh; - } - -@@ -1659,6 +1676,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); - hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); - local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); - DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -@@ -1676,6 +1694,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - mb(); - st->hcchar_copy.b.chen = 0; -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - } -@@ -1811,7 +1830,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); - - local_fiq_disable(); -- mb(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); - - if (hc->ep_type & 0x1) { - hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -@@ -1909,7 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcchar_copy.b.chen = 1; - DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); - } -- mb(); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index a5566bc..ee35196 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -101,6 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - if (dwc_otg_is_host_mode(core_if)) { - if (fiq_enable) { - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - /* Pull in from the FIQ's disabled mask */ - gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); - dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; -@@ -116,8 +117,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - } - gintsts.d32 &= gintmsk.d32; - -- if (fiq_enable) -+ if (fiq_enable) { -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); -+ } - - if (!gintsts.d32) { - goto exit_handler_routine; -@@ -200,6 +203,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - gintmsk_data_t gintmsk_new; - haintmsk_data_t haintmsk_new; - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; - if(fiq_fsm_enable) - haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; -@@ -222,6 +226,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); - /* Re-enable interrupts that the FIQ masked (first time round) */ - FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); - - if ((jiffies / HZ) > last_time) { -@@ -633,8 +638,10 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - /* check the mask? */ - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); - dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); - } - - -From 2180b4febd684a72e2ef217fdb87a26dcc4073dc Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 11:59:41 +0000 -Subject: [PATCH 090/172] fiq_fsm: fix build on bcm2708 and bcm2709 platforms - ---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 84618a5..0d2b04e 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -78,6 +78,7 @@ void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state - * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock - * Must be called with local interrupts and FIQ disabled. - */ -+#ifdef CONFIG_ARCH_BCM2709 - inline void fiq_fsm_spin_lock(fiq_lock_t *lock) - { - unsigned long tmp; -@@ -103,16 +104,23 @@ inline void fiq_fsm_spin_lock(fiq_lock_t *lock) - } - smp_mb(); - } -+#else -+inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { } -+#endif - - /** - * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock - */ -+#ifdef CONFIG_ARCH_BCM2709 - inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) - { - smp_mb(); - lock->tickets.owner++; - dsb_sev(); - } -+#else -+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { } -+#endif - - /** - * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction - -From e4d3c223f680bb7a75fc99d53603923afb28b1dd Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 17:49:23 +0000 -Subject: [PATCH 091/172] dwc_otg: put some barriers back where they should be - for UP - ---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 2 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 1 + - 2 files changed, 3 insertions(+) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 0d2b04e..276ad0c7 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -1280,6 +1280,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - - } - state->fiq_done++; -+ mb(); - fiq_fsm_spin_unlock(&state->lock); - } - -@@ -1340,5 +1341,6 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - - } - state->fiq_done++; -+ mb(); - fiq_fsm_spin_unlock(&state->lock); - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 124ac16..ac70f1d 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1928,6 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcchar_copy.b.chen = 1; - DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); - } -+ mb(); - fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - -From e9c0e5a0de4a65238b03ba6729a3d3611e0e0d8a Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 18:02:44 +0000 -Subject: [PATCH 092/172] mach_bcm2709: Add Mailbox resources to USB driver - ---- - arch/arm/mach-bcm2709/bcm2709.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 3055e72d..3cb3756 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -338,6 +338,16 @@ static struct resource bcm2708_usb_resources[] = { - .end = IRQ_USB, - .flags = IORESOURCE_IRQ, - }, -+ [4] = { -+ .start = ARM_LOCAL_BASE, -+ .end = ARM_LOCAL_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [5] = { -+ .start = IRQ_ARM_LOCAL_MAILBOX1, -+ .end = IRQ_ARM_LOCAL_MAILBOX1, -+ .flags = IORESOURCE_IRQ -+ }, - }; - - - -From ec73fdffb06f1c2751229834465ffd3152bdfd3f Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 18:45:23 +0000 -Subject: [PATCH 093/172] bcm2709/dwc_otg: Setup FIQ on core 1 if >1 core - active - ---- - arch/arm/mach-bcm2709/armctrl.c | 10 +++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 15 +++++++++++---- - 2 files changed, 20 insertions(+), 5 deletions(-) - -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -index 8e69a813..6e3f0f2ff 100644 ---- a/arch/arm/mach-bcm2709/armctrl.c -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -89,7 +89,15 @@ static void armctrl_unmask_irq(struct irq_data *d) - }; - int i; - if (d->irq >= FIQ_START) { -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ unsigned int data; -+ if (num_online_cpus() > 1) { -+ data = readl(__io_address(ARM_LOCAL_GPU_INT_ROUTING)); -+ data &= ~0xc; -+ data |= (1 << 2); -+ writel(data, __io_address(ARM_LOCAL_GPU_INT_ROUTING)); -+ } -+ /* Unmask in ARMCTRL block after routing it properly */ -+ data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; - writel(0x80 | data, __io_address(ARM_IRQ_FAST)); - } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { - #if 1 -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index 4d8dd95..1d28459 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -407,7 +407,9 @@ static void hcd_init_fiq(void *cookie) - DWC_ERROR("Can't claim FIQ"); - BUG(); - } -- DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ on core %d at 0x%08x", -+ smp_processor_id(), -+ (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); - DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); - set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); - memset(®s,0,sizeof(regs)); -@@ -510,9 +512,14 @@ int hcd_init(dwc_bus_dev_t *_dev) - goto error2; - } - -- if (fiq_enable) -- smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); -- -+ if (fiq_enable) { -+ if (num_online_cpus() > 1) { -+ /* bcm2709: can run the FIQ on a separate core to IRQs */ -+ smp_call_function_single(1, hcd_init_fiq, otg_dev, 1); -+ } else { -+ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); -+ } -+ } - - otg_dev->hcd->otg_dev = otg_dev; - hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); - -From f1c713f051a95787416095a6dbfe5846a15c9f8e Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 26 Jan 2015 17:40:22 +0000 -Subject: [PATCH 094/172] bcm2709: Port pps-gpio and i2c patches - ---- - arch/arm/mach-bcm2709/bcm2709.c | 41 +++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 39 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 3cb3756..8936427 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -95,6 +96,8 @@ static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; -+static int pps_gpio_pin = -1; -+static bool vc_i2c_override = false; - - static unsigned use_dt = 0; - -@@ -283,6 +286,19 @@ static struct platform_device w1_device = { - }; - #endif - -+static struct pps_gpio_platform_data pps_gpio_info = { -+ .assert_falling_edge = false, -+ .capture_clear = false, -+ .gpio_pin = -1, -+ .gpio_label = "PPS", -+}; -+ -+static struct platform_device pps_gpio_device = { -+ .name = "pps-gpio", -+ .id = PLATFORM_DEVID_NONE, -+ .dev.platform_data = &pps_gpio_info, -+}; -+ - static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_fb_device = { -@@ -862,6 +878,16 @@ void __init bcm2709_init(void) - #ifdef CONFIG_BCM2708_GPIO - bcm_register_device_dt(&bcm2708_gpio_device); - #endif -+ -+#if defined(CONFIG_PPS_CLIENT_GPIO) || defined(CONFIG_PPS_CLIENT_GPIO_MODULE) -+ if (!use_dt && (pps_gpio_pin >= 0)) { -+ pr_info("bcm2709: GPIO %d setup as pps-gpio device\n", pps_gpio_pin); -+ pps_gpio_info.gpio_pin = pps_gpio_pin; -+ pps_gpio_device.id = pps_gpio_pin; -+ bcm_register_device(&pps_gpio_device); -+ } -+#endif -+ - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; - w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -@@ -886,8 +912,15 @@ void __init bcm2709_init(void) - bcm_register_device(&bcm2835_thermal_device); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device_dt(&bcm2708_bsc0_device); -- bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+ if (vc_i2c_override) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ } else { -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } - - #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - bcm_register_device_dt(&bcm2708_i2s_device); -@@ -1242,3 +1275,7 @@ module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); - module_param(w1_gpio_pin, uint, 0644); - module_param(w1_gpio_pullup, uint, 0644); -+module_param(pps_gpio_pin, int, 0644); -+MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); -+module_param(vc_i2c_override, bool, 0644); -+MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); - -From 30a40bd6d577e4b4b4b13c3bb249a99aa8216def Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 28 Jan 2015 17:57:23 +0000 -Subject: [PATCH 095/172] bcm2709: Also accept the 2708 machine ID - ---- - arch/arm/mach-bcm2709/bcm2709.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 8936427..55c21a5 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -1267,6 +1267,19 @@ MACHINE_START(BCM2709, "BCM2709") - .dt_compat = bcm2709_compat, - MACHINE_END - -+MACHINE_START(BCM2708, "BCM2709") -+ /* Maintainer: Broadcom Europe Ltd. */ -+ .smp = smp_ops(bcm2709_smp_ops), -+ .map_io = bcm2709_map_io, -+ .init_irq = bcm2709_init_irq, -+ .init_time = bcm2709_timer_init, -+ .init_machine = bcm2709_init, -+ .init_early = bcm2709_init_early, -+ .reserve = board_reserve, -+ .restart = bcm2709_restart, -+ .dt_compat = bcm2709_compat, -+MACHINE_END -+ - module_param(boardrev, uint, 0644); - module_param(serial, uint, 0644); - module_param(uart_clock, uint, 0644); - -From 24097ac1917183b1b10b2315a1a3db527fcf8dfb Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 2 Feb 2015 12:45:07 +0000 -Subject: [PATCH 096/172] BCM2709_DT: Fix bad merge - ---- - arch/arm/boot/dts/Makefile | 25 +++++++++++++------------ - 1 file changed, 13 insertions(+), 12 deletions(-) - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 83dceff..f3b6364 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -63,18 +63,19 @@ ifeq ($(CONFIG_BCM2709_DT),y) - endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += ds1307-rtc-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb -+dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb -+dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ - -From f3e38d36525f6f1f59dacddd9595ad860ae2eacd Mon Sep 17 00:00:00 2001 +From f28cbc4c87034ca6cec586aab72550ed2ec873b4 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Tue, 3 Feb 2015 07:15:19 +0100 -Subject: [PATCH 097/172] HiFiBerry Amp: fix device-tree problems +Subject: [PATCH 073/104] HiFiBerry Amp: fix device-tree problems Some code to load the driver based on device-tree-overlays was missing. This is added by this patch. --- @@ -137832,7 +125796,7 @@ Some code to load the driver based on device-tree-overlays was missing. This is 1 file changed, 21 insertions(+) diff --git a/sound/soc/bcm/hifiberry_amp.c b/sound/soc/bcm/hifiberry_amp.c -index 1e87ee0..5903915 100644 +index 1e87ee06..5903915 100644 --- a/sound/soc/bcm/hifiberry_amp.c +++ b/sound/soc/bcm/hifiberry_amp.c @@ -65,6 +65,12 @@ static struct snd_soc_card snd_rpi_hifiberry_amp = { @@ -137878,10 +125842,10 @@ index 1e87ee0..5903915 100644 .probe = snd_rpi_hifiberry_amp_probe, .remove = snd_rpi_hifiberry_amp_remove, -From 93addb736b05475b6ed41035b4dab1eee9bf35e4 Mon Sep 17 00:00:00 2001 +From a1cdf290a0f57e8bd06f9dcd231e0e75900eb91f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 3 Feb 2015 11:41:38 +0000 -Subject: [PATCH 098/172] BCM270x_DT: Add i2c0_baudrate and i2c1_baudrate +Subject: [PATCH 074/104] BCM270x_DT: Add i2c0_baudrate and i2c1_baudrate parameters --- @@ -137930,10 +125894,10 @@ index 46f4908..75c222f 100644 act_led_gpio = <&act_led>,"gpios:4"; act_led_activelow = <&act_led>,"gpios:8"; -From 83bef71ee725251abe65c91dd6ae76547acbc5ca Mon Sep 17 00:00:00 2001 +From 8d1370ff81ebc22ee80a61d66991ee2e4a5fdc48 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 4 Feb 2015 10:02:24 +0000 -Subject: [PATCH 099/172] pinctrl-bcm2835: bcm2835_gpio_direction_output must +Subject: [PATCH 075/104] pinctrl-bcm2835: bcm2835_gpio_direction_output must set the value --- @@ -137961,10 +125925,10 @@ index 9d1149e..d9c727b 100644 static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -From 3e2353455d485d72a0bfbfefdfd7ce3dea783b93 Mon Sep 17 00:00:00 2001 +From 680333e666e7b63d41168b0b1a8ff22a4cf6095e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 4 Feb 2015 12:59:36 +0000 -Subject: [PATCH 100/172] w1-gpio: Sort out the pullup/parasitic power tangle +Subject: [PATCH 076/104] w1-gpio: Sort out the pullup/parasitic power tangle --- arch/arm/boot/dts/w1-gpio-overlay.dts | 4 +++- @@ -138137,143 +126101,10 @@ index d58594a..feae942 100644 unsigned int ext_pullup_enable_pin; unsigned int pullup_duration; -From ca08a2747acc5e2033abdbae1696d00769754fda Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 4 Feb 2015 12:16:50 +0000 -Subject: [PATCH 101/172] dwc_otg: fixup read-modify-write in critical paths - -Be more careful about read-modify-write on registers that the FIQ -also touches. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 13 +++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 30 +++++++++++++++++++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 22 ++++++++++++++++---- - 3 files changed, 55 insertions(+), 10 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index ac70f1d..1782d65 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -2447,9 +2447,16 @@ void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - */ - gintmsk_data_t gintmsk = {.d32 = 0 }; - gintmsk.b.nptxfempty = 1; -- DWC_MODIFY_REG32(&hcd->core_if-> -- core_global_regs->gintmsk, gintmsk.d32, -- 0); -+ -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); -+ } - } - } - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index ee35196..6266661 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -165,7 +165,15 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - - gintmsk_data_t gintmsk = { .b.portintr = 1}; - retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); -- DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ } - } - if (gintsts.b.hcintr) { - retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); -@@ -1069,7 +1077,15 @@ static void halt_channel(dwc_otg_hcd_t * hcd, - * be processed. - */ - gintmsk.b.nptxfempty = 1; -- DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ } - } else { - /* - * Move the QH from the periodic queued schedule to -@@ -1086,7 +1102,15 @@ static void halt_channel(dwc_otg_hcd_t * hcd, - * processed. - */ - gintmsk.b.ptxfempty = 1; -- DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ } - } - } - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 17d3030..acd0dd7 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -683,8 +683,15 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { - intr_mask.b.sofintr = 1; -- DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -- intr_mask.d32, intr_mask.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); -+ } - } - hcd->periodic_qh_count++; - } -@@ -745,8 +752,15 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->periodic_qh_count--; - if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { - intr_mask.b.sofintr = 1; -- DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -- intr_mask.d32, 0); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); -+ } - } - } - } - -From 14a8bf59835972411f4b73c37aae457ab46b6ee1 Mon Sep 17 00:00:00 2001 +From de7749cbb3ef82fec9860fcf156ebc3c86cd5e55 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 5 Feb 2015 16:01:44 +0000 -Subject: [PATCH 102/172] i2c_bcm2708: Fix clock reference counting +Subject: [PATCH 077/104] i2c_bcm2708: Fix clock reference counting --- drivers/i2c/busses/i2c-bcm2708.c | 12 ++++++++++-- @@ -138321,10 +126152,10 @@ index 526129b..fda59ba 100644 kfree(bi); -From 2ec0ca5c31b9dc7498362727d40361b5d542552a Mon Sep 17 00:00:00 2001 +From b2603b5c8719225f6d2cfef99f150158336c472a Mon Sep 17 00:00:00 2001 From: Byron Bradley Date: Fri, 6 Feb 2015 14:19:41 +0000 -Subject: [PATCH 103/172] Add device-tree overlay for pcf2127 +Subject: [PATCH 078/104] Add device-tree overlay for pcf2127 Signed-off-by: Byron Bradley --- @@ -138334,10 +126165,10 @@ Signed-off-by: Byron Bradley create mode 100644 arch/arm/boot/dts/pcf2127-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index f3b6364..2031ffe 100644 +index d45424c..ca55980 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -72,6 +72,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb +@@ -20,6 +20,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb @@ -138374,10 +126205,10 @@ index 0000000..01fc81d + }; +}; -From dc183cc424cc15195d4f5f6d559ba14da39e4c9f Mon Sep 17 00:00:00 2001 +From 9b0008cdb418c0c75914d6306995f06a6a72564f Mon Sep 17 00:00:00 2001 From: android Date: Mon, 25 Aug 2014 13:18:21 +0100 -Subject: [PATCH 104/172] BCM2708_VCIO : Add automatic creation of device node +Subject: [PATCH 079/104] BCM2708_VCIO : Add automatic creation of device node --- arch/arm/mach-bcm2708/vcio.c | 12 +++++++++++- @@ -138459,10 +126290,10 @@ index 5e43e85..700bff4 100644 } -From 7ae975fb0e9e826f87a895915a958b9a65c0f073 Mon Sep 17 00:00:00 2001 +From 0b6043e147ac773bcb495704a6f4e393981aca38 Mon Sep 17 00:00:00 2001 From: jeanleflambeur Date: Sun, 1 Feb 2015 12:35:38 +0100 -Subject: [PATCH 105/172] Fix grabbing lock from atomic context in i2c driver +Subject: [PATCH 080/104] Fix grabbing lock from atomic context in i2c driver 2 main changes: - check for timeouts in the bcm2708_bsc_setup function as indicated by this comment: @@ -138681,10 +126512,10 @@ index fda59ba..81e9374 100644 dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", pdev->id, (unsigned long)regs->start, irq, baudrate); -From 777022879290ae0dcd44f327a73088369957f2b8 Mon Sep 17 00:00:00 2001 +From 278b258303b0b5772e3b2847d304644072bae177 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 12 Feb 2015 11:17:53 +0000 -Subject: [PATCH 106/172] Fix LED "input" trigger implementation for 3.19 +Subject: [PATCH 081/104] Fix LED "input" trigger implementation for 3.19 --- drivers/leds/leds-gpio.c | 10 +++++++++- @@ -138693,7 +126524,7 @@ Subject: [PATCH 106/172] Fix LED "input" trigger implementation for 3.19 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c -index 7ea1ea42..cf70ea1 100644 +index d26af0a..9e49c72 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -41,6 +41,13 @@ static void gpio_led_work(struct work_struct *work) @@ -138757,410 +126588,24 @@ index 2ca2b98..07d1219 100644 static struct led_trigger input_led_trigger = { diff --git a/include/linux/leds.h b/include/linux/leds.h -index cfceef3..4c8dc19 100644 +index f70f84f..f3fcaa1 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h -@@ -46,6 +46,9 @@ struct led_classdev { - #define LED_SYSFS_DISABLE (1 << 20) - #define SET_BRIGHTNESS_ASYNC (1 << 21) +@@ -48,6 +48,9 @@ struct led_classdev { #define SET_BRIGHTNESS_SYNC (1 << 22) + #define LED_DEV_CAP_FLASH (1 << 23) + #define LED_DEV_CAP_SYNC_STROBE (1 << 24) + /* Additions for Raspberry Pi PWR LED */ -+#define SET_GPIO_INPUT (1 << 23) -+#define SET_GPIO_OUTPUT (1 << 24) ++#define SET_GPIO_INPUT (1 << 30) ++#define SET_GPIO_OUTPUT (1 << 31) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ -From 8d8b2a22f6227d1c55b05b7e4247c57fcbb3bd88 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 12 Feb 2015 11:20:00 +0000 -Subject: [PATCH 107/172] fiq_fsm: Falling out of the state machine isn't fatal - -This edge case can be hit if the port is disabled while the FIQ is -in the middle of a transaction. Make the effects less severe. - -Also get rid of the useless return value. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 20 +++++++------------- - 1 file changed, 7 insertions(+), 13 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 6266661..8a31562 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -2357,7 +2357,7 @@ int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qt - * different from the normal (messy) path. This function and its friends handles - * channel cleanup and transaction completion from a FIQ transaction. - */ --int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) -+void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - { - struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; - dwc_hc_t *hc = hcd->hc_ptr_array[num]; -@@ -2366,7 +2366,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; - hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; - int hostchannels = 0; -- int ret = 0; - fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); - - hostchannels = hcd->available_host_channels; -@@ -2380,7 +2379,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - * CLEAR_TT_BUFFER hub command if we were in the start-split phase. - */ - release_channel(hcd, hc, NULL, hc->halt_status); -- ret = 1; - break; - - case FIQ_NP_SPLIT_DONE: -@@ -2393,7 +2391,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - } else if (hcint.b.nak) { - handle_hc_nak_intr(hcd, hc, hc_regs, qtd); - } -- ret = 1; - break; - - case FIQ_NP_SPLIT_HS_ABORTED: -@@ -2604,15 +2601,12 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - break; - - default: -- local_fiq_disable(); -- DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm); -- BUG(); -- } -- //if (hostchannels != hcd->available_host_channels) { -- /* should have incremented by now! */ -- // BUG(); --// } -- return ret; -+ DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x", -+ hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num); -+ qtd->error_count++; -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ return; - } - - /** Handles interrupt for a specific Host Channel */ - -From 2455e8024a76f5de920dfa3d7e3177830fcacb7a Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 15 Feb 2015 12:06:16 +0000 -Subject: [PATCH 108/172] platform: Remove bcmrpi_small_defconfig - ---- - arch/arm/configs/bcmrpi_small_defconfig | 103 -------------------------------- - 1 file changed, 103 deletions(-) - delete mode 100644 arch/arm/configs/bcmrpi_small_defconfig - -diff --git a/arch/arm/configs/bcmrpi_small_defconfig b/arch/arm/configs/bcmrpi_small_defconfig -deleted file mode 100644 -index 3f6e378..0000000 ---- a/arch/arm/configs/bcmrpi_small_defconfig -+++ /dev/null -@@ -1,103 +0,0 @@ --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2836=y --# CONFIG_BCM2708_GPIO is not set --# CONFIG_BCM2708_VCMEM is not set --# CONFIG_CACHE_L2X0 is not set --CONFIG_SMP=y --CONFIG_HAVE_ARM_ARCH_TIMER=y --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_IDLE=y --# CONFIG_SUSPEND is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --# CONFIG_BCM2708_VCHIQ is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_RAW_DRIVER=y --# CONFIG_HID is not set --CONFIG_DMADEVICES=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NLS=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_KERNEL=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_DEBUG_LL=y --CONFIG_EARLY_PRINTK=y --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC16=y --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y - -From 82c208c7034e6a112808b16ee6fd1b910a50494c Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 15 Feb 2015 12:09:47 +0000 -Subject: [PATCH 109/172] platform: Remove bcmrpi_sdcard_defconfig - ---- - arch/arm/configs/bcmrpi_sdcard_defconfig | 176 ------------------------------- - 1 file changed, 176 deletions(-) - delete mode 100644 arch/arm/configs/bcmrpi_sdcard_defconfig - -diff --git a/arch/arm/configs/bcmrpi_sdcard_defconfig b/arch/arm/configs/bcmrpi_sdcard_defconfig -deleted file mode 100644 -index fef887a..0000000 ---- a/arch/arm/configs/bcmrpi_sdcard_defconfig -+++ /dev/null -@@ -1,176 +0,0 @@ --# CONFIG_ARM_PATCH_PHYS_VIRT is not set --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_POSIX_MQUEUE=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_EMBEDDED=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2708=y --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_UACCESS_WITH_MEMCPY=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_FREQ=y --CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y --CONFIG_CPU_FREQ_GOV_PERFORMANCE=y --CONFIG_CPU_FREQ_GOV_USERSPACE=y --CONFIG_CPU_FREQ_GOV_ONDEMAND=y --CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y --CONFIG_VFP=y --CONFIG_BINFMT_MISC=y --CONFIG_NET=y --CONFIG_PACKET=y --CONFIG_UNIX=y --CONFIG_INET=y --CONFIG_IP_MULTICAST=y --CONFIG_IP_PNP=y --CONFIG_IP_PNP_DHCP=y --CONFIG_IP_PNP_RARP=y --CONFIG_SYN_COOKIES=y --# CONFIG_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set --# CONFIG_WIRELESS is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --CONFIG_SCSI=y --# CONFIG_SCSI_PROC_FS is not set --# CONFIG_SCSI_LOWLEVEL is not set --CONFIG_NETDEVICES=y --CONFIG_MII=y --# CONFIG_NET_VENDOR_BROADCOM is not set --# CONFIG_NET_VENDOR_CIRRUS is not set --# CONFIG_NET_VENDOR_FARADAY is not set --# CONFIG_NET_VENDOR_INTEL is not set --# CONFIG_NET_VENDOR_MARVELL is not set --# CONFIG_NET_VENDOR_MICREL is not set --# CONFIG_NET_VENDOR_NATSEMI is not set --# CONFIG_NET_VENDOR_SEEQ is not set --# CONFIG_NET_VENDOR_STMICRO is not set --# CONFIG_NET_VENDOR_WIZNET is not set --CONFIG_PHYLIB=y --# CONFIG_WLAN is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --CONFIG_VT_HW_CONSOLE_BINDING=y --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_TTY_PRINTK=y --CONFIG_RAW_DRIVER=y --CONFIG_WATCHDOG=y --CONFIG_REGULATOR=y --CONFIG_REGULATOR_DEBUG=y --CONFIG_REGULATOR_FIXED_VOLTAGE=y --CONFIG_REGULATOR_VIRTUAL_CONSUMER=y --CONFIG_REGULATOR_USERSPACE_CONSUMER=y --CONFIG_FB=y --CONFIG_FRAMEBUFFER_CONSOLE=y --CONFIG_LOGO=y --# CONFIG_LOGO_LINUX_MONO is not set --# CONFIG_LOGO_LINUX_VGA16 is not set --CONFIG_SOUND=y --CONFIG_SND=y --CONFIG_MMC=y --CONFIG_MMC_SDHCI=y --CONFIG_MMC_SDHCI_PLTFM=y --CONFIG_MMC_SDHCI_BCM2708=y --CONFIG_MMC_SDHCI_BCM2708_DMA=y --CONFIG_NEW_LEDS=y --CONFIG_LEDS_CLASS=y --CONFIG_LEDS_TRIGGERS=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_EXT4_FS=y --CONFIG_EXT4_FS_POSIX_ACL=y --CONFIG_EXT4_FS_SECURITY=y --CONFIG_AUTOFS4_FS=y --CONFIG_FSCACHE=y --CONFIG_CACHEFILES=y --CONFIG_MSDOS_FS=y --CONFIG_VFAT_FS=y --CONFIG_FAT_DEFAULT_IOCHARSET="ascii" --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --CONFIG_CONFIGFS_FS=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NFS_FS=y --CONFIG_NFS_V3_ACL=y --CONFIG_NFS_V4=y --CONFIG_ROOT_NFS=y --CONFIG_NFS_FSCACHE=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_FS=y --CONFIG_DETECT_HUNG_TASK=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_DEBUG_BUGVERBOSE is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y - -From cf9e2491332c7b58f25d489baa2040ab529e7223 Mon Sep 17 00:00:00 2001 +From 7612654a1bf2d6ed7005e69b07c9dd43f68449b6 Mon Sep 17 00:00:00 2001 From: Rainer Herbers Date: Sun, 15 Feb 2015 13:44:14 +0100 -Subject: [PATCH 110/172] Create bmp085_i2c-sensor-overlay.dts and update +Subject: [PATCH 082/104] Create bmp085_i2c-sensor-overlay.dts and update Makefile --- @@ -139170,13 +126615,13 @@ Subject: [PATCH 110/172] Create bmp085_i2c-sensor-overlay.dts and update create mode 100644 arch/arm/boot/dts/bmp085_i2c-sensor-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 2031ffe..754291e 100644 +index ca55980..6839ece 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -64,6 +64,7 @@ endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -12,6 +12,7 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif + +dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb @@ -139211,838 +126656,10 @@ index 0000000..b830bf2 + }; +}; -From 62d72625e0c8f80a16aca64437885bea2d8e5bee Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 16 Feb 2015 13:51:02 +0000 -Subject: [PATCH 111/172] lirc-rpi: use getnstimeofday instead of - read_current_timer - -read_current_timer isn't guaranteed to return values in -microseconds, and indeed it doesn't on a Pi2. - -Issue: linux#827 ---- - drivers/staging/media/lirc/lirc_rpi.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -index cd66ca2..24563ec 100644 ---- a/drivers/staging/media/lirc/lirc_rpi.c -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -107,6 +108,13 @@ static void safe_udelay(unsigned long usecs) - udelay(usecs); - } - -+static unsigned long read_current_us(void) -+{ -+ struct timespec now; -+ getnstimeofday(&now); -+ return (now.tv_sec * 1000000) + (now.tv_nsec/1000); -+} -+ - static int init_timing_params(unsigned int new_duty_cycle, - unsigned int new_freq) - { -@@ -135,7 +143,7 @@ static long send_pulse_softcarrier(unsigned long length) - length *= 1000; - - actual = 0; target = 0; flag = 0; -- read_current_timer(&actual_us); -+ actual_us = read_current_us(); - - while (actual < length) { - if (flag) { -@@ -153,7 +161,7 @@ static long send_pulse_softcarrier(unsigned long length) - */ - if ((int)(target_us - actual_us) > 0) - udelay(target_us - actual_us); -- read_current_timer(&actual_us); -+ actual_us = read_current_us(); - actual += (actual_us - initial_us) * 1000; - flag = !flag; - } - -From d38f6c099572f92660d4a7125b97159b89ac5970 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 18 Feb 2015 10:40:28 +0000 -Subject: [PATCH 112/172] vc_cma: Make the vc_cma area the default contiguous - DMA area - ---- - drivers/char/broadcom/vc_cma/vc_cma.c | 98 ++++++++++++++++++++++++++--------- - 1 file changed, 73 insertions(+), 25 deletions(-) - -diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c -index a635f9f..bd64317 100644 ---- a/drivers/char/broadcom/vc_cma/vc_cma.c -+++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -58,6 +58,8 @@ - #define LOG_DBG(fmt, ...) \ - if (vc_cma_debug) \ - printk(KERN_INFO fmt "\n", ##__VA_ARGS__) -+#define LOG_INFO(fmt, ...) \ -+ printk(KERN_INFO fmt "\n", ##__VA_ARGS__) - #define LOG_ERR(fmt, ...) \ - printk(KERN_ERR fmt "\n", ##__VA_ARGS__) - -@@ -120,6 +122,10 @@ unsigned int vc_cma_chunks; - unsigned int vc_cma_chunks_used; - unsigned int vc_cma_chunks_reserved; - -+ -+void *vc_cma_dma_alloc; -+unsigned int vc_cma_dma_size; -+ - static int in_loud_error; - - unsigned int vc_cma_reserve_total; -@@ -198,8 +204,17 @@ void vc_cma_reserve(void) - * size from the end of memory - */ - if (vc_cma_size) { -- if (dma_declare_contiguous(NULL /*&vc_cma_device.dev*/, vc_cma_size, -+ if (dma_declare_contiguous(&vc_cma_device.dev, vc_cma_size, - vc_cma_base, 0) == 0) { -+ if (!dev_get_cma_area(NULL)) { -+ /* There is no default CMA area - make this -+ the default */ -+ struct cma *vc_cma_area = dev_get_cma_area( -+ &vc_cma_device.dev); -+ dma_contiguous_set_default(vc_cma_area); -+ LOG_INFO("vc_cma_reserve - using vc_cma as " -+ "the default contiguous DMA area"); -+ } - } else { - LOG_ERR("vc_cma: dma_declare_contiguous(%x,%x) failed", - vc_cma_size, (unsigned int)vc_cma_base); -@@ -309,6 +324,9 @@ static int vc_cma_show_info(struct seq_file *m, void *v) - seq_printf(m, " PID %5d: %d bytes\n", user->pid, - user->reserve); - } -+ seq_printf(m, " dma_alloc : %p (%d pages)\n", -+ vc_cma_dma_alloc ? page_address(vc_cma_dma_alloc) : 0, -+ vc_cma_dma_size); - - seq_printf(m, "\n"); - -@@ -348,31 +366,33 @@ static int vc_cma_proc_write(struct file *file, - #define FREE_STR "free" - #define DEBUG_STR "debug" - #define RESERVE_STR "reserve" -+#define DMA_ALLOC_STR "dma_alloc" -+#define DMA_FREE_STR "dma_free" - if (strncmp(input_str, ALLOC_STR, strlen(ALLOC_STR)) == 0) { -- int size; -+ int alloc_size; - char *p = input_str + strlen(ALLOC_STR); - - while (*p == ' ') - p++; -- size = memparse(p, NULL); -- LOG_ERR("/proc/vc-cma: alloc %d", size); -- if (size) -+ alloc_size = memparse(p, NULL); -+ LOG_INFO("/proc/vc-cma: alloc %d", alloc_size); -+ if (alloc_size) - send_vc_msg(VC_CMA_MSG_REQUEST_FREE, -- size / VC_CMA_CHUNK_SIZE, 0); -+ alloc_size / VC_CMA_CHUNK_SIZE, 0); - else - LOG_ERR("invalid size '%s'", p); - rc = size; - } else if (strncmp(input_str, FREE_STR, strlen(FREE_STR)) == 0) { -- int size; -+ int alloc_size; - char *p = input_str + strlen(FREE_STR); - - while (*p == ' ') - p++; -- size = memparse(p, NULL); -- LOG_ERR("/proc/vc-cma: free %d", size); -- if (size) -+ alloc_size = memparse(p, NULL); -+ LOG_INFO("/proc/vc-cma: free %d", alloc_size); -+ if (alloc_size) - send_vc_msg(VC_CMA_MSG_REQUEST_ALLOC, -- size / VC_CMA_CHUNK_SIZE, 0); -+ alloc_size / VC_CMA_CHUNK_SIZE, 0); - else - LOG_ERR("invalid size '%s'", p); - rc = size; -@@ -384,18 +404,46 @@ static int vc_cma_proc_write(struct file *file, - vc_cma_debug = 1; - else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) - vc_cma_debug = 0; -- LOG_ERR("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); -+ LOG_INFO("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); - rc = size; - } else if (strncmp(input_str, RESERVE_STR, strlen(RESERVE_STR)) == 0) { -- int size; -+ int alloc_size; - int reserved; - char *p = input_str + strlen(RESERVE_STR); - while (*p == ' ') - p++; -- size = memparse(p, NULL); -+ alloc_size = memparse(p, NULL); - -- reserved = vc_cma_set_reserve(size, current->tgid); -+ reserved = vc_cma_set_reserve(alloc_size, current->tgid); - rc = (reserved >= 0) ? size : reserved; -+ } else if (strncmp(input_str, DMA_ALLOC_STR, strlen(DMA_ALLOC_STR)) == 0) { -+ int alloc_size; -+ char *p = input_str + strlen(DMA_ALLOC_STR); -+ while (*p == ' ') -+ p++; -+ alloc_size = memparse(p, NULL); -+ -+ if (vc_cma_dma_alloc) { -+ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, -+ vc_cma_dma_size); -+ vc_cma_dma_alloc = NULL; -+ vc_cma_dma_size = 0; -+ } -+ vc_cma_dma_alloc = dma_alloc_from_contiguous(NULL, alloc_size, 0); -+ vc_cma_dma_size = (vc_cma_dma_alloc ? alloc_size : 0); -+ if (vc_cma_dma_alloc) -+ LOG_INFO("dma_alloc(%d pages) -> %p", alloc_size, page_address(vc_cma_dma_alloc)); -+ else -+ LOG_ERR("dma_alloc(%d pages) failed", alloc_size); -+ rc = size; -+ } else if (strncmp(input_str, DMA_FREE_STR, strlen(DMA_FREE_STR)) == 0) { -+ if (vc_cma_dma_alloc) { -+ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, -+ vc_cma_dma_size); -+ vc_cma_dma_alloc = NULL; -+ vc_cma_dma_size = 0; -+ } -+ rc = size; - } - - out: -@@ -526,7 +574,7 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - uint8_t *chunk_addr; - size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; - -- chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, - PAGES_PER_CHUNK, - VC_CMA_CHUNK_ORDER); - if (!chunk) -@@ -548,7 +596,7 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - "bad SPARSEMEM configuration?", - __func__, (unsigned int)page_to_phys(chunk), - vc_cma_base, vc_cma_base + vc_cma_size - 1); -- LOG_ERR("%s: dev->cma_area = %p\n", __func__, -+ LOG_ERR("%s: dev->cma_area = %p", __func__, - (void*)0/*vc_cma_device.dev.cma_area*/); - LOG_ERR("%s: ===============================", - __func__); -@@ -672,7 +720,7 @@ static int cma_worker_proc(void *param) - } - - if (!dma_release_from_contiguous -- (NULL /*&vc_cma_device.dev*/, page, -+ (&vc_cma_device.dev, page, - PAGES_PER_CHUNK)) { - LOG_ERR - ("CMA_MSG_FREE - failed to " -@@ -1023,12 +1071,12 @@ static int vc_cma_init(void) - if (!check_cma_config()) - goto out_release; - -- printk(KERN_INFO "vc-cma: Videocore CMA driver\n"); -- printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); -- printk(KERN_INFO "vc-cma: vc_cma_size = 0x%08x (%u MiB)\n", -- vc_cma_size, vc_cma_size / (1024 * 1024)); -- printk(KERN_INFO "vc-cma: vc_cma_initial = 0x%08x (%u MiB)\n", -- vc_cma_initial, vc_cma_initial / (1024 * 1024)); -+ LOG_INFO("vc-cma: Videocore CMA driver"); -+ LOG_INFO("vc-cma: vc_cma_base = 0x%08x", vc_cma_base); -+ LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", -+ vc_cma_size, vc_cma_size / (1024 * 1024)); -+ LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", -+ vc_cma_initial, vc_cma_initial / (1024 * 1024)); - - vc_cma_base_page = phys_to_page(vc_cma_base); - -@@ -1038,7 +1086,7 @@ static int vc_cma_init(void) - for (vc_cma_chunks_used = 0; - vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { - struct page *chunk; -- chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, - PAGES_PER_CHUNK, - VC_CMA_CHUNK_ORDER); - if (!chunk) - -From 35c8e88b85da5994d81aad77928546f82784d5cc Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Feb 2015 22:00:00 +0000 -Subject: [PATCH 113/172] config: extra options - ---- - arch/arm/configs/bcm2709_defconfig | 23 +++- - arch/arm/configs/bcm2835_sdcard_defconfig | 176 ------------------------------ - arch/arm/configs/bcmrpi_defconfig | 22 +++- - 3 files changed, 33 insertions(+), 188 deletions(-) - delete mode 100644 arch/arm/configs/bcm2835_sdcard_defconfig - -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index d4c18fa..bc557a5 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -5,7 +5,6 @@ CONFIG_LOCALVERSION="-v7" - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y - CONFIG_FHANDLE=y --CONFIG_AUDIT=y - CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y -@@ -14,12 +13,11 @@ CONFIG_TASKSTATS=y - CONFIG_TASK_DELAY_ACCT=y - CONFIG_TASK_XACCT=y - CONFIG_TASK_IO_ACCOUNTING=y --CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG=m - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y - CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y -@@ -55,7 +53,6 @@ CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_KEXEC=y - CONFIG_CPU_FREQ=y - CONFIG_CPU_FREQ_STAT=m - CONFIG_CPU_FREQ_STAT_DETAILS=y -@@ -64,11 +61,11 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y - CONFIG_CPU_FREQ_GOV_USERSPACE=y - CONFIG_CPU_FREQ_GOV_ONDEMAND=y - CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_NEON=y - CONFIG_KERNEL_MODE_NEON=y - CONFIG_BINFMT_MISC=m -+# CONFIG_SUSPEND is not set - CONFIG_NET=y - CONFIG_PACKET=y - CONFIG_UNIX=y -@@ -126,7 +123,6 @@ CONFIG_NF_CONNTRACK_SIP=m - CONFIG_NF_CONNTRACK_TFTP=m - CONFIG_NF_CT_NETLINK=m - CONFIG_NETFILTER_XT_SET=m --CONFIG_NETFILTER_XT_TARGET_AUDIT=m - CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m - CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m - CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -@@ -282,6 +278,7 @@ CONFIG_BRIDGE=m - CONFIG_VLAN_8021Q=m - CONFIG_VLAN_8021Q_GVRP=y - CONFIG_ATALK=m -+CONFIG_6LOWPAN=m - CONFIG_NET_SCHED=y - CONFIG_NET_SCH_CBQ=m - CONFIG_NET_SCH_HTB=m -@@ -345,6 +342,9 @@ CONFIG_BPQETHER=m - CONFIG_BAYCOM_SER_FDX=m - CONFIG_BAYCOM_SER_HDX=m - CONFIG_YAM=m -+CONFIG_CAN=m -+CONFIG_CAN_VCAN=m -+CONFIG_CAN_MCP251X=m - CONFIG_IRDA=m - CONFIG_IRLAN=m - CONFIG_IRNET=m -@@ -360,12 +360,14 @@ CONFIG_USB_IRDA=m - CONFIG_SIGMATEL_FIR=m - CONFIG_MCS_FIR=m - CONFIG_BT=m -+CONFIG_BT_6LOWPAN=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 -+CONFIG_BT_6LOWPAN=m - CONFIG_BT_HCIBTUSB=m - CONFIG_BT_HCIBCM203X=m - CONFIG_BT_HCIBPA10X=m -@@ -394,6 +396,7 @@ CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_ATA_OVER_ETH=m - CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set -@@ -527,6 +530,9 @@ CONFIG_JOYSTICK_XPAD=m - CONFIG_JOYSTICK_XPAD_FF=y - CONFIG_INPUT_TOUCHSCREEN=y - CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m -+CONFIG_TOUCHSCREEN_STMPE=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -560,6 +566,7 @@ CONFIG_I2C_CHARDEV=m - CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m -@@ -567,6 +574,7 @@ CONFIG_PPS_CLIENT_LDISC=m - CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y - CONFIG_GPIO_ARIZONA=m -+CONFIG_GPIO_STMPE=y - CONFIG_W1=m - CONFIG_W1_MASTER_DS2490=m - CONFIG_W1_MASTER_DS2482=m -@@ -592,6 +600,8 @@ CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y - CONFIG_BCM2708_WDT=m - CONFIG_UCB1400_CORE=m -+CONFIG_MFD_STMPE=y -+CONFIG_STMPE_SPI=y - CONFIG_MFD_ARIZONA_I2C=m - CONFIG_MFD_ARIZONA_SPI=m - CONFIG_MFD_WM5102=y -@@ -763,6 +773,7 @@ CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set -diff --git a/arch/arm/configs/bcm2835_sdcard_defconfig b/arch/arm/configs/bcm2835_sdcard_defconfig -deleted file mode 100644 -index 987471b..0000000 ---- a/arch/arm/configs/bcm2835_sdcard_defconfig -+++ /dev/null -@@ -1,176 +0,0 @@ --# CONFIG_ARM_PATCH_PHYS_VIRT is not set --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_POSIX_MQUEUE=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_EMBEDDED=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2708=y --# CONFIG_BCM2708_GPIO is not set --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_UACCESS_WITH_MEMCPY=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_FREQ=y --CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y --CONFIG_CPU_FREQ_GOV_PERFORMANCE=y --CONFIG_CPU_FREQ_GOV_USERSPACE=y --CONFIG_CPU_FREQ_GOV_ONDEMAND=y --CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y --CONFIG_VFP=y --CONFIG_BINFMT_MISC=y --CONFIG_NET=y --CONFIG_PACKET=y --CONFIG_UNIX=y --CONFIG_INET=y --CONFIG_IP_MULTICAST=y --CONFIG_IP_PNP=y --CONFIG_IP_PNP_DHCP=y --CONFIG_IP_PNP_RARP=y --CONFIG_SYN_COOKIES=y --# CONFIG_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set --# CONFIG_WIRELESS is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --CONFIG_SCSI=y --# CONFIG_SCSI_PROC_FS is not set --# CONFIG_SCSI_LOWLEVEL is not set --CONFIG_NETDEVICES=y --CONFIG_MII=y --# CONFIG_NET_VENDOR_BROADCOM is not set --# CONFIG_NET_VENDOR_CIRRUS is not set --# CONFIG_NET_VENDOR_FARADAY is not set --# CONFIG_NET_VENDOR_INTEL is not set --# CONFIG_NET_VENDOR_MARVELL is not set --# CONFIG_NET_VENDOR_MICREL is not set --# CONFIG_NET_VENDOR_NATSEMI is not set --# CONFIG_NET_VENDOR_SEEQ is not set --# CONFIG_NET_VENDOR_STMICRO is not set --# CONFIG_NET_VENDOR_WIZNET is not set --CONFIG_PHYLIB=y --# CONFIG_WLAN is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --CONFIG_VT_HW_CONSOLE_BINDING=y --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_TTY_PRINTK=y --CONFIG_RAW_DRIVER=y --CONFIG_WATCHDOG=y --CONFIG_REGULATOR=y --CONFIG_REGULATOR_DEBUG=y --CONFIG_REGULATOR_FIXED_VOLTAGE=y --CONFIG_REGULATOR_VIRTUAL_CONSUMER=y --CONFIG_REGULATOR_USERSPACE_CONSUMER=y --CONFIG_FB=y --CONFIG_FRAMEBUFFER_CONSOLE=y --CONFIG_LOGO=y --# CONFIG_LOGO_LINUX_MONO is not set --# CONFIG_LOGO_LINUX_VGA16 is not set --CONFIG_SOUND=y --CONFIG_SND=y --CONFIG_MMC=y --CONFIG_MMC_SDHCI=y --CONFIG_MMC_SDHCI_PLTFM=y --CONFIG_MMC_SDHCI_BCM2708=y --CONFIG_MMC_SDHCI_BCM2708_DMA=y --CONFIG_NEW_LEDS=y --CONFIG_LEDS_CLASS=y --CONFIG_LEDS_TRIGGERS=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_EXT4_FS=y --CONFIG_EXT4_FS_POSIX_ACL=y --CONFIG_EXT4_FS_SECURITY=y --CONFIG_AUTOFS4_FS=y --CONFIG_FSCACHE=y --CONFIG_CACHEFILES=y --CONFIG_MSDOS_FS=y --CONFIG_VFAT_FS=y --CONFIG_FAT_DEFAULT_IOCHARSET="ascii" --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --CONFIG_CONFIGFS_FS=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NFS_FS=y --CONFIG_NFS_V3_ACL=y --CONFIG_NFS_V4=y --CONFIG_ROOT_NFS=y --CONFIG_NFS_FSCACHE=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_FS=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_DEBUG_BUGVERBOSE is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 898ebd2..4d703f4 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -4,7 +4,6 @@ CONFIG_PHYS_OFFSET=0 - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y - CONFIG_FHANDLE=y --CONFIG_AUDIT=y - CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y -@@ -13,12 +12,11 @@ CONFIG_TASKSTATS=y - CONFIG_TASK_DELAY_ACCT=y - CONFIG_TASK_XACCT=y - CONFIG_TASK_IO_ACCOUNTING=y --CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG=m - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y - CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y -@@ -50,7 +48,6 @@ CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_KEXEC=y - CONFIG_CPU_FREQ=y - CONFIG_CPU_FREQ_STAT=m - CONFIG_CPU_FREQ_STAT_DETAILS=y -@@ -59,9 +56,9 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y - CONFIG_CPU_FREQ_GOV_USERSPACE=y - CONFIG_CPU_FREQ_GOV_ONDEMAND=y - CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_BINFMT_MISC=m -+# CONFIG_SUSPEND is not set - CONFIG_NET=y - CONFIG_PACKET=y - CONFIG_UNIX=y -@@ -119,7 +116,6 @@ CONFIG_NF_CONNTRACK_SIP=m - CONFIG_NF_CONNTRACK_TFTP=m - CONFIG_NF_CT_NETLINK=m - CONFIG_NETFILTER_XT_SET=m --CONFIG_NETFILTER_XT_TARGET_AUDIT=m - CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m - CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m - CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -@@ -275,6 +271,7 @@ CONFIG_BRIDGE=m - CONFIG_VLAN_8021Q=m - CONFIG_VLAN_8021Q_GVRP=y - CONFIG_ATALK=m -+CONFIG_6LOWPAN=m - CONFIG_NET_SCHED=y - CONFIG_NET_SCH_CBQ=m - CONFIG_NET_SCH_HTB=m -@@ -338,6 +335,9 @@ CONFIG_BPQETHER=m - CONFIG_BAYCOM_SER_FDX=m - CONFIG_BAYCOM_SER_HDX=m - CONFIG_YAM=m -+CONFIG_CAN=m -+CONFIG_CAN_VCAN=m -+CONFIG_CAN_MCP251X=m - CONFIG_IRDA=m - CONFIG_IRLAN=m - CONFIG_IRNET=m -@@ -353,6 +353,7 @@ CONFIG_USB_IRDA=m - CONFIG_SIGMATEL_FIR=m - CONFIG_MCS_FIR=m - CONFIG_BT=m -+CONFIG_BT_6LOWPAN=m - CONFIG_BT_RFCOMM=m - CONFIG_BT_RFCOMM_TTY=y - CONFIG_BT_BNEP=m -@@ -387,6 +388,7 @@ CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_ATA_OVER_ETH=m - CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set -@@ -520,6 +522,9 @@ CONFIG_JOYSTICK_XPAD=m - CONFIG_JOYSTICK_XPAD_FF=y - CONFIG_INPUT_TOUCHSCREEN=y - CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m -+CONFIG_TOUCHSCREEN_STMPE=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -553,6 +558,7 @@ CONFIG_I2C_CHARDEV=m - CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m -@@ -560,6 +566,7 @@ CONFIG_PPS_CLIENT_LDISC=m - CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y - CONFIG_GPIO_ARIZONA=m -+CONFIG_GPIO_STMPE=y - CONFIG_W1=m - CONFIG_W1_MASTER_DS2490=m - CONFIG_W1_MASTER_DS2482=m -@@ -585,6 +592,8 @@ CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y - CONFIG_BCM2708_WDT=m - CONFIG_UCB1400_CORE=m -+CONFIG_MFD_STMPE=y -+CONFIG_STMPE_SPI=y - CONFIG_MFD_ARIZONA_I2C=m - CONFIG_MFD_ARIZONA_SPI=m - CONFIG_MFD_WM5102=y -@@ -756,6 +765,7 @@ CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set - -From b08151a4eda84af4ce2dbe0b85dab4895964b692 Mon Sep 17 00:00:00 2001 -From: "Yann E. MORIN" -Date: Wed, 18 Feb 2015 12:46:07 +0100 -Subject: [PATCH 114/172] char/vc_cma: fix build with LPAE enabled -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -LPAE is required for virtualisation on ARM. However, enabling LPAE -results in an unbuildable configuration, due to improper formats -used in vc_cma: - - CC drivers/char/broadcom/vc_cma/vc_cma.o - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘vc_cma_alloc_chunks’: - drivers/char/broadcom/vc_cma/vc_cma.c:547:4: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 4 has type ‘phys_addr_t’ [-Werror=format=] - LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " - ^ - drivers/char/broadcom/vc_cma/vc_cma.c:547:4: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 5 has type ‘phys_addr_t’ [-Werror=format=] - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘cma_worker_proc’: - drivers/char/broadcom/vc_cma/vc_cma.c:677:7: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 3 has type ‘long long unsigned int’ [-Werror=format=] - LOG_ERR - ^ - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘vc_cma_init’: - drivers/char/broadcom/vc_cma/vc_cma.c:1027:2: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 2 has type ‘phys_addr_t’ [-Werror=format=] - printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); - ^ - cc1: all warnings being treated as errors - make[6]: *** [drivers/char/broadcom/vc_cma/vc_cma.o] Error 1 - -As explained in Documentation/printk-formats.txt, printing a physical -address phys_addr_t should be done using the %pa[p] format, which -expects a "reference" to the value. - -Even if virtualisation is currently not possible (due to the bootloader -starting the kernel in SVC, not HYP, mode), fix the printk formats -nonetheless, it will anyway give better-looking code! ;-) - -Signed-off-by: "Yann E. MORIN" ---- - drivers/char/broadcom/vc_cma/vc_cma.c | 12 +++++++----- - 1 file changed, 7 insertions(+), 5 deletions(-) - -diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c -index bd64317..07b31a7 100644 ---- a/drivers/char/broadcom/vc_cma/vc_cma.c -+++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -590,12 +590,13 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - BUG_ON(((page_to_phys(chunk) - vc_cma_base) % - VC_CMA_CHUNK_SIZE) != 0); - if (chunk_num >= vc_cma_chunks) { -+ phys_addr_t _pa = vc_cma_base + vc_cma_size - 1; - LOG_ERR("%s: ===============================", - __func__); -- LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " -+ LOG_ERR("%s: chunk phys %x, vc_cma %pa-%pa - " - "bad SPARSEMEM configuration?", - __func__, (unsigned int)page_to_phys(chunk), -- vc_cma_base, vc_cma_base + vc_cma_size - 1); -+ &vc_cma_base, &_pa); - LOG_ERR("%s: dev->cma_area = %p", __func__, - (void*)0/*vc_cma_device.dev.cma_area*/); - LOG_ERR("%s: ===============================", -@@ -722,11 +723,12 @@ static int cma_worker_proc(void *param) - if (!dma_release_from_contiguous - (&vc_cma_device.dev, page, - PAGES_PER_CHUNK)) { -+ phys_addr_t _pa = page_to_phys(page); - LOG_ERR - ("CMA_MSG_FREE - failed to " -- "release chunk %d (phys %x, " -+ "release chunk %d (phys %pa, " - "page %x)", chunk_num, -- page_to_phys(page), -+ &_pa, - (unsigned int)page); - } - vc_cma_chunks_used--; -@@ -1072,7 +1074,7 @@ static int vc_cma_init(void) - goto out_release; - - LOG_INFO("vc-cma: Videocore CMA driver"); -- LOG_INFO("vc-cma: vc_cma_base = 0x%08x", vc_cma_base); -+ LOG_INFO("vc-cma: vc_cma_base = %pa", &vc_cma_base); - LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", - vc_cma_size, vc_cma_size / (1024 * 1024)); - LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", - -From 60a017bba035a8623994425e9a4e72bf73988358 Mon Sep 17 00:00:00 2001 +From a0be7922a0ebe90a4e23fd86b4fe5797e7ae88fc Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 24 Feb 2015 13:40:50 +0000 -Subject: [PATCH 115/172] pinctrl-bcm2835: Fix interrupt handling for GPIOs +Subject: [PATCH 083/104] pinctrl-bcm2835: Fix interrupt handling for GPIOs 28-31 and 46-53 Contrary to the documentation, the BCM2835 GPIO controller actually has @@ -140059,11 +126676,11 @@ This bug has only just been observed because GPIOs above 27 can only be accessed on an old Raspberry Pi with the optional P5 header fitted, where the pins are often used for I2S instead. --- - drivers/pinctrl/pinctrl-bcm2835.c | 50 +++++++++++++++++++++++++++++---------- - 1 file changed, 38 insertions(+), 12 deletions(-) + drivers/pinctrl/pinctrl-bcm2835.c | 51 ++++++++++++++++++++++++++++++--------- + 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index d9c727b..01a8966 100644 +index d9c727b..ae68710 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -47,6 +47,7 @@ @@ -140179,96 +126796,7 @@ index d9c727b..01a8966 100644 len = strlen(dev_name(pc->dev)) + 16; name = devm_kzalloc(pc->dev, len, GFP_KERNEL); - -From 4ac9769f94555bf911430ae6bb60147c86eef83c Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 25 Feb 2015 17:14:57 +0000 -Subject: [PATCH 116/172] perf: Add support for performance counter interrupt - and enable in DT - ---- - arch/arm/boot/dts/bcm2708.dtsi | 4 ++++ - arch/arm/boot/dts/bcm2709.dtsi | 5 +++++ - arch/arm/mach-bcm2709/armctrl.c | 6 +++++- - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index aeeabae..0713a1ed 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -80,6 +80,10 @@ - leds: leds { - compatible = "gpio-leds"; - }; -+ -+ arm-pmu { -+ compatible = "arm,arm1176-pmu"; -+ }; - }; - - clocks { -diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -index 6986455..34d2226 100644 ---- a/arch/arm/boot/dts/bcm2709.dtsi -+++ b/arch/arm/boot/dts/bcm2709.dtsi -@@ -80,6 +80,11 @@ - leds: leds { - compatible = "gpio-leds"; - }; -+ -+ arm-pmu { -+ compatible = "arm,cortex-a7-pmu"; -+ interrupts = <3 9>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -index 6e3f0f2ff..fc6cb8b 100644 ---- a/arch/arm/mach-bcm2709/armctrl.c -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -76,6 +76,8 @@ static void armctrl_mask_irq(struct irq_data *d) - } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { - unsigned int data = (unsigned int)irq_get_chip_data(d->irq); - writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { -+ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_CLR)); - } else { printk("%s: %d\n", __func__, d->irq); BUG(); } - } - -@@ -119,6 +121,8 @@ static void armctrl_unmask_irq(struct irq_data *d) - } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { - unsigned int data = (unsigned int)irq_get_chip_data(d->irq); - writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { -+ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_SET)); - } else { printk("%s: %d\n", __func__, d->irq); BUG(); } - } - -@@ -145,7 +149,7 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, - if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) - return -EINVAL; - -- if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5)) -+ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5 && intspec[1] != 9)) - return -EINVAL; - - if (intspec[0] == 0) - -From e9793e25e0c44d44d780efcda5b338185d158b41 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Thu, 26 Feb 2015 11:42:34 +0000 -Subject: [PATCH 117/172] pinctrl: squash - ---- - drivers/pinctrl/pinctrl-bcm2835.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index 01a8966..ae68710 100644 ---- a/drivers/pinctrl/pinctrl-bcm2835.c -+++ b/drivers/pinctrl/pinctrl-bcm2835.c -@@ -1095,6 +1095,7 @@ static struct platform_driver bcm2835_pinctrl_driver = { +@@ -1069,6 +1095,7 @@ static struct platform_driver bcm2835_pinctrl_driver = { .remove = bcm2835_pinctrl_remove, .driver = { .name = MODULE_NAME, @@ -140277,10 +126805,10 @@ index 01a8966..ae68710 100644 }, }; -From 30ed96c07f97f78a923fa5ef0611ace11e521d1c Mon Sep 17 00:00:00 2001 +From ee761823de93128c8a25e31ef84f7214516c326d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 26 Feb 2015 09:58:22 +0000 -Subject: [PATCH 118/172] pinctrl-bcm2835: Only request the interrupts listed +Subject: [PATCH 084/104] pinctrl-bcm2835: Only request the interrupts listed in the DTB Although the GPIO controller can generate three interrupts (four counting @@ -140307,10 +126835,10 @@ index ae68710..87b7a4a 100644 pc->irq_data[i].irqgroup = i; -From ac3374c94c497e1e55a7b734f3bf238a5587430a Mon Sep 17 00:00:00 2001 +From 03a991f4473ffb93d3ada0d93787ba85a033f2e7 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 27 Feb 2015 15:51:37 +0000 -Subject: [PATCH 119/172] serial/amba-pl011: Activate TX IRQ passively +Subject: [PATCH 085/104] serial/amba-pl011: Activate TX IRQ passively The current PL011 driver transmits a dummy character when the UART is opened, to assert the TX IRQ for the first time @@ -140654,32 +127182,223 @@ index 16ca535..c243729 100644 /* Ensure interrupts from this UART are masked and cleared */ -From 2820ce043829968ca725e149e5a008e3eb86b570 Mon Sep 17 00:00:00 2001 +From b67d48cca0c9b8f1b1cc07ff325f228a1ab51620 Mon Sep 17 00:00:00 2001 From: Dave Martin -Date: Fri, 27 Feb 2015 15:52:31 +0000 -Subject: [PATCH 120/172] serial/amba-pl011: Leave the TX IRQ alone when the - UART is not open +Date: Wed, 1 Apr 2015 14:08:31 +0100 +Subject: [PATCH 086/104] serial/amba-pl011: Refactor and simplify TX FIFO + handling -Getting the TX IRQ re-asserted from scratch can be inefficient in -some setups. +Commit 734745c serial/amba-pl011: Activate TX IRQ passively +adds some complexity and overhead in the form of a softirq +mechanism for transmitting in the absence of interrupts. -This patch avoids clearing the TX IRQ across pl011_shutdown()... -pl011_startup(), so that if the port is closed and reopened, the -IRQ will still work afterwards without having to bootstrap it again. +This patch simplifies the code flow to reduce the reliance on +subtle behaviour and avoid fragility under future maintenance. -The TX IRQ continues to be masked in IMSC when the UART is not in -use. +To this end, the TX softirq mechanism is removed and instead +pl011_start_tx() will now simply stuff the FIFO until full +(guaranteeing future TX IRQs), or until there are no more chars +to write (in which case we don't care whether an IRQ happens). Signed-off-by: Dave Martin +Signed-off-by: Jakub Kicinski --- - drivers/tty/serial/amba-pl011.c | 5 +---- - 1 file changed, 1 insertion(+), 4 deletions(-) + drivers/tty/serial/amba-pl011.c | 119 +++++++++------------------------------- + 1 file changed, 26 insertions(+), 93 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c -index c243729..9c6a2f4 100644 +index c243729..e32f4d7 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c -@@ -1676,9 +1676,6 @@ static int pl011_startup(struct uart_port *port) +@@ -58,7 +58,6 @@ + #include + #include + #include +-#include + + #define UART_NR 14 + +@@ -157,9 +156,7 @@ struct uart_amba_port { + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ + unsigned int old_cr; /* state during shutdown */ +- struct delayed_work tx_softirq_work; + bool autorts; +- unsigned int tx_irq_seen; /* 0=none, 1=1, 2=2 or more */ + char type[12]; + #ifdef CONFIG_DMA_ENGINE + /* DMA stuff */ +@@ -1209,15 +1206,14 @@ static void pl011_stop_tx(struct uart_port *port) + pl011_dma_tx_stop(uap); + } + +-static bool pl011_tx_chars(struct uart_amba_port *uap); ++static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); + + /* Start TX with programmed I/O only (no DMA) */ + static void pl011_start_tx_pio(struct uart_amba_port *uap) + { + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); +- if (!uap->tx_irq_seen) +- pl011_tx_chars(uap); ++ pl011_tx_chars(uap, false); + } + + static void pl011_start_tx(struct uart_port *port) +@@ -1284,87 +1280,54 @@ __acquires(&uap->port.lock) + spin_lock(&uap->port.lock); + } + +-/* +- * Transmit a character +- * There must be at least one free entry in the TX FIFO to accept the char. +- * +- * Returns true if the FIFO might have space in it afterwards; +- * returns false if the FIFO definitely became full. +- */ +-static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) ++static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, ++ bool from_irq) + { ++ if (unlikely(!from_irq) && ++ readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) ++ return false; /* unable to transmit character */ ++ + writew(c, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + +- if (likely(uap->tx_irq_seen > 1)) +- return true; +- +- return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF); ++ return true; + } + +-static bool pl011_tx_chars(struct uart_amba_port *uap) ++static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) + { + struct circ_buf *xmit = &uap->port.state->xmit; +- int count; +- +- if (unlikely(uap->tx_irq_seen < 2)) +- /* +- * Initial FIFO fill level unknown: we must check TXFF +- * after each write, so just try to fill up the FIFO. +- */ +- count = uap->fifosize; +- else /* tx_irq_seen >= 2 */ +- /* +- * FIFO initially at least half-empty, so we can simply +- * write half the FIFO without polling TXFF. +- +- * Note: the *first* TX IRQ can still race with +- * pl011_start_tx_pio(), which can result in the FIFO +- * being fuller than expected in that case. +- */ +- count = uap->fifosize >> 1; +- +- /* +- * If the FIFO is full we're guaranteed a TX IRQ at some later point, +- * and can't transmit immediately in any case: +- */ +- if (unlikely(uap->tx_irq_seen < 2 && +- readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)) +- return false; ++ int count = uap->fifosize >> 1; + + if (uap->port.x_char) { +- pl011_tx_char(uap, uap->port.x_char); ++ if (!pl011_tx_char(uap, uap->port.x_char, from_irq)) ++ return; + uap->port.x_char = 0; + --count; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + pl011_stop_tx(&uap->port); +- goto done; ++ return; + } + + /* If we are using DMA mode, try to send some characters. */ + if (pl011_dma_tx_irq(uap)) +- goto done; ++ return; + +- while (count-- > 0 && pl011_tx_char(uap, xmit->buf[xmit->tail])) { +- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +- if (uart_circ_empty(xmit)) ++ do { ++ if (likely(from_irq) && count-- == 0) + break; +- } ++ ++ if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) ++ break; ++ ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ } while (!uart_circ_empty(xmit)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + +- if (uart_circ_empty(xmit)) { ++ if (uart_circ_empty(xmit)) + pl011_stop_tx(&uap->port); +- goto done; +- } +- +- if (unlikely(!uap->tx_irq_seen)) +- schedule_delayed_work(&uap->tx_softirq_work, uap->port.timeout); +- +-done: +- return false; + } + + static void pl011_modem_status(struct uart_amba_port *uap) +@@ -1391,28 +1354,6 @@ static void pl011_modem_status(struct uart_amba_port *uap) + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); + } + +-static void pl011_tx_softirq(struct work_struct *work) +-{ +- struct delayed_work *dwork = to_delayed_work(work); +- struct uart_amba_port *uap = +- container_of(dwork, struct uart_amba_port, tx_softirq_work); +- +- spin_lock(&uap->port.lock); +- while (pl011_tx_chars(uap)) ; +- spin_unlock(&uap->port.lock); +-} +- +-static void pl011_tx_irq_seen(struct uart_amba_port *uap) +-{ +- if (likely(uap->tx_irq_seen > 1)) +- return; +- +- uap->tx_irq_seen++; +- if (uap->tx_irq_seen < 2) +- /* first TX IRQ */ +- cancel_delayed_work(&uap->tx_softirq_work); +-} +- + static irqreturn_t pl011_int(int irq, void *dev_id) + { + struct uart_amba_port *uap = dev_id; +@@ -1451,10 +1392,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) + if (status & (UART011_DSRMIS|UART011_DCDMIS| + UART011_CTSMIS|UART011_RIMIS)) + pl011_modem_status(uap); +- if (status & UART011_TXIS) { +- pl011_tx_irq_seen(uap); +- pl011_tx_chars(uap); +- } ++ if (status & UART011_TXIS) ++ pl011_tx_chars(uap, true); + + if (pass_counter-- == 0) + break; +@@ -1676,9 +1615,6 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); @@ -140689,12485 +127408,28 @@ index c243729..9c6a2f4 100644 spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ -@@ -1742,7 +1739,7 @@ static void pl011_shutdown(struct uart_port *port) - spin_lock_irq(&uap->port.lock); - uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); -- writew(0xffff, uap->port.membase + UART011_ICR); -+ writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR); - spin_unlock_irq(&uap->port.lock); - - pl011_dma_shutdown(uap); - -From 8def067fc67ecc6a7023cb22af2d7e39e7af3a3d Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:09 +0100 -Subject: [PATCH 121/172] staging: fbtft: core support - -This commit adds the core fbtft framework from -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/Kconfig | 2 + - drivers/staging/Makefile | 1 + - drivers/staging/fbtft/Kconfig | 9 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/README | 32 + - drivers/staging/fbtft/fbtft-bus.c | 256 ++++++ - drivers/staging/fbtft/fbtft-core.c | 1516 +++++++++++++++++++++++++++++++++++ - drivers/staging/fbtft/fbtft-io.c | 409 ++++++++++ - drivers/staging/fbtft/fbtft-sysfs.c | 222 +++++ - drivers/staging/fbtft/fbtft.h | 447 +++++++++++ - 10 files changed, 2897 insertions(+) - create mode 100644 drivers/staging/fbtft/Kconfig - create mode 100644 drivers/staging/fbtft/Makefile - create mode 100644 drivers/staging/fbtft/README - create mode 100644 drivers/staging/fbtft/fbtft-bus.c - create mode 100644 drivers/staging/fbtft/fbtft-core.c - create mode 100644 drivers/staging/fbtft/fbtft-io.c - create mode 100644 drivers/staging/fbtft/fbtft-sysfs.c - create mode 100644 drivers/staging/fbtft/fbtft.h - -diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig -index 815de37..ce8f420 100644 ---- a/drivers/staging/Kconfig -+++ b/drivers/staging/Kconfig -@@ -106,4 +106,6 @@ source "drivers/staging/unisys/Kconfig" - - source "drivers/staging/clocking-wizard/Kconfig" - -+source "drivers/staging/fbtft/Kconfig" -+ - endif # STAGING -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index 33c640b..c242787 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -45,3 +45,4 @@ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ - obj-$(CONFIG_CRYPTO_SKEIN) += skein/ - obj-$(CONFIG_UNISYSSPAR) += unisys/ - obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ -+obj-$(CONFIG_FB_TFT) += fbtft/ -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -new file mode 100644 -index 0000000..3d6598b ---- /dev/null -+++ b/drivers/staging/fbtft/Kconfig -@@ -0,0 +1,9 @@ -+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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -new file mode 100644 -index 0000000..9132dde ---- /dev/null -+++ b/drivers/staging/fbtft/Makefile -@@ -0,0 +1,3 @@ -+# Core module -+obj-$(CONFIG_FB_TFT) += fbtft.o -+fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o -diff --git a/drivers/staging/fbtft/README b/drivers/staging/fbtft/README -new file mode 100644 -index 0000000..ba4c74c ---- /dev/null -+++ b/drivers/staging/fbtft/README -@@ -0,0 +1,32 @@ -+ FBTFT -+========= -+ -+Linux Framebuffer drivers for small TFT LCD display modules. -+The module 'fbtft' makes writing drivers for some of these displays very easy. -+ -+Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. -+ -+INSTALLATION -+ Download kernel sources -+ -+ From Linux 3.15 -+ cd drivers/video/fbdev/fbtft -+ git clone https://github.com/notro/fbtft.git -+ -+ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" -+ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ -+ -+ Before Linux 3.15 -+ cd drivers/video -+ git clone https://github.com/notro/fbtft.git -+ -+ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" -+ Add to drivers/video/Makefile: obj-y += fbtft/ -+ -+ Enable driver(s) in menuconfig and build the kernel -+ -+ -+See wiki for more information: https://github.com/notro/fbtft/wiki -+ -+ -+Source: https://github.com/notro/fbtft/ -diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c -new file mode 100644 -index 0000000..b3cddb0 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c -new file mode 100644 -index 0000000..873e2c7 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c -new file mode 100644 -index 0000000..dfa2c46 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-sysfs.c b/drivers/staging/fbtft/fbtft-sysfs.c -new file mode 100644 -index 0000000..45f8de3 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h -new file mode 100644 -index 0000000..0dbf3f9 ---- /dev/null -+++ b/drivers/staging/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 */ - -From 06d51134bbb3a8c0424ff056c590f48c5c118655 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:10 +0100 -Subject: [PATCH 122/172] staging: fbtft: add fb_agm1264k-fl driver - -This commit adds the fb_agm1264k-fl driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/fb_agm1264k-fl.c | 462 +++++++++++++++++++++++++++++++++ - 3 files changed, 471 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_agm1264k-fl.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 3d6598b..d552af6 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -7,3 +7,9 @@ menuconfig FB_TFT - 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) -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 9132dde..eb13cc5 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -1,3 +1,6 @@ - # 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 -diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c -new file mode 100644 -index 0000000..7fe4fa0 ---- /dev/null -+++ b/drivers/staging/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"); - -From d39ffbc96a102b600acb37c3474492efa6ec8cff Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:11 +0100 -Subject: [PATCH 123/172] staging: fbtft: add fb_bd663474 driver - -This commit adds the fb_bd663474 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_bd663474.c | 193 ++++++++++++++++++++++++++++++++++++ - 3 files changed, 200 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_bd663474.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d552af6..400492a 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -13,3 +13,9 @@ config FB_TFT_AGM1264K_FL - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index eb13cc5..9f80a6e 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -4,3 +4,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - - # drivers - obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o -+obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o -diff --git a/drivers/staging/fbtft/fb_bd663474.c b/drivers/staging/fbtft/fb_bd663474.c -new file mode 100644 -index 0000000..7e00c60 ---- /dev/null -+++ b/drivers/staging/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"); - -From 67c6f93bb8a6222372f8a9101f0cc251519af1cb Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:12 +0100 -Subject: [PATCH 124/172] staging: fbtft: add fb_hx8340bn driver - -This commit adds the fb_hx8340bn driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8340bn.c | 229 ++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8340bn.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 400492a..e6c23d5e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -19,3 +19,9 @@ config FB_TFT_BD663474 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 9f80a6e..7d89af7 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -5,3 +5,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - # 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 -diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c -new file mode 100644 -index 0000000..3939502f ---- /dev/null -+++ b/drivers/staging/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"); - -From 8085e50c45d7c0265dc4e964c8c97cbe6c94f0f7 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:13 +0100 -Subject: [PATCH 125/172] staging: fbtft: add fb_hx8347d driver - -This commit adds the fb_hx8347d driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8347d.c | 181 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 188 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8347d.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index e6c23d5e..9bb6e7b 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -25,3 +25,9 @@ config FB_TFT_HX8340BN - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 7d89af7..de68bf8 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -6,3 +6,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - 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 -diff --git a/drivers/staging/fbtft/fb_hx8347d.c b/drivers/staging/fbtft/fb_hx8347d.c -new file mode 100644 -index 0000000..8139a8f ---- /dev/null -+++ b/drivers/staging/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"); - -From e8dd102dd88b944666f475b2561947f014dc5322 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:14 +0100 -Subject: [PATCH 126/172] staging: fbtft: add fb_hx8353d driver - -This commit adds the fb_hx8353d driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8353d.c | 166 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 173 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8353d.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 9bb6e7b..015429b 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -31,3 +31,9 @@ config FB_TFT_HX8347D - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index de68bf8..766e285 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -7,3 +7,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_hx8353d.c b/drivers/staging/fbtft/fb_hx8353d.c -new file mode 100644 -index 0000000..c9512dc ---- /dev/null -+++ b/drivers/staging/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"); - -From ac4b97f943abb1e75ae9ce89b9a87c4c11ca7540 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:15 +0100 -Subject: [PATCH 127/172] staging: fbtft: add fb_ili9320 driver - -This commit adds the fb_ili9320 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9320.c | 234 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 241 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9320.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 015429b..eb57f19 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -37,3 +37,9 @@ config FB_TFT_HX8353D - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 766e285..e101cb6 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -8,3 +8,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9320.c b/drivers/staging/fbtft/fb_ili9320.c -new file mode 100644 -index 0000000..b26d893 ---- /dev/null -+++ b/drivers/staging/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"); - -From f4cd1ccd1d97e19bf697027ef2bf6fe49b7931d9 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:16 +0100 -Subject: [PATCH 128/172] staging: fbtft: add fb_ili9325 driver - -This commit adds the fb_ili9325 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9325.c | 291 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 298 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9325.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index eb57f19..24b9d91 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -43,3 +43,9 @@ config FB_TFT_ILI9320 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index e101cb6..09d4697 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -9,3 +9,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c -new file mode 100644 -index 0000000..5f88145 ---- /dev/null -+++ b/drivers/staging/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"); - -From 1fde4032c966ca64aa650d1d432f0e624dda2ef4 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:17 +0100 -Subject: [PATCH 129/172] staging: fbtft: add fb_ili9340 driver - -This commit adds the fb_ili9340 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9340.c | 163 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 170 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9340.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 24b9d91..6295acf 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -49,3 +49,9 @@ config FB_TFT_ILI9325 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 09d4697..aa65da8 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -10,3 +10,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9340.c b/drivers/staging/fbtft/fb_ili9340.c -new file mode 100644 -index 0000000..985687d ---- /dev/null -+++ b/drivers/staging/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"); - -From 231a5ec27c7d2ef02ec3bebe2797ece490f3083e Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:18 +0100 -Subject: [PATCH 130/172] staging: fbtft: add fb_ili9341 driver - -This commit adds the fb_ili9341 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9341.c | 179 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 186 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9341.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 6295acf..ee09e9d 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -55,3 +55,9 @@ config FB_TFT_ILI9340 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index aa65da8..a83a685 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -11,3 +11,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9341.c b/drivers/staging/fbtft/fb_ili9341.c -new file mode 100644 -index 0000000..225b2d8 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4df2aaf201deb710cb086303678f90ea9f734a5e Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:19 +0100 -Subject: [PATCH 131/172] staging: fbtft: add fb_ili9481 driver - -This commit adds the fb_ili9481 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9481.c | 117 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 124 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9481.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index ee09e9d..86a1bee 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -61,3 +61,9 @@ config FB_TFT_ILI9341 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index a83a685..0740641 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -12,3 +12,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9481.c b/drivers/staging/fbtft/fb_ili9481.c -new file mode 100644 -index 0000000..725157a ---- /dev/null -+++ b/drivers/staging/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"); - -From 514dc6a5071ade3f146f9fb20e1ed602a79858b2 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:20 +0100 -Subject: [PATCH 132/172] staging: fbtft: add fb_ili9486 driver - -This commit adds the fb_ili9486 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9486.c | 121 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 128 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9486.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 86a1bee..ca73537 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -67,3 +67,9 @@ config FB_TFT_ILI9481 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 0740641..8e633cf 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -13,3 +13,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9486.c b/drivers/staging/fbtft/fb_ili9486.c -new file mode 100644 -index 0000000..95b8999 ---- /dev/null -+++ b/drivers/staging/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"); - -From 0c2fe050a244e71f458b93795c7542f0c0f7adf9 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:21 +0100 -Subject: [PATCH 133/172] staging: fbtft: add fb_pcd8544 driver - -This commit adds the fb_pcd8544 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_pcd8544.c | 177 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 184 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_pcd8544.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index ca73537..fc1c76a 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -73,3 +73,9 @@ config FB_TFT_ILI9486 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 8e633cf..d7b9409 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -14,3 +14,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -new file mode 100644 -index 0000000..c4f2a5b ---- /dev/null -+++ b/drivers/staging/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"); - -From 0f29b5d63e6cab9d69eb1f7b18b6bd6fd79cf7bc Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:22 +0100 -Subject: [PATCH 134/172] staging: fbtft: add fb_ra8875 driver - -This commit adds the fb_ra8875 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ra8875.c | 331 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 338 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ra8875.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index fc1c76a..d66281e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -79,3 +79,9 @@ config FB_TFT_PCD8544 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index d7b9409..d9aa2b3 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -15,3 +15,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c -new file mode 100644 -index 0000000..c323c06 ---- /dev/null -+++ b/drivers/staging/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"); - -From a3cb66339f091b595534af93c2e2d5439ea6eddd Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:23 +0100 -Subject: [PATCH 135/172] staging: fbtft: add fb_s6d02a1 driver - -This commit adds the fb_s6d02a1 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_s6d02a1.c | 168 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 175 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_s6d02a1.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d66281e..34676db 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -85,3 +85,9 @@ config FB_TFT_RA8875 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index d9aa2b3..e349e7f 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -16,3 +16,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_s6d02a1.c b/drivers/staging/fbtft/fb_s6d02a1.c -new file mode 100644 -index 0000000..e412a42 ---- /dev/null -+++ b/drivers/staging/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"); - -From 664ff53df529fa15bbdbd95ec761b9438463ca9d Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:24 +0100 -Subject: [PATCH 136/172] staging: fbtft: add fb_s6d1121 driver - -This commit adds the fb_s6d1121 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_s6d1121.c | 208 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 215 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_s6d1121.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 34676db..4fd9cfd 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -91,3 +91,9 @@ config FB_TFT_S6D02A1 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index e349e7f..48b3ce1 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -17,3 +17,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c -new file mode 100644 -index 0000000..7bbb21b ---- /dev/null -+++ b/drivers/staging/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"); - -From 147010bf2cc6bcd6e415c50c9c1af9dd4e32625a Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:25 +0100 -Subject: [PATCH 137/172] staging: fbtft: add fb_ssd1289 driver - -This commit adds the fb_ssd1289 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1289.c | 206 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 213 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1289.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 4fd9cfd..05e8571 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -97,3 +97,9 @@ config FB_TFT_S6D1121 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 48b3ce1..ea5fa1e 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -18,3 +18,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c -new file mode 100644 -index 0000000..ef46fbc ---- /dev/null -+++ b/drivers/staging/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"); - -From 358b88a04cd9f842fe7ee796f366f1974f9388af Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:26 +0100 -Subject: [PATCH 138/172] staging: fbtft: add fb_ssd1306 driver - -This commit adds the fb_ssd1306 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1306.c | 229 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1306.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 05e8571..3ae8259 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -103,3 +103,9 @@ config FB_TFT_SSD1289 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index ea5fa1e..b612a4d 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -19,3 +19,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c -new file mode 100644 -index 0000000..5ea195b ---- /dev/null -+++ b/drivers/staging/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"); - -From 21e5c10651440dde2f52472e220cab45418250a2 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:27 +0100 -Subject: [PATCH 139/172] staging: fbtft: add fb_ssd1331 driver - -This commit adds the fb_ssd1331 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1331.c | 205 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 212 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1331.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 3ae8259..f1e4832 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -109,3 +109,9 @@ config FB_TFT_SSD1306 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index b612a4d..8dffb75 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -20,3 +20,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c -new file mode 100644 -index 0000000..da7464f ---- /dev/null -+++ b/drivers/staging/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"); - -From c5d81394418cd5a1c03d9fb1806b487d3aeaa8c8 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:28 +0100 -Subject: [PATCH 140/172] staging: fbtft: add fb_ssd1351 driver - -This commit adds the fb_ssd1351 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1351.c | 258 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 265 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1351.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index f1e4832..2ba8ef7 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -115,3 +115,9 @@ config FB_TFT_SSD1331 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 8dffb75..7481e34 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -21,3 +21,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c -new file mode 100644 -index 0000000..062d986 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4ea4d4cde4cefeddf0de53fac33251c1e1fbef0c Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:29 +0100 -Subject: [PATCH 141/172] staging: fbtft: add fb_st7735r driver - -This commit adds the fb_st7735r driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_st7735r.c | 195 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 202 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_st7735r.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 2ba8ef7..6bc2121 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -121,3 +121,9 @@ config FB_TFT_SSD1351 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 7481e34..2771e60 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -22,3 +22,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_st7735r.c b/drivers/staging/fbtft/fb_st7735r.c -new file mode 100644 -index 0000000..ea8f2b0 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4c95802207386b77ce4382d6da461c531b0a5233 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:30 +0100 -Subject: [PATCH 142/172] staging: fbtft: add fb_tinylcd driver - -This commit adds the fb_tinylcd driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_tinylcd.c | 124 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 131 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_tinylcd.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 6bc2121..dd740bfb 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -127,3 +127,9 @@ config FB_TFT_ST7735R - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 2771e60..a5c2d15 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -23,3 +23,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c -new file mode 100644 -index 0000000..ca98bfb ---- /dev/null -+++ b/drivers/staging/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"); - -From bda5811957a10834fb6fff897798cb88f483df69 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:31 +0100 -Subject: [PATCH 143/172] staging: fbtft: add fb_tls8204 driver - -This commit adds the fb_tls8204 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_tls8204.c | 176 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 183 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_tls8204.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index dd740bfb..73c181c 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -133,3 +133,9 @@ config FB_TFT_TINYLCD - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index a5c2d15..5fa20f0 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -24,3 +24,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c -new file mode 100644 -index 0000000..8738c7a ---- /dev/null -+++ b/drivers/staging/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"); - -From 356a07e91094b2137aa065cda8109f3b311c049b Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:32 +0100 -Subject: [PATCH 144/172] staging: fbtft: add fb_uc1701 driver - -This commit adds the fb_uc1701 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_uc1701.c | 210 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 217 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_uc1701.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 73c181c..fb3f4df 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -139,3 +139,9 @@ config FB_TFT_TLS8204 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 5fa20f0..732b219 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -25,3 +25,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_uc1701.c b/drivers/staging/fbtft/fb_uc1701.c -new file mode 100644 -index 0000000..d70ac52 ---- /dev/null -+++ b/drivers/staging/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"); - -From 9b7cb18c0b0d94f9f810f51c3f1ea702041ee48a Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:33 +0100 -Subject: [PATCH 145/172] staging: fbtft: add fb_upd161704 driver - -This commit adds the fb_upd161704 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_upd161704.c | 206 +++++++++++++++++++++++++++++++++++ - 3 files changed, 213 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_upd161704.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index fb3f4df..886a48c 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -145,3 +145,9 @@ config FB_TFT_UC1701 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 732b219..dca2a45 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -26,3 +26,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_upd161704.c b/drivers/staging/fbtft/fb_upd161704.c -new file mode 100644 -index 0000000..8d7794e ---- /dev/null -+++ b/drivers/staging/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"); - -From fba3874e2796888b550853e6a64b38bf0dfe97f5 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:34 +0100 -Subject: [PATCH 146/172] staging: fbtft: add fb_watterott driver - -This commit adds the fb_watterott driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_watterott.c | 324 +++++++++++++++++++++++++++++++++++ - 3 files changed, 331 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_watterott.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 886a48c..8edec90 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -151,3 +151,9 @@ config FB_TFT_UPD161704 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index dca2a45..78008c3 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -27,3 +27,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c -new file mode 100644 -index 0000000..975b579 ---- /dev/null -+++ b/drivers/staging/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"); - -From dcf965435e041238d46101633705e5b60bd448e3 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:35 +0100 -Subject: [PATCH 147/172] staging: fbtft: add flexfb driver - -This commit adds the flexfb driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/flexfb.c | 593 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 600 insertions(+) - create mode 100644 drivers/staging/fbtft/flexfb.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 8edec90..d7ddd6e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -157,3 +157,9 @@ config FB_TFT_WATTEROTT - 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. -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 78008c3..5b3254a 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -28,3 +28,4 @@ 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 -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -new file mode 100644 -index 0000000..45574a0 ---- /dev/null -+++ b/drivers/staging/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"); - -From e6ea545ba51748ab7d35a9a66d15130f10b20cde Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:36 +0100 -Subject: [PATCH 148/172] staging: fbtft: add fbtft_device driver - -This commit adds the fbtft_device driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 4 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/fbtft_device.c | 1444 ++++++++++++++++++++++++++++++++++ - 3 files changed, 1451 insertions(+) - create mode 100644 drivers/staging/fbtft/fbtft_device.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d7ddd6e..995a910 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -163,3 +163,7 @@ config FB_FLEX - 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/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 5b3254a..e773f0f 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -29,3 +29,6 @@ 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 -diff --git a/drivers/staging/fbtft/fbtft_device.c b/drivers/staging/fbtft/fbtft_device.c -new file mode 100644 -index 0000000..b9f4c30 ---- /dev/null -+++ b/drivers/staging/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"); - -From 6cd6c52b928abddd97ed84d81dc7dc4299e4ef0b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Tue, 20 Jan 2015 23:40:26 +0100 -Subject: [PATCH 149/172] staging: fbtft: remove ARCH_BCM2708 optimization -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -ARCH_BCM2708 is not present in mainline so remove optimization. - -Signed-off-by: Noralf Trønnes -Reported-by: Paul Bolle -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fbtft-io.c | 170 --------------------------------------- - 1 file changed, 170 deletions(-) - -diff --git a/drivers/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c -index dfa2c46..32155a7 100644 ---- a/drivers/staging/fbtft/fbtft-io.c -+++ b/drivers/staging/fbtft/fbtft-io.c -@@ -2,9 +2,6 @@ - #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) -@@ -129,171 +126,6 @@ int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len) - } - EXPORT_SYMBOL(fbtft_read_spi); +@@ -1734,8 +1670,6 @@ static void pl011_shutdown(struct uart_port *port) + container_of(port, struct uart_amba_port, port); + unsigned int cr; +- cancel_delayed_work_sync(&uap->tx_softirq_work); - --#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 -@@ -405,5 +237,3 @@ int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) - return -1; - } - EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); -- --#endif /* CONFIG_ARCH_BCM2708 */ + /* + * disable all interrupts + */ +@@ -2282,7 +2216,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) + uap->port.ops = &amba_pl011_pops; + uap->port.flags = UPF_BOOT_AUTOCONF; + uap->port.line = i; +- INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq); + pl011_dma_probe(&dev->dev, uap); + + /* Ensure interrupts from this UART are masked and cleared */ -From 4db13fcabcf3976a2996aeaabaae5c14dd203506 Mon Sep 17 00:00:00 2001 -From: Dan Carpenter -Date: Fri, 23 Jan 2015 17:20:25 +0300 -Subject: [PATCH 150/172] staging: fbtft: fix allyesconfig build - -There are two functions call reset() so the allyesconfig breaks. Let's -make this one static. - -Reported-by: Jim Davis -Signed-off-by: Dan Carpenter -Tested-by: Jim Davis -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_agm1264k-fl.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c -index 7fe4fa0..9cc7d25 100644 ---- a/drivers/staging/fbtft/fb_agm1264k-fl.c -+++ b/drivers/staging/fbtft/fb_agm1264k-fl.c -@@ -89,7 +89,7 @@ static int init_display(struct fbtft_par *par) - return 0; - } - --void reset(struct fbtft_par *par) -+static void reset(struct fbtft_par *par) - { - if (par->gpio.reset == -1) - return; - -From 6d2e37a80dfcefcbfbe944a494bfacd7a67fb850 Mon Sep 17 00:00:00 2001 -From: Krzysztof Adamski -Date: Thu, 22 Jan 2015 19:08:58 +0100 -Subject: [PATCH 151/172] staging: fbtft: fbtft-core mark functions static - -This fixes sparse warnings like: - warning: symbol XXX was not declared. Should it be static? - -by declaring all local functions static. - -Signed-off-by: Krzysztof Adamski -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fbtft-core.c | 47 +++++++++++++++++++++----------------- - 1 file changed, 26 insertions(+), 21 deletions(-) - -diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c -index 873e2c7..37dcf7e 100644 ---- a/drivers/staging/fbtft/fbtft-core.c -+++ b/drivers/staging/fbtft/fbtft-core.c -@@ -79,7 +79,7 @@ void fbtft_dbg_hex(const struct device *dev, int groupsize, - } - EXPORT_SYMBOL(fbtft_dbg_hex); - --unsigned long fbtft_request_gpios_match(struct fbtft_par *par, -+static unsigned long fbtft_request_gpios_match(struct fbtft_par *par, - const struct fbtft_gpio *gpio) - { - int ret; -@@ -123,7 +123,7 @@ unsigned long fbtft_request_gpios_match(struct fbtft_par *par, - return FBTFT_GPIO_NO_MATCH; - } - --int fbtft_request_gpios(struct fbtft_par *par) -+static int fbtft_request_gpios(struct fbtft_par *par) - { - struct fbtft_platform_data *pdata = par->pdata; - const struct fbtft_gpio *gpio; -@@ -249,7 +249,7 @@ static int fbtft_request_gpios_dt(struct fbtft_par *par) - #endif - - #ifdef CONFIG_FB_BACKLIGHT --int fbtft_backlight_update_status(struct backlight_device *bd) -+static 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); -@@ -266,7 +266,7 @@ int fbtft_backlight_update_status(struct backlight_device *bd) - return 0; - } - --int fbtft_backlight_get_brightness(struct backlight_device *bd) -+static int fbtft_backlight_get_brightness(struct backlight_device *bd) - { - return bd->props.brightness; - } -@@ -337,7 +337,8 @@ void fbtft_unregister_backlight(struct fbtft_par *par) { }; - 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) -+static 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); -@@ -355,7 +356,7 @@ void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) - } - - --void fbtft_reset(struct fbtft_par *par) -+static void fbtft_reset(struct fbtft_par *par) - { - if (par->gpio.reset == -1) - return; -@@ -367,7 +368,8 @@ void fbtft_reset(struct fbtft_par *par) - } - - --void fbtft_update_display(struct fbtft_par *par, unsigned start_line, unsigned end_line) -+static 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; -@@ -445,7 +447,7 @@ void fbtft_update_display(struct fbtft_par *par, unsigned start_line, unsigned e - } - - --void fbtft_mkdirty(struct fb_info *info, int y, int height) -+static void fbtft_mkdirty(struct fb_info *info, int y, int height) - { - struct fbtft_par *par = info->par; - struct fb_deferred_io *fbdefio = info->fbdefio; -@@ -468,7 +470,7 @@ void fbtft_mkdirty(struct fb_info *info, int y, int height) - schedule_delayed_work(&info->deferred_work, fbdefio->delay); - } - --void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) -+static 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; -@@ -507,7 +509,8 @@ void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) - } - - --void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) -+static void fbtft_fb_fillrect(struct fb_info *info, -+ const struct fb_fillrect *rect) - { - struct fbtft_par *par = info->par; - -@@ -519,7 +522,8 @@ void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) - par->fbtftops.mkdirty(info, rect->dy, rect->height); - } - --void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -+static void fbtft_fb_copyarea(struct fb_info *info, -+ const struct fb_copyarea *area) - { - struct fbtft_par *par = info->par; - -@@ -531,7 +535,8 @@ void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) - par->fbtftops.mkdirty(info, area->dy, area->height); - } - --void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *image) -+static void fbtft_fb_imageblit(struct fb_info *info, -+ const struct fb_image *image) - { - struct fbtft_par *par = info->par; - -@@ -543,8 +548,8 @@ void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *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) -+static 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; -@@ -561,16 +566,16 @@ ssize_t fbtft_fb_write(struct fb_info *info, - } - - /* from pxafb.c */ --unsigned int chan_to_field(unsigned chan, struct fb_bitfield *bf) -+static 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) -+static 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; -@@ -598,7 +603,7 @@ int fbtft_fb_setcolreg(unsigned regno, - return ret; - } - --int fbtft_fb_blank(int blank, struct fb_info *info) -+static int fbtft_fb_blank(int blank, struct fb_info *info) - { - struct fbtft_par *par = info->par; - int ret = -EINVAL; -@@ -623,7 +628,7 @@ int fbtft_fb_blank(int blank, struct fb_info *info) - return ret; - } - --void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) -+static void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) - { - if (src->write) - dst->write = src->write; -@@ -1265,7 +1270,7 @@ EXPORT_SYMBOL(fbtft_init_display); - * - * Return: 0 if successful, negative if error - */ --int fbtft_verify_gpios(struct fbtft_par *par) -+static int fbtft_verify_gpios(struct fbtft_par *par) - { - struct fbtft_platform_data *pdata; - int i; - -From beabd79f51c4211da36c358630199afc2ecc0d70 Mon Sep 17 00:00:00 2001 -From: Mauro Stettler -Date: Sun, 25 Jan 2015 20:26:17 +0900 -Subject: [PATCH 152/172] staging: flexfb: Remove initialization to 0 - -(the initial post had a typo in the short summary, reposting) - -This fixes some of the code style issues in the flexfb driver by removing -unnecessary initializations of static variables to 0 or to NULL. - -Signed-off-by: Mauro Stettler -Cc: Greg Kroah-Hartman -Cc: Thomas Petazzoni -Cc: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/flexfb.c | 18 +++++++++--------- - 1 file changed, 9 insertions(+), 9 deletions(-) - -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -index 45574a0..4eec888 100644 ---- a/drivers/staging/fbtft/flexfb.c -+++ b/drivers/staging/fbtft/flexfb.c -@@ -31,24 +31,24 @@ - #define DRVNAME "flexfb" - - --static char *chip = NULL; -+static char *chip; - module_param(chip, charp, 0); - MODULE_PARM_DESC(chip, "LCD controller"); - --static unsigned int width = 0; -+static unsigned int width; - module_param(width, uint, 0); - MODULE_PARM_DESC(width, "Display width"); - --static unsigned int height = 0; -+static unsigned int height; - module_param(height, uint, 0); - MODULE_PARM_DESC(height, "Display height"); - - static int init[512]; --static int init_num = 0; -+static int init_num; - module_param_array(init, int, &init_num, 0); - MODULE_PARM_DESC(init, "Init sequence"); - --static unsigned int setaddrwin = 0; -+static unsigned int setaddrwin; - module_param(setaddrwin, uint, 0); - MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use"); - -@@ -60,17 +60,17 @@ static unsigned int regwidth = 8; - module_param(regwidth, uint, 0); - MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)"); - --static bool nobacklight = false; -+static bool nobacklight; - module_param(nobacklight, bool, 0); - MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality."); - --static bool latched = false; -+static bool latched; - module_param(latched, bool, 0); - MODULE_PARM_DESC(latched, "Use with latched 16-bit databus"); - - --static int *initp = NULL; --static int initp_num = 0; -+static int *initp; -+static int initp_num; - - /* default init sequences */ - static int st7735r_init[] = { \ - -From 7e831153b89601cab2d004a6ac0eaeb6e734a2c8 Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:33:03 +0200 -Subject: [PATCH 153/172] staging: fbtft: fix space required after that ';' - -This patch fixes the following checkpatch.pl error: -fix space required after that ';' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index c4f2a5b..a5f0ad4 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x=0;x<84;x++) { -- for (y=0;y<6;y++) { -+ for (x=0; x<84; x++) { -+ for (y=0; y<6; y++) { - *buf = 0x00; -- for (i=0;i<8;i++) { -+ for (i=0; i<8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From fd28a2e66acf9bb757cb5b63aba22e6765ab441b Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:35:30 +0200 -Subject: [PATCH 154/172] staging: fbtft: fix spaces required around that '=' - -This patch fixes the following checkpatch.pl error: -fix spaces required around that '=' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index a5f0ad4..e44eecb 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x=0; x<84; x++) { -- for (y=0; y<6; y++) { -+ for (x = 0; x<84; x++) { -+ for (y = 0; y<6; y++) { - *buf = 0x00; -- for (i=0; i<8; i++) { -+ for (i = 0; i<8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From 6f65a6510903f0c9fceb1f3d65ff28bd9a784e6c Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:37:24 +0200 -Subject: [PATCH 155/172] staging: fbtft: fix spaces required around that '<' - -This patch fixes the following checkpatch.pl error: -fix spaces required around that '<' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index e44eecb..ab46ccf 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x = 0; x<84; x++) { -- for (y = 0; y<6; y++) { -+ for (x = 0; x < 84; x++) { -+ for (y = 0; y < 6; y++) { - *buf = 0x00; -- for (i = 0; i<8; i++) { -+ for (i = 0; i < 8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From 2e22627744670a081bb431ca45bbf5cf84a2030c Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:39:56 +0200 -Subject: [PATCH 156/172] staging: fbtft: fix Macros with complex values should - be enclosed in parentheses - -This patch fixes the following checkpatch.pl error: -fix Macros with complex values should be enclosed in parentheses - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index ab46ccf..1d1a865 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -33,7 +33,7 @@ - #define DRVNAME "fb_pcd8544" - #define WIDTH 84 - #define HEIGHT 48 --#define TXBUFLEN 84*6 -+#define TXBUFLEN (84*6) - #define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */ - - static unsigned tc = 0; - -From 3b5f096eae4c0080b6e88f6b8aaa1fffefc5a0c7 Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:42:59 +0200 -Subject: [PATCH 157/172] staging: fbtft: fix do not initialise statics to 0 or - NULL - -This patch fixes the following checkpatch.pl error: -fix do not initialise statics to 0 or NULL - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index 1d1a865..8b9ebfb 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -36,7 +36,7 @@ - #define TXBUFLEN (84*6) - #define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */ - --static unsigned tc = 0; -+static unsigned tc; - module_param(tc, uint, 0); - MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)"); - - -From 608b9170aa906a03b34017095c076df47c68b10b Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Wed, 28 Jan 2015 15:29:05 +0200 -Subject: [PATCH 158/172] staging: fbtft: remove redundant .owner - -This patch fixes the following coccicheck warning: -No need to set .owner here. The core will do it. - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/flexfb.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -index 4eec888..90832c3 100644 ---- a/drivers/staging/fbtft/flexfb.c -+++ b/drivers/staging/fbtft/flexfb.c -@@ -559,7 +559,6 @@ static const struct platform_device_id flexfb_platform_ids[] = { - static struct platform_driver flexfb_platform_driver = { - .driver = { - .name = DRVNAME, -- .owner = THIS_MODULE, - }, - .id_table = flexfb_platform_ids, - .probe = flexfb_probe_pdev, - -From fc7a6c1b574ecf2541ba11643b4936e8b24d2b09 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Wed, 18 Feb 2015 22:49:42 +0100 -Subject: [PATCH 159/172] config: enable FBTFT drivers - ---- - arch/arm/configs/bcm2709_defconfig | 28 ++++++++++++++++++++++++++++ - arch/arm/configs/bcmrpi_defconfig | 28 ++++++++++++++++++++++++++++ - 2 files changed, 56 insertions(+) - -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index bc557a5..70d9e3b 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -1027,6 +1027,34 @@ CONFIG_LIRC_IMON=m - CONFIG_LIRC_RPI=m - CONFIG_LIRC_SASEM=m - CONFIG_LIRC_SERIAL=m -+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_IOMMU_SUPPORT is not set - CONFIG_EXTCON=m - CONFIG_EXTCON_ARIZONA=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 4d703f4..1563996 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -1019,6 +1019,34 @@ CONFIG_LIRC_IMON=m - CONFIG_LIRC_RPI=m - CONFIG_LIRC_SASEM=m - CONFIG_LIRC_SERIAL=m -+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_IOMMU_SUPPORT is not set - CONFIG_EXTCON=m - CONFIG_EXTCON_ARIZONA=m - -From 1e1f6c97e1b39e609d2c5e63db46082f5dbd1312 Mon Sep 17 00:00:00 2001 +From 379ed9d4153e718fb64f5954348bcb09b46de153 Mon Sep 17 00:00:00 2001 From: Jon Burgess Date: Tue, 17 Feb 2015 13:22:17 +0000 -Subject: [PATCH 160/172] Create generic i2c-rtc overlay for supporting ds1307, +Subject: [PATCH 087/104] Create generic i2c-rtc overlay for supporting ds1307, ds3231, pcf2127 and pcf8523. Signed-off-by: Jon Burgess @@ -153178,11 +127440,11 @@ Signed-off-by: Jon Burgess create mode 100644 arch/arm/boot/dts/i2c-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 754291e..831d946 100644 +index 6839ece..bc13a9a 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -66,6 +66,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -14,6 +14,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb @@ -153239,10 +127501,10 @@ index 0000000..5d5abb1 + }; +}; -From b06a6f1514466b593b2a533c128dc87cc8d16979 Mon Sep 17 00:00:00 2001 +From f484d5e034443ee69b02cc557e20ad5d06b4eafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:24:05 +0100 -Subject: [PATCH 161/172] dts: overlay: add support for rpi-display +Subject: [PATCH 088/104] dts: overlay: add support for rpi-display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153257,17 +127519,17 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/rpi-display-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 831d946..b3b775b 100644 +index bc13a9a..3b2da65 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -77,6 +77,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb +@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb + diff --git a/arch/arm/boot/dts/rpi-display-overlay.dts b/arch/arm/boot/dts/rpi-display-overlay.dts new file mode 100644 index 0000000..0578810 @@ -153356,10 +127618,10 @@ index 0000000..0578810 + }; +}; -From 9da9198099befac4b05083230123fd114c5d286d Mon Sep 17 00:00:00 2001 +From 6db0c3f79508f0cef74658ee671ad055685f36b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:36:27 +0100 -Subject: [PATCH 162/172] dts: overlay: add support for HY28A display +Subject: [PATCH 089/104] dts: overlay: add support for HY28A display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153375,10 +127637,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/hy28a-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index b3b775b..9d6f947 100644 +index 3b2da65..1e1be41 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -71,6 +71,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb @@ -153480,10 +127742,10 @@ index 0000000..3cd3083 + }; +}; -From 44b11d19ade98ef346cb6135d5b9697deadd8b39 Mon Sep 17 00:00:00 2001 +From 6d38a81ec9b4c405e32dd3fb0a61318e5edc1643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:38:00 +0100 -Subject: [PATCH 163/172] dts: overlay: add support for HY28B display +Subject: [PATCH 090/104] dts: overlay: add support for HY28B display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153499,10 +127761,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/hy28b-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 9d6f947..e4b26b3 100644 +index 1e1be41..7321acb 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -72,6 +72,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb +@@ -20,6 +20,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb @@ -153659,10 +127921,10 @@ index 0000000..f774c4a + }; +}; -From 9fa8ac547b9ec39fb6fcdaa4a3409aa518ffd5cc Mon Sep 17 00:00:00 2001 +From 69280718c53ce21129f1ccb56d5efabe868b6c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:38:47 +0100 -Subject: [PATCH 164/172] dts: overlay: add support for PiScreen display +Subject: [PATCH 091/104] dts: overlay: add support for PiScreen display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153677,10 +127939,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/piscreen-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index e4b26b3..890c789 100644 +index 7321acb..68ee5dd 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -78,6 +78,7 @@ dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb +@@ -26,6 +26,7 @@ dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb @@ -153789,415 +128051,10 @@ index 0000000..8cd6a95 + }; +}; -From 75aedead57189858c6aec1568f028d8e13445752 Mon Sep 17 00:00:00 2001 -From: Dave Stevenson <6by9@users.noreply.github.com> -Date: Thu, 26 Feb 2015 20:51:03 +0000 -Subject: [PATCH 165/172] BCM2835-V4L2: Fix compliance test failures - -VIDIOC_TRY_FMT and VIDIOC_S_FMT tests were faling due -to reporting V4L2_COLORSPACE_JPEG when the colour -format wasn't V4L2_PIX_FMT_JPEG. -Now reports V4L2_COLORSPACE_SMPTE170M for YUV formats. ---- - drivers/media/platform/bcm2835/bcm2835-camera.c | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -index e5a0010..eb4d831 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.c -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -774,7 +774,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, - a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; - a->fmt.sizeimage = (preview_port->es.video.width * - preview_port->es.video.height * 3)>>1; -- a->fmt.colorspace = V4L2_COLORSPACE_JPEG; -+ a->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; - } -@@ -860,8 +860,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - - if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -- else -+ else if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; - - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -@@ -904,10 +906,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) - f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; - -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) -+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -- else -+ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, - -From 4c8a8d6a18dea92a111c797d48e4b05491ec1399 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 25 Feb 2015 09:45:56 +0000 -Subject: [PATCH 166/172] vchiq_arm: Complete support for SYNCHRONOUS mode - ---- - .../vc04_services/interface/vchi/vchi_common.h | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 5 +- - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 106 +++++++++++++++++---- - .../vc04_services/interface/vchiq_arm/vchiq_core.h | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 3 + - 5 files changed, 98 insertions(+), 18 deletions(-) - -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -index d76118c..d535a72 100644 ---- a/drivers/misc/vc04_services/interface/vchi/vchi_common.h -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -@@ -116,6 +116,7 @@ typedef enum - VCHI_SERVICE_OPTION_MIN, - - VCHI_SERVICE_OPTION_TRACE, -+ VCHI_SERVICE_OPTION_SYNCHRONOUS, - - VCHI_SERVICE_OPTION_MAX - } VCHI_SERVICE_OPTION_T; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -index c382740..d2797db 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -@@ -36,7 +36,7 @@ - - #define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') - /* The version of VCHIQ - change with any non-trivial change */ --#define VCHIQ_VERSION 7 -+#define VCHIQ_VERSION 8 - /* The minimum compatible version - update to match VCHIQ_VERSION with any - ** incompatible change */ - #define VCHIQ_VERSION_MIN 3 -@@ -47,6 +47,9 @@ - /* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ - #define VCHIQ_VERSION_CLOSE_DELIVERED 7 - -+/* The version that made it safe to use SYNCHRONOUS mode */ -+#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 -+ - #define VCHIQ_MAX_STATES 1 - #define VCHIQ_MAX_SERVICES 4096 - #define VCHIQ_MAX_SLOTS 128 -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -index 14a4155..835688b 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -66,6 +66,13 @@ struct vchiq_openack_payload { - short version; - }; - -+enum -+{ -+ QMFLAGS_IS_BLOCKING = (1 << 0), -+ QMFLAGS_NO_MUTEX_LOCK = (1 << 1), -+ QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2) -+}; -+ - /* we require this for consistency between endpoints */ - vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); - vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); -@@ -743,7 +750,7 @@ process_free_queue(VCHIQ_STATE_T *state) - static VCHIQ_STATUS_T - queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - int msgid, const VCHIQ_ELEMENT_T *elements, -- int count, int size, int is_blocking) -+ int count, int size, int flags) - { - VCHIQ_SHARED_STATE_T *local; - VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; -@@ -758,7 +765,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - - WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); - -- if ((type != VCHIQ_MSG_RESUME) && -+ if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && - (mutex_lock_interruptible(&state->slot_mutex) != 0)) - return VCHIQ_RETRY; - -@@ -766,6 +773,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - int tx_end_index; - - BUG_ON(!service); -+ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); - - if (service->closing) { - /* The service has been closed */ -@@ -841,12 +850,15 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - spin_unlock("a_spinlock); - } - -- header = reserve_space(state, stride, is_blocking); -+ header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); - - if (!header) { - if (service) - VCHIQ_SERVICE_STATS_INC(service, slot_stalls); -- mutex_unlock(&state->slot_mutex); -+ /* In the event of a failure, return the mutex to the -+ state it was in */ -+ if (!(flags & QMFLAGS_NO_MUTEX_LOCK)) -+ mutex_unlock(&state->slot_mutex); - return VCHIQ_RETRY; - } - -@@ -864,6 +876,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - VCHIQ_MSG_DSTPORT(msgid)); - - BUG_ON(!service); -+ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); - - for (i = 0, pos = 0; i < (unsigned int)count; - pos += elements[i++].size) -@@ -965,7 +979,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - if (service && (type == VCHIQ_MSG_CLOSE)) - vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); - -- if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) - mutex_unlock(&state->slot_mutex); - - remote_event_signal(&state->remote->trigger); -@@ -1535,8 +1549,14 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) - sizeof(ack_payload) - }; - -+ if (state->version_common < -+ VCHIQ_VERSION_SYNCHRONOUS_MODE) -+ service->sync = 0; -+ - /* Acknowledge the OPEN */ -- if (service->sync) { -+ if (service->sync && -+ (state->version_common >= -+ VCHIQ_VERSION_SYNCHRONOUS_MODE)) { - if (queue_message_sync(state, NULL, - VCHIQ_MAKE_MSG( - VCHIQ_MSG_OPENACK, -@@ -1790,6 +1810,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) - vchiq_log_info(vchiq_core_log_level, - "%d: prs CONNECT@%x", - state->id, (unsigned int)header); -+ state->version_common = ((VCHIQ_SLOT_ZERO_T *) -+ state->slot_data)->version; - up(&state->connect); - break; - case VCHIQ_MSG_BULK_RX: -@@ -1943,7 +1965,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) - /* Send a PAUSE in response */ - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -- NULL, 0, 0, 0) == VCHIQ_RETRY) -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK) -+ == VCHIQ_RETRY) - goto bail_not_ready; - if (state->is_master) - pause_bulks(state); -@@ -2041,7 +2064,9 @@ slot_handler_func(void *v) - pause_bulks(state); - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -- NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ NULL, 0, 0, -+ QMFLAGS_NO_MUTEX_UNLOCK) -+ != VCHIQ_RETRY) { - vchiq_set_conn_state(state, - VCHIQ_CONNSTATE_PAUSE_SENT); - } else { -@@ -2059,7 +2084,8 @@ slot_handler_func(void *v) - case VCHIQ_CONNSTATE_RESUMING: - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), -- NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK) -+ != VCHIQ_RETRY) { - if (state->is_master) - resume_bulks(state); - vchiq_set_conn_state(state, -@@ -2180,6 +2206,7 @@ sync_func(void *v) - service->remoteport = remoteport; - vchiq_set_service_state(service, - VCHIQ_SRVSTATE_OPENSYNC); -+ service->sync = 1; - up(&service->remove_event); - } - release_message_sync(state, header); -@@ -2358,6 +2385,9 @@ vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, - return VCHIQ_ERROR; - } - -+ if (VCHIQ_VERSION < slot_zero->version) -+ slot_zero->version = VCHIQ_VERSION; -+ - if (is_master) { - local = &slot_zero->master; - remote = &slot_zero->slave; -@@ -2661,7 +2691,7 @@ vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) - vchiq_use_service_internal(service); - status = queue_message(service->state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), -- &body, 1, sizeof(payload), 1); -+ &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING); - if (status == VCHIQ_SUCCESS) { - /* Wait for the ACK/NAK */ - if (down_interruptible(&service->remove_event) != 0) { -@@ -2690,7 +2720,18 @@ release_service_messages(VCHIQ_SERVICE_T *service) - int slot_last = state->remote->slot_last; - int i; - -- /* Release any claimed messages */ -+ /* Release any claimed messages aimed at this service */ -+ -+ if (service->sync) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ state->remote->slot_sync); -+ if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport) -+ release_message_sync(state, header); -+ -+ return; -+ } -+ - for (i = state->remote->slot_first; i <= slot_last; i++) { - VCHIQ_SLOT_INFO_T *slot_info = - SLOT_INFO_FROM_INDEX(state, i); -@@ -2888,17 +2929,31 @@ vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) - (VCHIQ_MSG_CLOSE, - service->localport, - VCHIQ_MSG_DSTPORT(service->remoteport)), -- NULL, 0, 0, 0); -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK); - - if (status == VCHIQ_SUCCESS) { -- if (!close_recvd) -+ if (!close_recvd) { -+ /* Change the state while the mutex is -+ still held */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_CLOSESENT); -+ mutex_unlock(&state->slot_mutex); -+ if (service->sync) -+ mutex_unlock(&state->sync_mutex); - break; -+ } - } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { - mutex_unlock(&state->sync_mutex); - break; - } else - break; - -+ /* Change the state while the mutex is still held */ -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD); -+ mutex_unlock(&state->slot_mutex); -+ if (service->sync) -+ mutex_unlock(&state->sync_mutex); -+ - status = close_service_complete(service, - VCHIQ_SRVSTATE_CLOSERECVD); - break; -@@ -3005,7 +3060,7 @@ vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) - if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, -- 0, 1) == VCHIQ_RETRY) -+ 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY) - return VCHIQ_RETRY; - - vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); -@@ -3291,6 +3346,16 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - service->localport, service->remoteport, dir_char, - size, (unsigned int)bulk->data, (unsigned int)userdata); - -+ /* The slot mutex must be held when the service is being closed, so -+ claim it here to ensure that isn't happening */ -+ if (mutex_lock_interruptible(&state->slot_mutex) != 0) { -+ status = VCHIQ_RETRY; -+ goto cancel_bulk_error_exit; -+ } -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) -+ goto unlock_both_error_exit; -+ - if (state->is_master) { - queue->local_insert++; - if (resolve_bulks(service, queue)) -@@ -3304,14 +3369,17 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - status = queue_message(state, NULL, - VCHIQ_MAKE_MSG(dir_msgtype, - service->localport, service->remoteport), -- &element, 1, sizeof(payload), 1); -+ &element, 1, sizeof(payload), -+ QMFLAGS_IS_BLOCKING | -+ QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK); - if (status != VCHIQ_SUCCESS) { -- vchiq_complete_bulk(bulk); -- goto unlock_error_exit; -+ goto unlock_both_error_exit; - } - queue->local_insert++; - } - -+ mutex_unlock(&state->slot_mutex); - mutex_unlock(&service->bulk_mutex); - - vchiq_log_trace(vchiq_core_log_level, -@@ -3335,6 +3403,10 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - - return status; - -+unlock_both_error_exit: -+ mutex_unlock(&state->slot_mutex); -+cancel_bulk_error_exit: -+ vchiq_complete_bulk(bulk); - unlock_error_exit: - mutex_unlock(&service->bulk_mutex); - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -index 1b27917..9be484c 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -@@ -402,6 +402,7 @@ struct vchiq_state_struct { - int initialised; - VCHIQ_CONNSTATE_T conn_state; - int is_master; -+ short version_common; - - VCHIQ_SHARED_STATE_T *local; - VCHIQ_SHARED_STATE_T *remote; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -index c26eff7..8072ff6 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -738,6 +738,9 @@ int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, - case VCHI_SERVICE_OPTION_TRACE: - vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; - break; -+ case VCHI_SERVICE_OPTION_SYNCHRONOUS: -+ vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; -+ break; - default: - service = NULL; - break; - -From 32765026651961cf8f67a28d224165af9adeae32 Mon Sep 17 00:00:00 2001 +From 4ac0db1ce0bd16d4a16add2bd605680556ec6014 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 27 Feb 2015 15:10:24 +0000 -Subject: [PATCH 167/172] enc28j60: Add device tree compatible string and an +Subject: [PATCH 092/104] enc28j60: Add device tree compatible string and an overlay --- @@ -154208,11 +128065,11 @@ Subject: [PATCH 167/172] enc28j60: Add device tree compatible string and an create mode 100755 arch/arm/boot/dts/enc28j60-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 890c789..7480e1c 100644 +index 68ee5dd..a91c55d 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -66,6 +66,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -14,6 +14,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb @@ -154281,10 +128138,10 @@ index b1b5f66..c6b6e1a 100644 .probe = enc28j60_probe, .remove = enc28j60_remove, -From d90aba0187af45d761259e4060387821b9bfa3f4 Mon Sep 17 00:00:00 2001 +From cd7f5832bc7f0e01e2f1df445d7f5f86434e22e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 27 Feb 2015 18:17:25 +0100 -Subject: [PATCH 168/172] dts: overlay: add support for Adafruit PiTFT +Subject: [PATCH 093/104] dts: overlay: add support for Adafruit PiTFT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154299,10 +128156,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/pitft28-resistive-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 7480e1c..ad6984e 100644 +index a91c55d..707d373 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -80,6 +80,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb +@@ -28,6 +28,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb @@ -154432,32 +128289,30 @@ index 0000000..d506eae + }; +}; -From acc9df3814ad9edf4a323c12b150123fd690f3be Mon Sep 17 00:00:00 2001 +From 061c3aeaf590bcf9550d78f763a763f97ad46380 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 2 Mar 2015 09:37:02 +0000 -Subject: [PATCH 169/172] enable compiling spi-bcm2835 and add overlay to allow +Subject: [PATCH 094/104] enable compiling spi-bcm2835 and add overlay to allow us to load the driver --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/spi-bcm2835-overlay.dts | 18 ++++++++++++++++++ - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - 4 files changed, 21 insertions(+) + 2 files changed, 19 insertions(+) create mode 100644 arch/arm/boot/dts/spi-bcm2835-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index ad6984e..ed45f81 100644 +index 707d373..3988c44 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -85,6 +85,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb +@@ -33,6 +33,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += spi-bcm2835-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ + + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb diff --git a/arch/arm/boot/dts/spi-bcm2835-overlay.dts b/arch/arm/boot/dts/spi-bcm2835-overlay.dts new file mode 100644 index 0000000..fc1e39b @@ -154482,35 +128337,11 @@ index 0000000..fc1e39b + }; + }; +}; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index 70d9e3b..d1d6b6c 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -568,6 +568,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 1563996..044de53 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -560,6 +560,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m -From b27eece1cbc6981540739b849572a58ffd34aefb Mon Sep 17 00:00:00 2001 +From 0ccf9782b6eb86e532bc7dc3b82de29729719219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:39:30 +0100 -Subject: [PATCH 170/172] dts: overlay: add support for MZ61581 display +Subject: [PATCH 095/104] dts: overlay: add support for MZ61581 display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154525,10 +128356,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/mz61581-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index ed45f81..f918523 100644 +index 3988c44..371d34a 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -77,6 +77,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb +@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb @@ -154652,10 +128483,10 @@ index 0000000..c06fe12 + }; +}; -From e5c5c9e51bb483f34f0210f0406a3cf9597f77d0 Mon Sep 17 00:00:00 2001 +From 6c94db64884db6e86207ebc62acd815154b79bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:40:07 +0100 -Subject: [PATCH 171/172] dts: overlay: piscreen: set speed to 24MHz +Subject: [PATCH 096/104] dts: overlay: piscreen: set speed to 24MHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154682,10 +128513,10 @@ index 8cd6a95..b7fd7ea 100644 bgr; fps = <30>; -From a1c1709f994bde15ce55aac74de0dcb40761fc7b Mon Sep 17 00:00:00 2001 +From f6e5fb7511f69e49729399b5cc30e84710297b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:40:43 +0100 -Subject: [PATCH 172/172] dts: overlay: rpi-display: pullup irq gpio +Subject: [PATCH 097/104] dts: overlay: rpi-display: pullup irq gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154710,3 +128541,3535 @@ index 0578810..a8fa974 100644 }; }; }; + +From f85415a5136808a71dd215aed3656f378a7df9c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 14:48:30 +0100 +Subject: [PATCH 098/104] BCM270x_DT: add bcm2835-dma entry +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Device Tree entry for bcm2835-dma. +The entry doesn't contain any resources since they are handled +by the arch/arm/mach-bcm270x/dma.c driver. +In non-DT mode, don't add the device in the board file. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/boot/dts/bcm2708.dtsi | 5 +++++ + arch/arm/boot/dts/bcm2709.dtsi | 5 +++++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + arch/arm/mach-bcm2709/bcm2709.c | 2 +- + 4 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 0713a1ed..846f148 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -17,6 +17,11 @@ + #size-cells = <1>; + ranges = <0x7e000000 0x20000000 0x01000000>; + ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ + intc: interrupt-controller { + compatible = "brcm,bcm2708-armctrl-ic"; + reg = <0x7e00b200 0x200>; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index 34d2226..d468363 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -17,6 +17,11 @@ + #size-cells = <1>; + ranges = <0x7e000000 0x3f000000 0x01000000>; + ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ + intc: interrupt-controller { + compatible = "brcm,bcm2708-armctrl-ic"; + reg = <0x7e00b200 0x200>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index c8aea5b..6a08e95 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -857,7 +857,7 @@ void __init bcm2708_init(void) + bcm2708_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); +- bcm_register_device(&bcm2708_dmaengine_device); ++ bcm_register_device_dt(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index e3882f3..ae60973 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -879,7 +879,7 @@ void __init bcm2709_init(void) + bcm2709_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); +- bcm_register_device(&bcm2708_dmaengine_device); ++ bcm_register_device_dt(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); + +From a493226d7c7781bb49bbaaadf4180beadd5bbeeb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 14:50:19 +0100 +Subject: [PATCH 099/104] bcm270x: add mmc-bcm2835 clock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add clock for the mmc-bcm2835.0 device. +Will be used in non-DT mode. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/mach-bcm2708/bcm2708.c | 1 + + arch/arm/mach-bcm2709/bcm2709.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 6a08e95..b5f22e2 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -233,6 +233,7 @@ void __init bcm2708_init_clocks(void) + bcm2708_register_clkdev(clk, "dev:f1"); + + clk = bcm2708_clk_register("sdhost_clk", 250000000); ++ bcm2708_register_clkdev(clk, "mmc-bcm2835.0"); + bcm2708_register_clkdev(clk, "bcm2708_spi.0"); + bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); + bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index ae60973..a327e74 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -243,6 +243,7 @@ void __init bcm2709_init_clocks(void) + bcm2709_register_clkdev(clk, "dev:f1"); + + clk = bcm2709_clk_register("sdhost_clk", 250000000); ++ bcm2709_register_clkdev(clk, "mmc-bcm2835.0"); + bcm2709_register_clkdev(clk, "bcm2708_spi.0"); + bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); + bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); + +From 5b3a82514aa4e5c58846329cd581b187a4b32731 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 15:12:35 +0100 +Subject: [PATCH 100/104] BCM270x_DT: add bcm2835-mmc entry +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Device Tree entry for bcm2835-mmc. +In non-DT mode, don't add the device in the board file. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2708.dtsi | 19 +++++++++++++++++++ + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2709.dtsi | 19 +++++++++++++++++++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + arch/arm/mach-bcm2709/bcm2709.c | 2 +- + 7 files changed, 55 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +index 7f84473..f25563a 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +index a39562f..17b4b8c 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 846f148..42edda5 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -41,6 +41,17 @@ + #interrupt-cells = <2>; + }; + ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + i2s: i2s@7e203000 { + compatible = "brcm,bcm2708-i2s"; + reg = <0x7e203000 0x20>, +@@ -96,6 +107,14 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ + clk_i2c: i2c { + compatible = "fixed-clock"; + reg = <1>; +diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +index 75c222f..c73249b 100644 +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index d468363..bd6605d 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -41,6 +41,17 @@ + #interrupt-cells = <2>; + }; + ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + i2s: i2s@7e203000 { + compatible = "brcm,bcm2708-i2s"; + reg = <0x7e203000 0x20>, +@@ -97,6 +108,14 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ + clk_i2c: i2c { + compatible = "fixed-clock"; + reg = <1>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index b5f22e2..16ba248 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -885,7 +885,7 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_powerman_device); + + #ifdef CONFIG_MMC_BCM2835 +- bcm_register_device(&bcm2835_emmc_device); ++ bcm_register_device_dt(&bcm2835_emmc_device); + #endif + bcm2708_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index a327e74..aa91259 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -909,7 +909,7 @@ void __init bcm2709_init(void) + bcm_register_device(&bcm2708_powerman_device); + + #ifdef CONFIG_MMC_BCM2835 +- bcm_register_device(&bcm2835_emmc_device); ++ bcm_register_device_dt(&bcm2835_emmc_device); + #endif + bcm2709_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + +From 0fb4c9b7d54a6dd6208b64b5b3f8199e92ec8e51 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 27 Mar 2015 11:45:44 +0000 +Subject: [PATCH 101/104] BCM270x_DT: Refactor bcm2708.dtsi and bcm2709.dtsi + +Extract the common elements, creating bcm2708_common.dtsi +--- + arch/arm/boot/dts/bcm2708.dtsi | 118 +-------------------------------- + arch/arm/boot/dts/bcm2708_common.dtsi | 120 ++++++++++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 118 +-------------------------------- + 3 files changed, 124 insertions(+), 232 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm2708_common.dtsi + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 42edda5..0d47427 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -1,133 +1,19 @@ +-/include/ "skeleton.dtsi" ++/include/ "bcm2708_common.dtsi" + + / { + compatible = "brcm,bcm2708"; + model = "BCM2708"; + +- interrupt-parent = <&intc>; +- + chosen { + /* No padding required - the boot loader can do that. */ + bootargs = ""; + }; + +- soc: soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; ++ soc { + ranges = <0x7e000000 0x20000000 0x01000000>; + +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- #dma-cells = <1>; +- }; +- +- intc: interrupt-controller { +- compatible = "brcm,bcm2708-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gpio: gpio { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- interrupts = <2 17>, <2 18>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- mmc: mmc@7e300000 { +- compatible = "brcm,bcm2835-mmc"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clk_mmc>; +- dmas = <&dma 11>, +- <&dma 11>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2708-i2s"; +- reg = <0x7e203000 0x20>, +- <0x7e101098 0x02>; +- +- //dmas = <&dma 2>, +- // <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi0: spi@7e204000 { +- compatible = "brcm,bcm2708-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clk_spi>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- leds: leds { +- compatible = "gpio-leds"; +- }; +- + arm-pmu { + compatible = "arm,arm1176-pmu"; + }; + }; +- +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- clk_mmc: clock@0 { +- compatible = "fixed-clock"; +- reg = <0>; +- #clock-cells = <0>; +- clock-output-names = "mmc"; +- clock-frequency = <250000000>; +- }; +- +- clk_i2c: i2c { +- compatible = "fixed-clock"; +- reg = <1>; +- #clock-cells = <0>; +- clock-frequency = <250000000>; +- }; +- +- clk_spi: clock@2 { +- compatible = "fixed-clock"; +- reg = <2>; +- #clock-cells = <0>; +- clock-output-names = "spi"; +- clock-frequency = <250000000>; +- }; +- }; + }; +diff --git a/arch/arm/boot/dts/bcm2708_common.dtsi b/arch/arm/boot/dts/bcm2708_common.dtsi +new file mode 100644 +index 0000000..ff70c58 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -0,0 +1,120 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ interrupt-parent = <&intc>; ++ ++ soc: soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ leds: leds { ++ compatible = "gpio-leds"; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index bd6605d..5e0b935 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -1,137 +1,23 @@ +-/include/ "skeleton.dtsi" ++/include/ "bcm2708_common.dtsi" + + / { + compatible = "brcm,bcm2709"; + model = "BCM2709"; + +- interrupt-parent = <&intc>; +- + chosen { + /* No padding required - the boot loader can do that. */ + bootargs = ""; + }; + +- soc: soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; ++ soc { + ranges = <0x7e000000 0x3f000000 0x01000000>; + +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- #dma-cells = <1>; +- }; +- +- intc: interrupt-controller { +- compatible = "brcm,bcm2708-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gpio: gpio { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- interrupts = <2 17>, <2 18>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- mmc: mmc@7e300000 { +- compatible = "brcm,bcm2835-mmc"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clk_mmc>; +- dmas = <&dma 11>, +- <&dma 11>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2708-i2s"; +- reg = <0x7e203000 0x20>, +- <0x7e101098 0x02>; +- +- //dmas = <&dma 2>, +- // <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi0: spi@7e204000 { +- compatible = "brcm,bcm2708-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clk_spi>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- leds: leds { +- compatible = "gpio-leds"; +- }; +- + arm-pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = <3 9>; + }; + }; + +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- clk_mmc: clock@0 { +- compatible = "fixed-clock"; +- reg = <0>; +- #clock-cells = <0>; +- clock-output-names = "mmc"; +- clock-frequency = <250000000>; +- }; +- +- clk_i2c: i2c { +- compatible = "fixed-clock"; +- reg = <1>; +- #clock-cells = <0>; +- clock-frequency = <250000000>; +- }; +- +- clk_spi: clock@2 { +- compatible = "fixed-clock"; +- reg = <2>; +- #clock-cells = <0>; +- clock-output-names = "spi"; +- clock-frequency = <250000000>; +- }; +- }; +- + timer { + compatible = "arm,armv7-timer"; + clock-frequency = <19200000>; + +From 9a8b27b914735cbf8b886591160b821aacee3920 Mon Sep 17 00:00:00 2001 +From: Waldemar Brodkorb +Date: Wed, 25 Mar 2015 09:26:17 +0100 +Subject: [PATCH 102/104] Add driver for rpi-proto + +Forward port of 3.10.x driver from https://github.com/koalo +We are using a custom board and would like to use rpi 3.18.x +kernel. Patch works fine for our embedded system. + +URL to the audio chip: +http://www.mikroe.com/add-on-boards/audio-voice/audio-codec-proto/ + +Playback tested with devicetree enabled. + +Signed-off-by: Waldemar Brodkorb +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/rpi-proto-overlay.dts | 39 ++++++++ + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-proto.c | 153 ++++++++++++++++++++++++++++++++ + 5 files changed, 202 insertions(+) + create mode 100644 arch/arm/boot/dts/rpi-proto-overlay.dts + create mode 100644 sound/soc/bcm/rpi-proto.c + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 371d34a..eda0009 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -24,6 +24,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb +diff --git a/arch/arm/boot/dts/rpi-proto-overlay.dts b/arch/arm/boot/dts/rpi-proto-overlay.dts +new file mode 100644 +index 0000000..2029930 +--- /dev/null ++++ b/arch/arm/boot/dts/rpi-proto-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for Rpi-Proto ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "rpi,rpi-proto"; ++ i2s-controller = <&i2s>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ wm8731@1a { ++ #sound-dai-cells = <0>; ++ compatible = "wlf,wm8731"; ++ reg = <0x1a>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 40d27c1..003ae28 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -54,6 +54,13 @@ config SND_BCM2708_SOC_RPI_DAC + help + Say Y or M if you want to add support for RPi-DAC. + ++config SND_BCM2708_SOC_RPI_PROTO ++ tristate "Support for Rpi-PROTO" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_WM8731 ++ help ++ Say Y or M if you want to add support for Audio Codec Board PROTO (WM8731). ++ + config SND_BCM2708_SOC_IQAUDIO_DAC + tristate "Support for IQaudIO-DAC" + depends on SND_BCM2708_SOC_I2S +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 17ea2b0..883241b 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -14,6 +14,7 @@ snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o + snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-hifiberry-amp-objs := hifiberry_amp.o + snd-soc-rpi-dac-objs := rpi-dac.o ++snd-soc-rpi-proto-objs := rpi-proto.o + snd-soc-iqaudio-dac-objs := iqaudio-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o +@@ -21,4 +22,5 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o + obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o +diff --git a/sound/soc/bcm/rpi-proto.c b/sound/soc/bcm/rpi-proto.c +new file mode 100644 +index 0000000..c6e45a0 +--- /dev/null ++++ b/sound/soc/bcm/rpi-proto.c +@@ -0,0 +1,153 @@ ++/* ++ * ASoC driver for PROTO AudioCODEC (with a WM8731) ++ * connected to a Raspberry Pi ++ * ++ * Author: Florian Meier, ++ * Copyright 2013 ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++ ++#include "../codecs/wm8731.h" ++ ++static const unsigned int wm8731_rates_12288000[] = { ++ 8000, 32000, 48000, 96000, ++}; ++ ++static struct snd_pcm_hw_constraint_list wm8731_constraints_12288000 = { ++ .list = wm8731_rates_12288000, ++ .count = ARRAY_SIZE(wm8731_rates_12288000), ++}; ++ ++static int snd_rpi_proto_startup(struct snd_pcm_substream *substream) ++{ ++ /* Setup constraints, because there is a 12.288 MHz XTAL on the board */ ++ snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &wm8731_constraints_12288000); ++ return 0; ++} ++ ++static int snd_rpi_proto_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *codec_dai = rtd->codec_dai; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ int sysclk = 12288000; /* This is fixed on this board */ ++ ++ /* Set proto bclk */ ++ int ret = snd_soc_dai_set_bclk_ratio(cpu_dai,32*2); ++ if (ret < 0){ ++ dev_err(codec->dev, ++ "Failed to set BCLK ratio %d\n", ret); ++ return ret; ++ } ++ ++ /* Set proto sysclk */ ++ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, ++ sysclk, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ dev_err(codec->dev, ++ "Failed to set WM8731 SYSCLK: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_proto_ops = { ++ .startup = snd_rpi_proto_startup, ++ .hw_params = snd_rpi_proto_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_proto_dai[] = { ++{ ++ .name = "WM8731", ++ .stream_name = "WM8731 HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "wm8731-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "wm8731.1-001a", ++ .dai_fmt = SND_SOC_DAIFMT_I2S ++ | SND_SOC_DAIFMT_NB_NF ++ | SND_SOC_DAIFMT_CBM_CFM, ++ .ops = &snd_rpi_proto_ops, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_proto = { ++ .name = "snd_rpi_proto", ++ .dai_link = snd_rpi_proto_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_proto_dai), ++}; ++ ++static int snd_rpi_proto_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_proto.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_proto_dai[0]; ++ i2s_node = of_parse_phandle(pdev->dev.of_node, ++ "i2s-controller", 0); ++ ++ if (i2s_node) { ++ dai->cpu_dai_name = NULL; ++ dai->cpu_of_node = i2s_node; ++ dai->platform_name = NULL; ++ dai->platform_of_node = i2s_node; ++ } ++ } ++ ++ ret = snd_soc_register_card(&snd_rpi_proto); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ } ++ ++ return ret; ++} ++ ++ ++static int snd_rpi_proto_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_proto); ++} ++ ++static const struct of_device_id snd_rpi_proto_of_match[] = { ++ { .compatible = "rpi,rpi-proto", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_proto_of_match); ++ ++static struct platform_driver snd_rpi_proto_driver = { ++ .driver = { ++ .name = "snd-rpi-proto", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_proto_of_match, ++ }, ++ .probe = snd_rpi_proto_probe, ++ .remove = snd_rpi_proto_remove, ++}; ++ ++module_platform_driver(snd_rpi_proto_driver); ++ ++MODULE_AUTHOR("Florian Meier"); ++MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to PROTO board (WM8731)"); ++MODULE_LICENSE("GPL"); + +From 2615f067017adc52048591f3dd1659647555a92a Mon Sep 17 00:00:00 2001 +From: Clive Messer +Date: Thu, 2 Apr 2015 12:22:55 +0100 +Subject: [PATCH 103/104] Add Device Tree support for RPi-DAC. + +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/rpi-dac-overlay.dts | 34 ++++++++++++++++++++++++++++++++++ + sound/soc/bcm/rpi-dac.c | 21 +++++++++++++++++++++ + sound/soc/codecs/pcm1794a.c | 7 +++++++ + 4 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/rpi-dac-overlay.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index eda0009..2b8c692a 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -24,6 +24,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb +diff --git a/arch/arm/boot/dts/rpi-dac-overlay.dts b/arch/arm/boot/dts/rpi-dac-overlay.dts +new file mode 100644 +index 0000000..7fc6ac9 +--- /dev/null ++++ b/arch/arm/boot/dts/rpi-dac-overlay.dts +@@ -0,0 +1,34 @@ ++// Definitions for RPi DAC ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "rpi,rpi-dac"; ++ i2s-controller = <&i2s>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ pcm1794a-codec { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm1794a"; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c +index 6d6e0ba..d5fac1b 100644 +--- a/sound/soc/bcm/rpi-dac.c ++++ b/sound/soc/bcm/rpi-dac.c +@@ -69,6 +69,20 @@ static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) + int ret = 0; + + snd_rpi_rpi_dac.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_rpi_dac_dai[0]; ++ i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0); ++ ++ if (i2s_node) { ++ dai->cpu_dai_name = NULL; ++ dai->cpu_of_node = i2s_node; ++ dai->platform_name = NULL; ++ dai->platform_of_node = i2s_node; ++ } ++ } ++ + ret = snd_soc_register_card(&snd_rpi_rpi_dac); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); +@@ -81,10 +95,17 @@ static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) + return snd_soc_unregister_card(&snd_rpi_rpi_dac); + } + ++static const struct of_device_id snd_rpi_rpi_dac_of_match[] = { ++ { .compatible = "rpi,rpi-dac", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_rpi_dac_of_match); ++ + static struct platform_driver snd_rpi_rpi_dac_driver = { + .driver = { + .name = "snd-rpi-dac", + .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_rpi_dac_of_match, + }, + .probe = snd_rpi_rpi_dac_probe, + .remove = snd_rpi_rpi_dac_remove, +diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c +index b4eaa44..afe1b41 100644 +--- a/sound/soc/codecs/pcm1794a.c ++++ b/sound/soc/codecs/pcm1794a.c +@@ -46,12 +46,19 @@ static int pcm1794a_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id pcm1794a_of_match[] = { ++ { .compatible = "ti,pcm1794a", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, pcm1794a_of_match); ++ + static struct platform_driver pcm1794a_codec_driver = { + .probe = pcm1794a_probe, + .remove = pcm1794a_remove, + .driver = { + .name = "pcm1794a-codec", + .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(pcm1794a_of_match), + }, + }; + + +From 15eb32f3eaecad4cba2432e3ee339ef5f04f62f5 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 17:16:29 +0100 +Subject: [PATCH 104/104] config: Add default configs + +--- + arch/arm/configs/bcm2709_defconfig | 1192 ++++++++++++++++++++++++++++++++++++ + arch/arm/configs/bcmrpi_defconfig | 1187 +++++++++++++++++++++++++++++++++++ + 2 files changed, 2379 insertions(+) + create mode 100644 arch/arm/configs/bcm2709_defconfig + create mode 100644 arch/arm/configs/bcmrpi_defconfig + +diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig +new file mode 100644 +index 0000000..23079b4 +--- /dev/null ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -0,0 +1,1192 @@ ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0 ++CONFIG_LOCALVERSION="-v7" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_IKCONFIG=m ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2709=y ++CONFIG_BCM2709_DT=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_SMP=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=m ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_BINFMT_MISC=m ++# CONFIG_SUSPEND is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++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_INET_AH=m ++CONFIG_INET_ESP=m ++CONFIG_INET_IPCOMP=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=m ++CONFIG_INET_XFRM_MODE_TUNNEL=m ++CONFIG_INET_XFRM_MODE_BEET=m ++CONFIG_INET_LRO=m ++CONFIG_INET_DIAG=m ++CONFIG_INET6_AH=m ++CONFIG_INET6_ESP=m ++CONFIG_INET6_IPCOMP=m ++CONFIG_IPV6_TUNNEL=m ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y ++CONFIG_NETFILTER=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_ZONES=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m ++CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 ++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_ESP=m ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_PROTO_ESP=y ++CONFIG_IP_VS_PROTO_AH=y ++CONFIG_IP_VS_PROTO_SCTP=y ++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 ++CONFIG_IP_VS_FTP=m ++CONFIG_IP_VS_PE_SIP=m ++CONFIG_NF_CONNTRACK_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_MATCH_AH=m ++CONFIG_IP_NF_MATCH_ECN=m ++CONFIG_IP_NF_MATCH_TTL=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=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 ++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_RT=m ++CONFIG_IP6_NF_TARGET_HL=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y ++CONFIG_ATM=m ++CONFIG_L2TP=m ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=m ++CONFIG_L2TP_ETH=m ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=m ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_ATALK=m ++CONFIG_6LOWPAN=m ++CONFIG_NET_SCHED=y ++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 ++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_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_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_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_BATMAN_ADV=m ++CONFIG_OPENVSWITCH=m ++CONFIG_NET_PKTGEN=m ++CONFIG_HAMRADIO=y ++CONFIG_AX25=m ++CONFIG_NETROM=m ++CONFIG_ROSE=m ++CONFIG_MKISS=m ++CONFIG_6PACK=m ++CONFIG_BPQETHER=m ++CONFIG_BAYCOM_SER_FDX=m ++CONFIG_BAYCOM_SER_HDX=m ++CONFIG_YAM=m ++CONFIG_CAN=m ++CONFIG_CAN_VCAN=m ++CONFIG_CAN_MCP251X=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++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 ++CONFIG_BT_6LOWPAN=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_BT_WILINK=m ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=m ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_RFKILL=m ++CONFIG_RFKILL_INPUT=y ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=5 ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_DRBD=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_ATA_OVER_ETH=m ++CONFIG_EEPROM_AT24=m ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=m ++CONFIG_CHR_DEV_SG=m ++CONFIG_SCSI_ISCSI_ATTRS=y ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m ++CONFIG_MD=y ++CONFIG_MD_LINEAR=m ++CONFIG_MD_RAID0=m ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_CRYPT=m ++CONFIG_DM_SNAPSHOT=m ++CONFIG_DM_MIRROR=m ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_RAID=m ++CONFIG_DM_ZERO=m ++CONFIG_DM_DELAY=m ++CONFIG_NETDEVICES=y ++CONFIG_BONDING=m ++CONFIG_DUMMY=m ++CONFIG_IFB=m ++CONFIG_MACVLAN=m ++CONFIG_NETCONSOLE=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++CONFIG_ENC28J60=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPPOATM=m ++CONFIG_PPPOE=m ++CONFIG_PPPOL2TP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=y ++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=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_CARDS=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_USB=m ++CONFIG_AR5523=m ++CONFIG_B43=m ++# CONFIG_B43_PHY_N is not set ++CONFIG_B43LEGACY=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_USB=y ++CONFIG_HOSTAP=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT3573=y ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RT2800USB_RT55XX=y ++CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_JOYSTICK_IFORCE=m ++CONFIG_JOYSTICK_IFORCE_USB=y ++CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m ++CONFIG_TOUCHSCREEN_STMPE=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2708=m ++CONFIG_RAW_DRIVER=y ++CONFIG_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VC_SM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2708=m ++CONFIG_SPI_SPIDEV=y ++CONFIG_PPS=m ++CONFIG_PPS_CLIENT_LDISC=m ++CONFIG_PPS_CLIENT_GPIO=m ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_ARIZONA=m ++CONFIG_GPIO_STMPE=y ++CONFIG_W1=m ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2406=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++CONFIG_BATTERY_DS2760=m ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++CONFIG_UCB1400_CORE=m ++CONFIG_MFD_STMPE=y ++CONFIG_STMPE_SPI=y ++CONFIG_MFD_ARIZONA_I2C=m ++CONFIG_MFD_ARIZONA_SPI=m ++CONFIG_MFD_WM5102=y ++CONFIG_MEDIA_SUPPORT=m ++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_LIRC=m ++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_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m ++CONFIG_USB_ZR364XX=m ++CONFIG_USB_STKWEBCAM=m ++CONFIG_USB_S2255=m ++CONFIG_VIDEO_USBTV=m ++CONFIG_VIDEO_PVRUSB2=m ++CONFIG_VIDEO_HDPVR=m ++CONFIG_VIDEO_USBVISION=m ++CONFIG_VIDEO_STK1160_COMMON=m ++CONFIG_VIDEO_STK1160_AC97=y ++CONFIG_VIDEO_GO7007=m ++CONFIG_VIDEO_GO7007_USB=m ++CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m ++CONFIG_VIDEO_AU0828=m ++CONFIG_VIDEO_AU0828_RC=y ++CONFIG_VIDEO_CX231XX=m ++CONFIG_VIDEO_CX231XX_ALSA=m ++CONFIG_VIDEO_CX231XX_DVB=m ++CONFIG_VIDEO_TM6000=m ++CONFIG_VIDEO_TM6000_ALSA=m ++CONFIG_VIDEO_TM6000_DVB=m ++CONFIG_DVB_USB=m ++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_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_AS102=m ++CONFIG_VIDEO_EM28XX=m ++CONFIG_VIDEO_EM28XX_V4L2=m ++CONFIG_VIDEO_EM28XX_ALSA=m ++CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_BCM2835=y ++CONFIG_VIDEO_BCM2835_MMAL=m ++CONFIG_RADIO_SI470X=y ++CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m ++CONFIG_RADIO_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_MA901=m ++CONFIG_RADIO_TEA5764=m ++CONFIG_RADIO_SAA7706H=m ++CONFIG_RADIO_TEF6862=m ++CONFIG_RADIO_WL1273=m ++CONFIG_RADIO_WL128X=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_VIDEO_UDA1342=m ++CONFIG_VIDEO_SONY_BTF_MPX=m ++CONFIG_VIDEO_TVP5150=m ++CONFIG_VIDEO_TW2804=m ++CONFIG_VIDEO_TW9903=m ++CONFIG_VIDEO_TW9906=m ++CONFIG_VIDEO_OV7640=m ++CONFIG_VIDEO_MT9V011=m ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_GPIO=m ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_SOC=m ++CONFIG_SND_BCM2708_SOC_I2S=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m ++CONFIG_SND_BCM2708_SOC_RPI_DAC=m ++CONFIG_SND_BCM2708_SOC_RPI_PROTO=m ++CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_ELO=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++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_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THINGM=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_XINMO=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_PRINTER=m ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++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 ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USBIP_CORE=m ++CONFIG_USBIP_VHCI_HCD=m ++CONFIG_USBIP_HOST=m ++CONFIG_USB_SERIAL=m ++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_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_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_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_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_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 ++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=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_USB_ATM=m ++CONFIG_USB_SPEEDTOUCH=m ++CONFIG_USB_CXACRU=m ++CONFIG_USB_UEAGLEATM=m ++CONFIG_USB_XUSBATM=m ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SPI=m ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=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=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_TRIGGER_INPUT=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_HCTOSYS is not set ++CONFIG_RTC_DRV_DS1307=m ++CONFIG_RTC_DRV_DS1374=m ++CONFIG_RTC_DRV_DS1672=m ++CONFIG_RTC_DRV_DS3232=m ++CONFIG_RTC_DRV_MAX6900=m ++CONFIG_RTC_DRV_RS5C372=m ++CONFIG_RTC_DRV_ISL1208=m ++CONFIG_RTC_DRV_ISL12022=m ++CONFIG_RTC_DRV_ISL12057=m ++CONFIG_RTC_DRV_X1205=m ++CONFIG_RTC_DRV_PCF2127=m ++CONFIG_RTC_DRV_PCF8523=m ++CONFIG_RTC_DRV_PCF8563=m ++CONFIG_RTC_DRV_PCF8583=m ++CONFIG_RTC_DRV_M41T80=m ++CONFIG_RTC_DRV_BQ32K=m ++CONFIG_RTC_DRV_S35390A=m ++CONFIG_RTC_DRV_FM3130=m ++CONFIG_RTC_DRV_RX8581=m ++CONFIG_RTC_DRV_RX8025=m ++CONFIG_RTC_DRV_EM3027=m ++CONFIG_RTC_DRV_RV3029C2=m ++CONFIG_RTC_DRV_M41T93=m ++CONFIG_RTC_DRV_M41T94=m ++CONFIG_RTC_DRV_DS1305=m ++CONFIG_RTC_DRV_DS1390=m ++CONFIG_RTC_DRV_MAX6902=m ++CONFIG_RTC_DRV_R9701=m ++CONFIG_RTC_DRV_RS5C348=m ++CONFIG_RTC_DRV_DS3234=m ++CONFIG_RTC_DRV_PCF2123=m ++CONFIG_RTC_DRV_RX4581=m ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2708=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV_GENIRQ=m ++CONFIG_STAGING=y ++CONFIG_PRISM2_USB=m ++CONFIG_R8712U=m ++CONFIG_R8188EU=m ++CONFIG_R8723AU=m ++CONFIG_VT6656=m ++CONFIG_SPEAKUP=m ++CONFIG_SPEAKUP_SYNTH_SOFT=m ++CONFIG_STAGING_MEDIA=y ++CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_IMON=m ++CONFIG_LIRC_RPI=m ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SERIAL=m ++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_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++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_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_NTFS_RW=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_F2FS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_SWAP=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_NFSD=m ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_NLS_DEFAULT="utf8" ++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=y ++CONFIG_NLS_ISO8859_1=m ++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_DLM=m ++CONFIG_PRINTK_TIME=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_PREEMPT is not set ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++# CONFIG_KPROBE_EVENT is not set ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=m ++CONFIG_CRYPTO_XTS=m ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_SHA1_ARM_NEON=m ++CONFIG_CRYPTO_SHA512_ARM_NEON=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_AES_ARM_BS=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +new file mode 100644 +index 0000000..919e0b7 +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -0,0 +1,1187 @@ ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0 ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_IKCONFIG=m ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2708=y ++CONFIG_BCM2708_DT=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=m ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_VFP=y ++CONFIG_BINFMT_MISC=m ++# CONFIG_SUSPEND is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++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_INET_AH=m ++CONFIG_INET_ESP=m ++CONFIG_INET_IPCOMP=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=m ++CONFIG_INET_XFRM_MODE_TUNNEL=m ++CONFIG_INET_XFRM_MODE_BEET=m ++CONFIG_INET_LRO=m ++CONFIG_INET_DIAG=m ++CONFIG_INET6_AH=m ++CONFIG_INET6_ESP=m ++CONFIG_INET6_IPCOMP=m ++CONFIG_IPV6_TUNNEL=m ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y ++CONFIG_NETFILTER=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_ZONES=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m ++CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 ++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_ESP=m ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_PROTO_ESP=y ++CONFIG_IP_VS_PROTO_AH=y ++CONFIG_IP_VS_PROTO_SCTP=y ++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 ++CONFIG_IP_VS_FTP=m ++CONFIG_IP_VS_PE_SIP=m ++CONFIG_NF_CONNTRACK_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_MATCH_AH=m ++CONFIG_IP_NF_MATCH_ECN=m ++CONFIG_IP_NF_MATCH_TTL=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=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 ++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_RT=m ++CONFIG_IP6_NF_TARGET_HL=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y ++CONFIG_ATM=m ++CONFIG_L2TP=m ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=m ++CONFIG_L2TP_ETH=m ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=m ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_ATALK=m ++CONFIG_6LOWPAN=m ++CONFIG_NET_SCHED=y ++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 ++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_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_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_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_BATMAN_ADV=m ++CONFIG_OPENVSWITCH=m ++CONFIG_NET_PKTGEN=m ++CONFIG_HAMRADIO=y ++CONFIG_AX25=m ++CONFIG_NETROM=m ++CONFIG_ROSE=m ++CONFIG_MKISS=m ++CONFIG_6PACK=m ++CONFIG_BPQETHER=m ++CONFIG_BAYCOM_SER_FDX=m ++CONFIG_BAYCOM_SER_HDX=m ++CONFIG_YAM=m ++CONFIG_CAN=m ++CONFIG_CAN_VCAN=m ++CONFIG_CAN_MCP251X=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++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 ++CONFIG_BT_6LOWPAN=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_BT_WILINK=m ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=m ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_RFKILL=m ++CONFIG_RFKILL_INPUT=y ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=5 ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_DRBD=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_ATA_OVER_ETH=m ++CONFIG_EEPROM_AT24=m ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=m ++CONFIG_CHR_DEV_SG=m ++CONFIG_SCSI_ISCSI_ATTRS=y ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m ++CONFIG_MD=y ++CONFIG_MD_LINEAR=m ++CONFIG_MD_RAID0=m ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_CRYPT=m ++CONFIG_DM_SNAPSHOT=m ++CONFIG_DM_MIRROR=m ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_RAID=m ++CONFIG_DM_ZERO=m ++CONFIG_DM_DELAY=m ++CONFIG_NETDEVICES=y ++CONFIG_BONDING=m ++CONFIG_DUMMY=m ++CONFIG_IFB=m ++CONFIG_MACVLAN=m ++CONFIG_NETCONSOLE=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++CONFIG_ENC28J60=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPPOATM=m ++CONFIG_PPPOE=m ++CONFIG_PPPOL2TP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=y ++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=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_CARDS=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_USB=m ++CONFIG_AR5523=m ++CONFIG_B43=m ++# CONFIG_B43_PHY_N is not set ++CONFIG_B43LEGACY=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_USB=y ++CONFIG_HOSTAP=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT3573=y ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RT2800USB_RT55XX=y ++CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_JOYSTICK_IFORCE=m ++CONFIG_JOYSTICK_IFORCE_USB=y ++CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m ++CONFIG_TOUCHSCREEN_STMPE=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2708=m ++CONFIG_RAW_DRIVER=y ++CONFIG_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VC_SM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2708=m ++CONFIG_SPI_SPIDEV=y ++CONFIG_PPS=m ++CONFIG_PPS_CLIENT_LDISC=m ++CONFIG_PPS_CLIENT_GPIO=m ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_ARIZONA=m ++CONFIG_GPIO_STMPE=y ++CONFIG_W1=m ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2406=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++CONFIG_BATTERY_DS2760=m ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++CONFIG_UCB1400_CORE=m ++CONFIG_MFD_STMPE=y ++CONFIG_STMPE_SPI=y ++CONFIG_MFD_ARIZONA_I2C=m ++CONFIG_MFD_ARIZONA_SPI=m ++CONFIG_MFD_WM5102=y ++CONFIG_MEDIA_SUPPORT=m ++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_LIRC=m ++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_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m ++CONFIG_USB_ZR364XX=m ++CONFIG_USB_STKWEBCAM=m ++CONFIG_USB_S2255=m ++CONFIG_VIDEO_USBTV=m ++CONFIG_VIDEO_PVRUSB2=m ++CONFIG_VIDEO_HDPVR=m ++CONFIG_VIDEO_USBVISION=m ++CONFIG_VIDEO_STK1160_COMMON=m ++CONFIG_VIDEO_STK1160_AC97=y ++CONFIG_VIDEO_GO7007=m ++CONFIG_VIDEO_GO7007_USB=m ++CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m ++CONFIG_VIDEO_AU0828=m ++CONFIG_VIDEO_AU0828_RC=y ++CONFIG_VIDEO_CX231XX=m ++CONFIG_VIDEO_CX231XX_ALSA=m ++CONFIG_VIDEO_CX231XX_DVB=m ++CONFIG_VIDEO_TM6000=m ++CONFIG_VIDEO_TM6000_ALSA=m ++CONFIG_VIDEO_TM6000_DVB=m ++CONFIG_DVB_USB=m ++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_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_AS102=m ++CONFIG_VIDEO_EM28XX=m ++CONFIG_VIDEO_EM28XX_V4L2=m ++CONFIG_VIDEO_EM28XX_ALSA=m ++CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_BCM2835=y ++CONFIG_VIDEO_BCM2835_MMAL=m ++CONFIG_RADIO_SI470X=y ++CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m ++CONFIG_RADIO_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_MA901=m ++CONFIG_RADIO_TEA5764=m ++CONFIG_RADIO_SAA7706H=m ++CONFIG_RADIO_TEF6862=m ++CONFIG_RADIO_WL1273=m ++CONFIG_RADIO_WL128X=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_VIDEO_UDA1342=m ++CONFIG_VIDEO_SONY_BTF_MPX=m ++CONFIG_VIDEO_TVP5150=m ++CONFIG_VIDEO_TW2804=m ++CONFIG_VIDEO_TW9903=m ++CONFIG_VIDEO_TW9906=m ++CONFIG_VIDEO_OV7640=m ++CONFIG_VIDEO_MT9V011=m ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_GPIO=m ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_SOC=m ++CONFIG_SND_BCM2708_SOC_I2S=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m ++CONFIG_SND_BCM2708_SOC_RPI_DAC=m ++CONFIG_SND_BCM2708_SOC_RPI_PROTO=m ++CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_ELO=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++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_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THINGM=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_XINMO=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_PRINTER=m ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++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 ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USBIP_CORE=m ++CONFIG_USBIP_VHCI_HCD=m ++CONFIG_USBIP_HOST=m ++CONFIG_USB_SERIAL=m ++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_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_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_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_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_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 ++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=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_USB_ATM=m ++CONFIG_USB_SPEEDTOUCH=m ++CONFIG_USB_CXACRU=m ++CONFIG_USB_UEAGLEATM=m ++CONFIG_USB_XUSBATM=m ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SPI=m ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=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=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_TRIGGER_INPUT=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_HCTOSYS is not set ++CONFIG_RTC_DRV_DS1307=m ++CONFIG_RTC_DRV_DS1374=m ++CONFIG_RTC_DRV_DS1672=m ++CONFIG_RTC_DRV_DS3232=m ++CONFIG_RTC_DRV_MAX6900=m ++CONFIG_RTC_DRV_RS5C372=m ++CONFIG_RTC_DRV_ISL1208=m ++CONFIG_RTC_DRV_ISL12022=m ++CONFIG_RTC_DRV_ISL12057=m ++CONFIG_RTC_DRV_X1205=m ++CONFIG_RTC_DRV_PCF2127=m ++CONFIG_RTC_DRV_PCF8523=m ++CONFIG_RTC_DRV_PCF8563=m ++CONFIG_RTC_DRV_PCF8583=m ++CONFIG_RTC_DRV_M41T80=m ++CONFIG_RTC_DRV_BQ32K=m ++CONFIG_RTC_DRV_S35390A=m ++CONFIG_RTC_DRV_FM3130=m ++CONFIG_RTC_DRV_RX8581=m ++CONFIG_RTC_DRV_RX8025=m ++CONFIG_RTC_DRV_EM3027=m ++CONFIG_RTC_DRV_RV3029C2=m ++CONFIG_RTC_DRV_M41T93=m ++CONFIG_RTC_DRV_M41T94=m ++CONFIG_RTC_DRV_DS1305=m ++CONFIG_RTC_DRV_DS1390=m ++CONFIG_RTC_DRV_MAX6902=m ++CONFIG_RTC_DRV_R9701=m ++CONFIG_RTC_DRV_RS5C348=m ++CONFIG_RTC_DRV_DS3234=m ++CONFIG_RTC_DRV_PCF2123=m ++CONFIG_RTC_DRV_RX4581=m ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2708=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV_GENIRQ=m ++CONFIG_STAGING=y ++CONFIG_PRISM2_USB=m ++CONFIG_R8712U=m ++CONFIG_R8188EU=m ++CONFIG_R8723AU=m ++CONFIG_VT6656=m ++CONFIG_SPEAKUP=m ++CONFIG_SPEAKUP_SYNTH_SOFT=m ++CONFIG_STAGING_MEDIA=y ++CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_IMON=m ++CONFIG_LIRC_RPI=m ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SERIAL=m ++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_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++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_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_NTFS_RW=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_F2FS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_SWAP=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_NFSD=m ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_NLS_DEFAULT="utf8" ++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=y ++CONFIG_NLS_ISO8859_1=m ++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_DLM=m ++CONFIG_PRINTK_TIME=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_PREEMPT is not set ++CONFIG_LATENCYTOP=y ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++# CONFIG_KPROBE_EVENT is not set ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_CRYPTD=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=m ++CONFIG_CRYPTO_XTS=m ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_SHA1_ARM=m ++CONFIG_CRYPTO_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_AES_ARM=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y diff --git a/projects/RPi/patches/linux/linux-02-RPi_support.patch b/projects/RPi/patches/linux/linux-02-RPi_support.patch deleted file mode 100644 index da6ce91543..0000000000 --- a/projects/RPi/patches/linux/linux-02-RPi_support.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 9cc28c602914b6b07ad8d7ab4ea146b147b78245 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 8 Apr 2015 18:51:44 +0100 -Subject: [PATCH] vchiq: Remove inline from suspend/resume - ---- - drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -index 3e328ab..0ad9656 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -1776,7 +1776,7 @@ vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) - ** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. - */ - --inline void -+void - set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, - enum vc_suspend_status new_state) - { -@@ -1813,7 +1813,7 @@ set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, - } - } - --inline void -+void - set_resume_state(VCHIQ_ARM_STATE_T *arm_state, - enum vc_resume_status new_state) - { diff --git a/projects/RPi2/linux/linux.arm.conf b/projects/RPi2/linux/linux.arm.conf index 51d2bb19ef..e936b60ca4 100644 --- a/projects/RPi2/linux/linux.arm.conf +++ b/projects/RPi2/linux/linux.arm.conf @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm 3.19.1 Kernel Configuration +# Linux/arm 4.0.0 Kernel Configuration # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y @@ -92,6 +92,7 @@ CONFIG_TICK_CPU_ACCOUNTING=y # RCU Subsystem # CONFIG_TREE_RCU=y +CONFIG_SRCU=y # CONFIG_TASKS_RCU is not set CONFIG_RCU_STALL_COMMON=y # CONFIG_RCU_USER_QS is not set @@ -100,6 +101,7 @@ 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_KTHREAD_PRIO=0 # CONFIG_RCU_NOCB_CPU is not set CONFIG_BUILD_BIN2C=y CONFIG_IKCONFIG=m @@ -131,7 +133,6 @@ CONFIG_RD_GZIP=y # CONFIG_RD_XZ is not set # CONFIG_RD_LZO is not set # CONFIG_RD_LZ4 is not set -# CONFIG_INIT_FALLBACK is not set # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y @@ -183,6 +184,7 @@ 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 @@ -277,6 +279,7 @@ 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 is not set # @@ -381,6 +384,7 @@ CONFIG_ARCH_HAS_BARRIERS=y # # Bus support # +# CONFIG_PCI_DOMAINS_GENERIC is not set # CONFIG_PCI_SYSCALL is not set # CONFIG_PCCARD is not set @@ -843,6 +847,7 @@ CONFIG_BT_RFCOMM_TTY=y # CONFIG_BT_BNEP is not set CONFIG_BT_HIDP=m CONFIG_BT_LE=y +# CONFIG_BT_SELFTEST is not set # # Bluetooth device drivers @@ -956,6 +961,7 @@ 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 @@ -969,7 +975,6 @@ CONFIG_BLK_DEV_NBD=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 -# 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 @@ -1341,6 +1346,7 @@ CONFIG_INPUT_MISC=y # CONFIG_INPUT_AD714X is not set # CONFIG_INPUT_ARIZONA_HAPTICS 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 @@ -1382,6 +1388,7 @@ CONFIG_UNIX98_PTYS=y # 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 # @@ -1410,6 +1417,7 @@ CONFIG_SERIAL_CORE_CONSOLE=y # 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_TTY_PRINTK is not set # CONFIG_HVC_DCC is not set @@ -1448,13 +1456,13 @@ CONFIG_I2C_ALGOBIT=y # # I2C Hardware Bus support # +CONFIG_I2C_BCM2708=y +CONFIG_I2C_BCM2708_BAUDRATE=100000 # # I2C system bus drivers (mostly embedded / system-on-chip) # # CONFIG_I2C_BCM2835 is not set -CONFIG_I2C_BCM2708=y -CONFIG_I2C_BCM2708_BAUDRATE=100000 # CONFIG_I2C_CBUS_GPIO is not set # CONFIG_I2C_DESIGNWARE_PLATFORM is not set CONFIG_I2C_GPIO=y @@ -1619,6 +1627,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_CHARGER_BQ24190 is not set # CONFIG_CHARGER_BQ24735 is not set # CONFIG_CHARGER_SMB347 is not set +# CONFIG_BATTERY_GAUGE_LTC2941 is not set # CONFIG_POWER_RESET is not set # CONFIG_POWER_AVS is not set CONFIG_HWMON=y @@ -1819,6 +1828,7 @@ CONFIG_MFD_CORE=y # 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 @@ -1845,6 +1855,7 @@ CONFIG_MFD_CORE=y # CONFIG_MFD_PCF50633 is not set # CONFIG_UCB1400_CORE is not set # CONFIG_MFD_PM8921_CORE is not set +# CONFIG_MFD_RT5033 is not set CONFIG_MFD_RTSX_USB=y # CONFIG_MFD_RC5T583 is not set # CONFIG_MFD_RK808 is not set @@ -2378,6 +2389,11 @@ CONFIG_SND_USB_CAIAQ_INPUT=y CONFIG_SND_USB_6FIRE=m CONFIG_SND_USB_HIFACE=m CONFIG_SND_BCD2000=m +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=m CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y # CONFIG_SND_ATMEL_SOC is not set @@ -2387,6 +2403,7 @@ CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m CONFIG_SND_BCM2708_SOC_RPI_DAC=m +CONFIG_SND_BCM2708_SOC_RPI_PROTO=m CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m # CONFIG_SND_DESIGNWARE_I2S is not set @@ -2403,6 +2420,7 @@ CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m # CONFIG_SND_SOC_FSL_SPDIF is not set # CONFIG_SND_SOC_FSL_ESAI is not set # CONFIG_SND_SOC_IMX_AUDMUX is not set +# CONFIG_SND_SOC_XTFPGA_I2S is not set CONFIG_SND_SOC_I2C_AND_SPI=m # @@ -2441,6 +2459,7 @@ CONFIG_SND_SOC_PCM5102A=m # CONFIG_SND_SOC_SSM2602_SPI is not set # CONFIG_SND_SOC_SSM2602_I2C is not set # CONFIG_SND_SOC_SSM4567 is not set +CONFIG_SND_SOC_STA32X=m # CONFIG_SND_SOC_STA350 is not set # CONFIG_SND_SOC_TAS2552 is not set # CONFIG_SND_SOC_TAS5086 is not set @@ -2456,7 +2475,7 @@ CONFIG_SND_SOC_TAS5713=m # CONFIG_SND_SOC_WM8580 is not set # CONFIG_SND_SOC_WM8711 is not set # CONFIG_SND_SOC_WM8728 is not set -# CONFIG_SND_SOC_WM8731 is not set +CONFIG_SND_SOC_WM8731=m # CONFIG_SND_SOC_WM8737 is not set # CONFIG_SND_SOC_WM8741 is not set # CONFIG_SND_SOC_WM8750 is not set @@ -2490,6 +2509,7 @@ CONFIG_HID_APPLE=y # CONFIG_HID_APPLEIR is not set CONFIG_HID_AUREAL=y CONFIG_HID_BELKIN=y +# CONFIG_HID_BETOP_FF is not set CONFIG_HID_CHERRY=y CONFIG_HID_CHICONY=y # CONFIG_HID_PRODIKEYS is not set @@ -2595,7 +2615,6 @@ CONFIG_USB_MON=m # CONFIG_USB_EHCI_HCD 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_FUSBH200_HCD is not set # CONFIG_USB_FOTG210_HCD is not set @@ -2649,6 +2668,7 @@ CONFIG_USB_UAS=y # CONFIG_USB_MUSB_HDRC is not set # CONFIG_USB_DWC3 is not set # CONFIG_USB_DWC2 is not set +# CONFIG_USB_ISP1760 is not set # # USB port drivers @@ -2760,15 +2780,14 @@ CONFIG_MMC_BLOCK_BOUNCE=y # # MMC/SD/SDIO Host Controller Drivers # +CONFIG_MMC_BCM2835=y +CONFIG_MMC_BCM2835_DMA=y +CONFIG_MMC_BCM2835_PIO_DMA_BARRIER=2 # CONFIG_MMC_ARMMMCI is not set CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y # CONFIG_MMC_SDHCI_OF_ARASAN is not set -# CONFIG_MMC_SDHCI_PXAV3 is not set -# CONFIG_MMC_SDHCI_PXAV2 is not set -CONFIG_MMC_BCM2835=y -CONFIG_MMC_BCM2835_DMA=y -CONFIG_MMC_BCM2835_PIO_DMA_BARRIER=2 +# CONFIG_MMC_SDHCI_F_SDH30 is not set # CONFIG_MMC_SPI is not set # CONFIG_MMC_DW is not set # CONFIG_MMC_VUB300 is not set @@ -2778,6 +2797,7 @@ CONFIG_MMC_REALTEK_USB=m # CONFIG_MEMSTICK is not set CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y +CONFIG_LEDS_CLASS_FLASH=y # # LED drivers @@ -2844,6 +2864,7 @@ CONFIG_RTC_INTF_DEV=y # # I2C RTC drivers # +# CONFIG_RTC_DRV_ABB5ZES3 is not set CONFIG_RTC_DRV_DS1307=m # CONFIG_RTC_DRV_DS1374 is not set # CONFIG_RTC_DRV_DS1672 is not set @@ -2893,6 +2914,7 @@ CONFIG_RTC_DRV_DS1307=m # 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 @@ -2956,7 +2978,6 @@ CONFIG_STAGING=y # CONFIG_RTLLIB is not set CONFIG_R8712U=m # CONFIG_R8188EU is not set -# CONFIG_LINE6_USB is not set CONFIG_VT6656=m # CONFIG_FT1000 is not set @@ -2964,13 +2985,10 @@ CONFIG_VT6656=m # 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=y -# CONFIG_VIDEO_TLG2300 is not set CONFIG_DVB_MN88472=m CONFIG_DVB_MN88473=m -# CONFIG_VIDEO_SAA7191 is not set CONFIG_LIRC_STAGING=y # CONFIG_LIRC_IMON is not set CONFIG_LIRC_RPI=m @@ -3000,7 +3018,9 @@ CONFIG_COMMON_CLK=y # # CONFIG_COMMON_CLK_SI5351 is not set # CONFIG_COMMON_CLK_SI570 is not set +# CONFIG_CLK_QORIQ is not set # CONFIG_COMMON_CLK_PXA is not set +# CONFIG_COMMON_CLK_CDCE706 is not set # CONFIG_COMMON_CLK_QCOM is not set # @@ -3204,6 +3224,7 @@ CONFIG_NFS_SWAP=y CONFIG_NFS_V4_1=y CONFIG_NFS_V4_2=y CONFIG_PNFS_FILE_LAYOUT=y +CONFIG_PNFS_FLEXFILE_LAYOUT=m CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="kernel.org" CONFIG_NFS_V4_1_MIGRATION=y CONFIG_ROOT_NFS=y @@ -3401,6 +3422,7 @@ CONFIG_TRACING_SUPPORT=y # 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 @@ -3419,7 +3441,6 @@ CONFIG_HAVE_ARCH_KGDB=y # CONFIG_DEBUG_USER is not set # CONFIG_DEBUG_LL is not set CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S" -# CONFIG_DEBUG_UART_PL01X is not set # CONFIG_DEBUG_UART_8250 is not set # CONFIG_DEBUG_UART_BCM63XX is not set CONFIG_UNCOMPRESS_INCLUDE="mach/uncompress.h" @@ -3434,7 +3455,6 @@ CONFIG_KEYS=y # CONFIG_PERSISTENT_KEYRINGS is not set # CONFIG_BIG_KEYS is not set # 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 @@ -3461,8 +3481,8 @@ 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_GF128MUL=m +CONFIG_CRYPTO_NULL=m # CONFIG_CRYPTO_PCRYPT is not set CONFIG_CRYPTO_WORKQUEUE=y # CONFIG_CRYPTO_CRYPTD is not set @@ -3474,7 +3494,7 @@ CONFIG_CRYPTO_WORKQUEUE=y # Authenticated Encryption with Associated Data # CONFIG_CRYPTO_CCM=m -# CONFIG_CRYPTO_GCM is not set +CONFIG_CRYPTO_GCM=m CONFIG_CRYPTO_SEQIV=m # @@ -3502,7 +3522,7 @@ CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_CRC32C=y # CONFIG_CRYPTO_CRC32 is not set # CONFIG_CRYPTO_CRCT10DIF is not set -# CONFIG_CRYPTO_GHASH is not set +CONFIG_CRYPTO_GHASH=m CONFIG_CRYPTO_MD4=y CONFIG_CRYPTO_MD5=y # CONFIG_CRYPTO_MICHAEL_MIC is not set @@ -3553,6 +3573,7 @@ CONFIG_CRYPTO_LZO=m # CONFIG_CRYPTO_DRBG_MENU is not set # 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_HW is not set # CONFIG_ASYMMETRIC_KEY_TYPE is not set # CONFIG_BINARY_PRINTF is not set @@ -3562,6 +3583,7 @@ CONFIG_CRYPTO_LZO=m # 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 diff --git a/projects/RPi2/options b/projects/RPi2/options index f2764f44f4..7e13ed0c3b 100644 --- a/projects/RPi2/options +++ b/projects/RPi2/options @@ -57,7 +57,7 @@ # Kernel to use. values can be: # default: default mainline kernel - LINUX="3.19" + LINUX="default" # NOOBS supported hex versions NOOBS_HEX="1040,1041" diff --git a/projects/RPi2/patches/linux/linux-01-RPi_support.patch b/projects/RPi2/patches/linux/linux-01-RPi_support.patch index fbe6e260a5..2259be5a25 100644 --- a/projects/RPi2/patches/linux/linux-01-RPi_support.patch +++ b/projects/RPi2/patches/linux/linux-01-RPi_support.patch @@ -1,14 +1,13 @@ -From 8fa199a9eafe1ffbb0b1dd9c67989ea322f72306 Mon Sep 17 00:00:00 2001 +From 6a451debad7237887314b96e5728277b816eb3bb Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 12 May 2013 12:24:19 +0100 -Subject: [PATCH 001/172] Main bcm2708 linux port +Subject: [PATCH 001/104] Main bcm2708 linux port Signed-off-by: popcornmix --- arch/arm/Kconfig | 17 + arch/arm/Kconfig.debug | 8 + arch/arm/Makefile | 1 + - arch/arm/configs/bcmrpi_defconfig | 464 ++++++++++++++++ arch/arm/kernel/process.c | 10 + arch/arm/mach-bcm2708/Kconfig | 26 + arch/arm/mach-bcm2708/Makefile | 6 + @@ -47,8 +46,7 @@ Signed-off-by: popcornmix arch/arm/tools/mach-types | 1 + drivers/tty/serial/amba-pl011.c | 2 +- include/linux/mmc/host.h | 1 + - 42 files changed, 4715 insertions(+), 5 deletions(-) - create mode 100644 arch/arm/configs/bcmrpi_defconfig + 41 files changed, 4251 insertions(+), 5 deletions(-) create mode 100644 arch/arm/mach-bcm2708/Kconfig create mode 100644 arch/arm/mach-bcm2708/Makefile create mode 100644 arch/arm/mach-bcm2708/Makefile.boot @@ -83,10 +81,10 @@ Signed-off-by: popcornmix create mode 100644 arch/arm/mach-bcm2708/vcio.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 97d07ed..1dc698c 100644 +index cf4c0c9..6876ae8 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -367,6 +367,22 @@ config ARCH_AT91 +@@ -369,6 +369,22 @@ config ARCH_AT91 This enables support for systems based on Atmel AT91RM9200, AT91SAM9 and SAMA5 processors. @@ -109,7 +107,7 @@ index 97d07ed..1dc698c 100644 config ARCH_CLPS711X bool "Cirrus Logic CLPS711x/EP721x/EP731x-based" select ARCH_REQUIRE_GPIOLIB -@@ -962,6 +978,7 @@ source "arch/arm/plat-versatile/Kconfig" +@@ -967,6 +983,7 @@ source "arch/arm/plat-versatile/Kconfig" source "arch/arm/mach-vt8500/Kconfig" source "arch/arm/mach-w90x900/Kconfig" @@ -118,10 +116,10 @@ index 97d07ed..1dc698c 100644 source "arch/arm/mach-zynq/Kconfig" diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug -index 5ddd490..e5b2370 100644 +index 970de75..eaadbe8 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug -@@ -1107,6 +1107,14 @@ choice +@@ -1209,6 +1209,14 @@ choice options; the platform specific options are deprecated and will be soon removed. @@ -135,9 +133,9 @@ index 5ddd490..e5b2370 100644 + endchoice - config DEBUG_EXYNOS_UART + config DEBUG_AT91_UART diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index c1785ee..ceb8a4e 100644 +index eb7bb51..27c0f92 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -146,6 +146,7 @@ textofs-$(CONFIG_ARCH_AXXIA) := 0x00308000 @@ -148,476 +146,6 @@ index c1785ee..ceb8a4e 100644 machine-$(CONFIG_ARCH_BERLIN) += berlin machine-$(CONFIG_ARCH_CLPS711X) += clps711x machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -new file mode 100644 -index 0000000..8950e53 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -0,0 +1,464 @@ -+# CONFIG_LOCALVERSION_AUTO is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_EMBEDDED=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+CONFIG_PROFILING=y -+CONFIG_OPROFILE=m -+CONFIG_KPROBES=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_PARTITION_ADVANCED=y -+CONFIG_MAC_PARTITION=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2708=y -+CONFIG_AEABI=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" -+CONFIG_KEXEC=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=m -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_XFRM_USER=y -+CONFIG_NET_KEY=m -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+CONFIG_NET_PKTGEN=m -+CONFIG_IRDA=m -+CONFIG_IRLAN=m -+CONFIG_IRCOMM=m -+CONFIG_IRDA_ULTRA=y -+CONFIG_IRDA_CACHE_LAST_LSAP=y -+CONFIG_IRDA_FAST_RR=y -+CONFIG_IRTTY_SIR=m -+CONFIG_KINGSUN_DONGLE=m -+CONFIG_KSDAZZLE_DONGLE=m -+CONFIG_KS959_DONGLE=m -+CONFIG_USB_IRDA=m -+CONFIG_SIGMATEL_FIR=m -+CONFIG_MCS_FIR=m -+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 -+CONFIG_BT_HCIBTUSB=m -+CONFIG_BT_HCIBCM203X=m -+CONFIG_BT_HCIBPA10X=m -+CONFIG_BT_HCIBFUSB=m -+CONFIG_BT_HCIVHCI=m -+CONFIG_BT_MRVL=m -+CONFIG_BT_MRVL_SDIO=m -+CONFIG_BT_ATH3K=m -+CONFIG_CFG80211=m -+CONFIG_MAC80211=m -+CONFIG_MAC80211_MESH=y -+CONFIG_WIMAX=m -+CONFIG_NET_9P=m -+CONFIG_NFC=m -+CONFIG_NFC_PN533=m -+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_NBD=m -+CONFIG_BLK_DEV_RAM=y -+CONFIG_CDROM_PKTCDVD=m -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+CONFIG_BLK_DEV_SD=m -+CONFIG_BLK_DEV_SR=m -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_MD=y -+CONFIG_NETDEVICES=y -+CONFIG_NETCONSOLE=m -+CONFIG_TUN=m -+CONFIG_MDIO_BITBANG=m -+CONFIG_PPP=m -+CONFIG_PPP_BSDCOMP=m -+CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_ASYNC=m -+CONFIG_PPP_SYNC_TTY=m -+CONFIG_SLIP=m -+CONFIG_SLIP_COMPRESSED=y -+CONFIG_USB_CATC=m -+CONFIG_USB_KAWETH=m -+CONFIG_USB_PEGASUS=m -+CONFIG_USB_RTL8150=m -+CONFIG_USB_USBNET=y -+CONFIG_USB_NET_AX8817X=m -+CONFIG_USB_NET_CDCETHER=m -+CONFIG_USB_NET_CDC_EEM=m -+CONFIG_USB_NET_DM9601=m -+CONFIG_USB_NET_SMSC75XX=m -+CONFIG_USB_NET_SMSC95XX=y -+CONFIG_USB_NET_GL620A=m -+CONFIG_USB_NET_NET1080=m -+CONFIG_USB_NET_PLUSB=m -+CONFIG_USB_NET_MCS7830=m -+CONFIG_USB_NET_CDC_SUBSET=m -+CONFIG_USB_ALI_M5632=y -+CONFIG_USB_AN2720=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_INT51X1=m -+CONFIG_USB_IPHETH=m -+CONFIG_USB_SIERRA_NET=m -+CONFIG_USB_VL600=m -+CONFIG_LIBERTAS_THINFIRM=m -+CONFIG_LIBERTAS_THINFIRM_USB=m -+CONFIG_AT76C50X_USB=m -+CONFIG_USB_ZD1201=m -+CONFIG_USB_NET_RNDIS_WLAN=m -+CONFIG_RTL8187=m -+CONFIG_MAC80211_HWSIM=m -+CONFIG_B43=m -+CONFIG_B43LEGACY=m -+CONFIG_HOSTAP=m -+CONFIG_LIBERTAS=m -+CONFIG_LIBERTAS_USB=m -+CONFIG_LIBERTAS_SDIO=m -+CONFIG_P54_COMMON=m -+CONFIG_P54_USB=m -+CONFIG_RT2X00=m -+CONFIG_RT2500USB=m -+CONFIG_RT73USB=m -+CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT53XX=y -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_MISC=y -+CONFIG_INPUT_AD714X=m -+CONFIG_INPUT_ATI_REMOTE2=m -+CONFIG_INPUT_KEYSPAN_REMOTE=m -+CONFIG_INPUT_POWERMATE=m -+CONFIG_INPUT_YEALINK=m -+CONFIG_INPUT_CM109=m -+CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_ADXL34X=m -+CONFIG_INPUT_CMA3000=m -+CONFIG_SERIO=m -+CONFIG_SERIO_RAW=m -+CONFIG_GAMEPORT=m -+CONFIG_GAMEPORT_NS558=m -+CONFIG_GAMEPORT_L4=m -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+# CONFIG_HW_RANDOM is not set -+CONFIG_RAW_DRIVER=y -+# CONFIG_HWMON is not set -+CONFIG_WATCHDOG=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_HID_A4TECH=m -+CONFIG_HID_ACRUX=m -+CONFIG_HID_APPLE=m -+CONFIG_HID_BELKIN=m -+CONFIG_HID_CHERRY=m -+CONFIG_HID_CHICONY=m -+CONFIG_HID_CYPRESS=m -+CONFIG_HID_DRAGONRISE=m -+CONFIG_HID_EMS_FF=m -+CONFIG_HID_ELECOM=m -+CONFIG_HID_EZKEY=m -+CONFIG_HID_HOLTEK=m -+CONFIG_HID_KEYTOUCH=m -+CONFIG_HID_KYE=m -+CONFIG_HID_UCLOGIC=m -+CONFIG_HID_WALTOP=m -+CONFIG_HID_GYRATION=m -+CONFIG_HID_TWINHAN=m -+CONFIG_HID_KENSINGTON=m -+CONFIG_HID_LCPOWER=m -+CONFIG_HID_LOGITECH=m -+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_HID_PETALYNX=m -+CONFIG_HID_PICOLCD=m -+CONFIG_HID_ROCCAT=m -+CONFIG_HID_SAMSUNG=m -+CONFIG_HID_SONY=m -+CONFIG_HID_SPEEDLINK=m -+CONFIG_HID_SUNPLUS=m -+CONFIG_HID_GREENASIA=m -+CONFIG_HID_SMARTJOYPLUS=m -+CONFIG_HID_TOPSEED=m -+CONFIG_HID_THRUSTMASTER=m -+CONFIG_HID_WACOM=m -+CONFIG_HID_WIIMOTE=m -+CONFIG_HID_ZEROPLUS=m -+CONFIG_HID_ZYDACRON=m -+CONFIG_HID_PID=y -+CONFIG_USB_HIDDEV=y -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_MON=m -+CONFIG_USB_STORAGE=y -+CONFIG_USB_STORAGE_REALTEK=m -+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 -+CONFIG_USB_UAS=m -+CONFIG_USB_MDC800=m -+CONFIG_USB_MICROTEK=m -+CONFIG_USB_SERIAL=m -+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_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_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_KLSI=m -+CONFIG_USB_SERIAL_KOBIL_SCT=m -+CONFIG_USB_SERIAL_MCT_U232=m -+CONFIG_USB_SERIAL_MOS7720=m -+CONFIG_USB_SERIAL_MOS7840=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_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_OPTION=m -+CONFIG_USB_SERIAL_OMNINET=m -+CONFIG_USB_SERIAL_OPTICON=m -+CONFIG_USB_SERIAL_SSU100=m -+CONFIG_USB_SERIAL_DEBUG=m -+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=m -+CONFIG_USB_LD=m -+CONFIG_USB_TRANCEVIBRATOR=m -+CONFIG_USB_IOWARRIOR=m -+CONFIG_USB_TEST=m -+CONFIG_USB_ISIGHTFW=m -+CONFIG_USB_YUREX=m -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_UIO=m -+CONFIG_UIO_PDRV_GENIRQ=m -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_REISERFS_FS=m -+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_STATISTICS=y -+CONFIG_XFS_FS=m -+CONFIG_XFS_QUOTA=y -+CONFIG_XFS_POSIX_ACL=y -+CONFIG_XFS_RT=y -+CONFIG_GFS2_FS=m -+CONFIG_OCFS2_FS=m -+CONFIG_BTRFS_FS=m -+CONFIG_BTRFS_FS_POSIX_ACL=y -+CONFIG_NILFS2_FS=m -+CONFIG_FANOTIFY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_ISO9660_FS=m -+CONFIG_JOLIET=y -+CONFIG_ZISOFS=y -+CONFIG_UDF_FS=m -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_NTFS_FS=m -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+CONFIG_SQUASHFS=m -+CONFIG_SQUASHFS_XATTR=y -+CONFIG_SQUASHFS_LZO=y -+CONFIG_SQUASHFS_XZ=y -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_CIFS=m -+CONFIG_CIFS_WEAK_PW_HASH=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_9P_FS=m -+CONFIG_9P_FS_POSIX_ACL=y -+CONFIG_NLS_DEFAULT="utf8" -+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=y -+CONFIG_NLS_ISO8859_1=m -+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_UTF8=m -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_INFO=y -+CONFIG_DEBUG_STACK_USAGE=y -+CONFIG_DEBUG_MEMORY_INIT=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+CONFIG_LATENCYTOP=y -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+CONFIG_KDB_KEYBOARD=y -+CONFIG_STRICT_DEVMEM=y -+CONFIG_CRYPTO_AUTHENC=m -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_XCBC=m -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_SHA512=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+CONFIG_CRYPTO_DEFLATE=m -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index fdfa3a7..f5ffaacb 100644 --- a/arch/arm/kernel/process.c @@ -3715,7 +3243,7 @@ index 0000000..4a4a338 +#endif /* VC_MEM_H */ diff --git a/arch/arm/mach-bcm2708/include/mach/vcio.h b/arch/arm/mach-bcm2708/include/mach/vcio.h new file mode 100644 -index 0000000..8e11d67 +index 0000000..8e11d67e --- /dev/null +++ b/arch/arm/mach-bcm2708/include/mach/vcio.h @@ -0,0 +1,165 @@ @@ -5031,7 +4559,7 @@ index 0000000..5e43e85 +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bcm-mbox"); diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig -index c43c714..9bedaea 100644 +index 9b4f29e..0c5d4ac 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -358,7 +358,7 @@ config CPU_PJ4B @@ -5096,7 +4624,7 @@ index 8d94c19..16ca535 100644 static struct vendor_data vendor_arm = { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h -index 9f32270..17ee61b 100644 +index 0c8cbe5..c3b84a2 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -291,6 +291,7 @@ struct mmc_host { @@ -5108,10 +4636,7354 @@ index 9f32270..17ee61b 100644 mmc_pm_flag_t pm_caps; /* supported pm features */ -From 2392ce74d3cd30172bc41023066f9f7abe7404ea Mon Sep 17 00:00:00 2001 +From debcc469754e0c81a822c8df081cea23c7ec5065 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 7 May 2013 14:32:27 +0100 +Subject: [PATCH 002/104] Add 2709 platform for Raspberry Pi 2 + +--- + arch/arm/Kconfig | 21 + + arch/arm/Makefile | 1 + + arch/arm/kernel/head.S | 8 + + arch/arm/mach-bcm2709/Kconfig | 49 + + arch/arm/mach-bcm2709/Makefile | 7 + + arch/arm/mach-bcm2709/Makefile.boot | 3 + + arch/arm/mach-bcm2709/armctrl.c | 361 +++++++ + arch/arm/mach-bcm2709/armctrl.h | 27 + + arch/arm/mach-bcm2709/bcm2708_gpio.c | 426 ++++++++ + arch/arm/mach-bcm2709/bcm2709.c | 1257 ++++++++++++++++++++++ + arch/arm/mach-bcm2709/bcm2709.h | 49 + + arch/arm/mach-bcm2709/clock.c | 61 ++ + arch/arm/mach-bcm2709/clock.h | 24 + + arch/arm/mach-bcm2709/delay.S | 21 + + arch/arm/mach-bcm2709/dma.c | 409 +++++++ + arch/arm/mach-bcm2709/dmaer.c | 886 +++++++++++++++ + arch/arm/mach-bcm2709/include/mach/arm_control.h | 493 +++++++++ + arch/arm/mach-bcm2709/include/mach/arm_power.h | 62 ++ + arch/arm/mach-bcm2709/include/mach/barriers.h | 3 + + arch/arm/mach-bcm2709/include/mach/clkdev.h | 7 + + arch/arm/mach-bcm2709/include/mach/debug-macro.S | 22 + + arch/arm/mach-bcm2709/include/mach/dma.h | 94 ++ + arch/arm/mach-bcm2709/include/mach/entry-macro.S | 123 +++ + arch/arm/mach-bcm2709/include/mach/frc.h | 38 + + arch/arm/mach-bcm2709/include/mach/gpio.h | 17 + + arch/arm/mach-bcm2709/include/mach/hardware.h | 28 + + arch/arm/mach-bcm2709/include/mach/io.h | 27 + + arch/arm/mach-bcm2709/include/mach/irqs.h | 225 ++++ + arch/arm/mach-bcm2709/include/mach/memory.h | 57 + + arch/arm/mach-bcm2709/include/mach/platform.h | 225 ++++ + arch/arm/mach-bcm2709/include/mach/power.h | 26 + + arch/arm/mach-bcm2709/include/mach/system.h | 38 + + arch/arm/mach-bcm2709/include/mach/timex.h | 23 + + arch/arm/mach-bcm2709/include/mach/uncompress.h | 84 ++ + arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 + + arch/arm/mach-bcm2709/include/mach/vc_support.h | 69 ++ + arch/arm/mach-bcm2709/include/mach/vcio.h | 165 +++ + arch/arm/mach-bcm2709/include/mach/vmalloc.h | 20 + + arch/arm/mach-bcm2709/power.c | 195 ++++ + arch/arm/mach-bcm2709/vc_mem.c | 431 ++++++++ + arch/arm/mach-bcm2709/vc_support.c | 318 ++++++ + arch/arm/mach-bcm2709/vcio.c | 474 ++++++++ + arch/arm/mm/proc-v7.S | 1 + + arch/arm/tools/mach-types | 1 + + drivers/clocksource/arm_arch_timer.c | 36 + + 45 files changed, 6947 insertions(+) + create mode 100644 arch/arm/mach-bcm2709/Kconfig + create mode 100644 arch/arm/mach-bcm2709/Makefile + create mode 100644 arch/arm/mach-bcm2709/Makefile.boot + create mode 100644 arch/arm/mach-bcm2709/armctrl.c + create mode 100644 arch/arm/mach-bcm2709/armctrl.h + create mode 100644 arch/arm/mach-bcm2709/bcm2708_gpio.c + create mode 100644 arch/arm/mach-bcm2709/bcm2709.c + create mode 100644 arch/arm/mach-bcm2709/bcm2709.h + create mode 100644 arch/arm/mach-bcm2709/clock.c + create mode 100644 arch/arm/mach-bcm2709/clock.h + create mode 100644 arch/arm/mach-bcm2709/delay.S + create mode 100644 arch/arm/mach-bcm2709/dma.c + create mode 100755 arch/arm/mach-bcm2709/dmaer.c + create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_control.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_power.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/barriers.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/clkdev.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/debug-macro.S + create mode 100644 arch/arm/mach-bcm2709/include/mach/dma.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/entry-macro.S + create mode 100644 arch/arm/mach-bcm2709/include/mach/frc.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/gpio.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/hardware.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/io.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/irqs.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/memory.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/platform.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/power.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/system.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/timex.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/uncompress.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h + create mode 100755 arch/arm/mach-bcm2709/include/mach/vc_support.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vcio.h + create mode 100644 arch/arm/mach-bcm2709/include/mach/vmalloc.h + create mode 100644 arch/arm/mach-bcm2709/power.c + create mode 100644 arch/arm/mach-bcm2709/vc_mem.c + create mode 100755 arch/arm/mach-bcm2709/vc_support.c + create mode 100644 arch/arm/mach-bcm2709/vcio.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 6876ae8..4018fea 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -793,6 +793,26 @@ config ARCH_OMAP1 + help + Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) + ++config ARCH_BCM2709 ++ bool "Broadcom BCM2709 family" ++ select ARCH_HAS_BARRIERS if SMP ++ select CPU_V7 ++ select HAVE_SMP ++ select ARM_AMBA ++ select MIGHT_HAVE_CACHE_L2X0 ++ select HAVE_SCHED_CLOCK ++ select NEED_MACH_MEMORY_H ++ select NEED_MACH_IO_H ++ select COMMON_CLK ++ select ARCH_HAS_CPUFREQ ++ select GENERIC_CLOCKEVENTS ++ select MACH_BCM2709 ++ select VC4 ++ select FIQ ++# select ZONE_DMA ++ help ++ This enables support for Broadcom BCM2709 boards. ++ + endchoice + + menu "Multiple platform selection" +@@ -984,6 +1004,7 @@ source "arch/arm/mach-vt8500/Kconfig" + + source "arch/arm/mach-w90x900/Kconfig" + source "arch/arm/mach-bcm2708/Kconfig" ++source "arch/arm/mach-bcm2709/Kconfig" + + source "arch/arm/mach-zynq/Kconfig" + +diff --git a/arch/arm/Makefile b/arch/arm/Makefile +index 27c0f92..21ba7f3 100644 +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -147,6 +147,7 @@ machine-$(CONFIG_ARCH_AT91) += at91 + machine-$(CONFIG_ARCH_AXXIA) += axxia + machine-$(CONFIG_ARCH_BCM) += bcm + machine-$(CONFIG_ARCH_BCM2708) += bcm2708 ++machine-$(CONFIG_ARCH_BCM2709) += bcm2709 + machine-$(CONFIG_ARCH_BERLIN) += berlin + machine-$(CONFIG_ARCH_CLPS711X) += clps711x + machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx +diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S +index 0196327..e4d6030 100644 +--- a/arch/arm/kernel/head.S ++++ b/arch/arm/kernel/head.S +@@ -680,6 +680,14 @@ ARM_BE8(rev16 ip, ip) + ldrcc r7, [r4], #4 @ use branch for delay slot + bcc 1b + ret lr ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop ++ nop + #endif + ENDPROC(__fixup_a_pv_table) + +diff --git a/arch/arm/mach-bcm2709/Kconfig b/arch/arm/mach-bcm2709/Kconfig +new file mode 100644 +index 0000000..4fb6e1b +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Kconfig +@@ -0,0 +1,49 @@ ++menu "Broadcom BCM2709 Implementations" ++ depends on ARCH_BCM2709 ++ ++config MACH_BCM2709 ++ bool "Broadcom BCM2709 Development Platform" ++ help ++ Include support for the Broadcom(R) BCM2709 platform. ++ ++config BCM2709_DT ++ bool "BCM2709 Device Tree support" ++ depends on MACH_BCM2709 ++ default n ++ select USE_OF ++ select ARCH_REQUIRE_GPIOLIB ++ select PINCTRL ++ select PINCTRL_BCM2835 ++ help ++ Enable Device Tree support for BCM2709 ++ ++config BCM2708_GPIO ++ bool "BCM2709 gpio support" ++ depends on MACH_BCM2709 ++ select ARCH_REQUIRE_GPIOLIB ++ default y ++ help ++ Include support for the Broadcom(R) BCM2709 gpio. ++ ++config BCM2708_VCMEM ++ bool "Videocore Memory" ++ depends on MACH_BCM2709 ++ default y ++ help ++ Helper for videocore memory access and total size allocation. ++ ++config BCM2708_NOL2CACHE ++ bool "Videocore L2 cache disable" ++ depends on MACH_BCM2709 ++ default y ++ help ++ Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. ++ ++config BCM2708_SPIDEV ++ bool "Bind spidev to SPI0 master" ++ depends on MACH_BCM2709 ++ depends on SPI ++ default y ++ help ++ Binds spidev driver to the SPI0 master ++endmenu +diff --git a/arch/arm/mach-bcm2709/Makefile b/arch/arm/mach-bcm2709/Makefile +new file mode 100644 +index 0000000..2a803bb +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux kernel. ++# ++ ++obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o dma.o ++obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o ++obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +diff --git a/arch/arm/mach-bcm2709/Makefile.boot b/arch/arm/mach-bcm2709/Makefile.boot +new file mode 100644 +index 0000000..67039c3 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/Makefile.boot +@@ -0,0 +1,3 @@ ++ zreladdr-y := 0x00008000 ++params_phys-y := 0x00000100 ++initrd_phys-y := 0x00800000 +diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c +new file mode 100644 +index 0000000..2815b64 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/armctrl.c +@@ -0,0 +1,361 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "armctrl.h" ++ ++/* For support of kernels >= 3.0 assume only one VIC for now*/ ++static unsigned int remap_irqs[(INTERRUPT_ARASANSDIO + 1) - INTERRUPT_JPEG] = { ++ INTERRUPT_VC_JPEG, ++ INTERRUPT_VC_USB, ++ INTERRUPT_VC_3D, ++ INTERRUPT_VC_DMA2, ++ INTERRUPT_VC_DMA3, ++ INTERRUPT_VC_I2C, ++ INTERRUPT_VC_SPI, ++ INTERRUPT_VC_I2SPCM, ++ INTERRUPT_VC_SDIO, ++ INTERRUPT_VC_UART, ++ INTERRUPT_VC_ARASANSDIO ++}; ++ ++static void armctrl_mask_irq(struct irq_data *d) ++{ ++ static const unsigned int disables[4] = { ++ ARM_IRQ_DIBL1, ++ ARM_IRQ_DIBL2, ++ ARM_IRQ_DIBL3, ++ 0 ++ }; ++ int i; ++ if (d->irq >= FIQ_START) { ++ writel(0, __io_address(ARM_IRQ_FAST)); ++ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { ++#if 1 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; ++ for (i=0; i<4; i++) // i = raw_smp_processor_id(); // ++ { ++ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ writel(val &~ (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { ++#if 0 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; ++ for (i=0; i<4; i++) { ++ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ writel(val &~ (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { ++ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_CLR)); ++ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } ++} ++ ++static void armctrl_unmask_irq(struct irq_data *d) ++{ ++ static const unsigned int enables[4] = { ++ ARM_IRQ_ENBL1, ++ ARM_IRQ_ENBL2, ++ ARM_IRQ_ENBL3, ++ 0 ++ }; ++ int i; ++ if (d->irq >= FIQ_START) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); ++ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { ++#if 1 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; ++ for (i=0; i<4; i++) // i = raw_smp_processor_id(); ++ { ++ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ writel(val | (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { ++#if 0 ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; ++ for (i=0; i<4; i++) { ++ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ writel(val | (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); ++ } ++#endif ++ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { ++ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_SET)); ++ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } ++} ++ ++#ifdef CONFIG_OF ++ ++#define NR_IRQS_BANK0 21 ++#define NR_BANKS 4 ++#define IRQS_PER_BANK 32 ++ ++/* from drivers/irqchip/irq-bcm2835.c */ ++static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, ++ const u32 *intspec, unsigned int intsize, ++ unsigned long *out_hwirq, unsigned int *out_type) ++{ ++ if (WARN_ON(intsize != 2)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] >= NR_BANKS)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5 && intspec[1] != 9)) ++ return -EINVAL; ++ ++ if (intspec[0] == 0) ++ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; ++ else if (intspec[0] == 1) ++ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; ++ else if (intspec[0] == 2) ++ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; ++ else ++ *out_hwirq = ARM_IRQ_LOCAL_BASE + intspec[1]; ++ ++ /* reverse remap_irqs[] */ ++ switch (*out_hwirq) { ++ case INTERRUPT_VC_JPEG: ++ *out_hwirq = INTERRUPT_JPEG; ++ break; ++ case INTERRUPT_VC_USB: ++ *out_hwirq = INTERRUPT_USB; ++ break; ++ case INTERRUPT_VC_3D: ++ *out_hwirq = INTERRUPT_3D; ++ break; ++ case INTERRUPT_VC_DMA2: ++ *out_hwirq = INTERRUPT_DMA2; ++ break; ++ case INTERRUPT_VC_DMA3: ++ *out_hwirq = INTERRUPT_DMA3; ++ break; ++ case INTERRUPT_VC_I2C: ++ *out_hwirq = INTERRUPT_I2C; ++ break; ++ case INTERRUPT_VC_SPI: ++ *out_hwirq = INTERRUPT_SPI; ++ break; ++ case INTERRUPT_VC_I2SPCM: ++ *out_hwirq = INTERRUPT_I2SPCM; ++ break; ++ case INTERRUPT_VC_SDIO: ++ *out_hwirq = INTERRUPT_SDIO; ++ break; ++ case INTERRUPT_VC_UART: ++ *out_hwirq = INTERRUPT_UART; ++ break; ++ case INTERRUPT_VC_ARASANSDIO: ++ *out_hwirq = INTERRUPT_ARASANSDIO; ++ break; ++ } ++ ++ *out_type = IRQ_TYPE_NONE; ++ return 0; ++} ++ ++static struct irq_domain_ops armctrl_ops = { ++ .xlate = armctrl_xlate ++}; ++ ++void __init armctrl_dt_init(void) ++{ ++ struct device_node *np; ++ struct irq_domain *domain; ++ ++ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); ++ if (!np) ++ return; ++ ++ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, ++ IRQ_ARMCTRL_START, 0, ++ &armctrl_ops, NULL); ++ WARN_ON(!domain); ++} ++#else ++void __init armctrl_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ ++#if defined(CONFIG_PM) ++ ++/* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ ++ ++/* Static defines ++ * struct armctrl_device - VIC PM device (< 3.xx) ++ * @sysdev: The system device which is registered. (< 3.xx) ++ * @irq: The IRQ number for the base of the VIC. ++ * @base: The register base for the VIC. ++ * @resume_sources: A bitmask of interrupts for resume. ++ * @resume_irqs: The IRQs enabled for resume. ++ * @int_select: Save for VIC_INT_SELECT. ++ * @int_enable: Save for VIC_INT_ENABLE. ++ * @soft_int: Save for VIC_INT_SOFT. ++ * @protect: Save for VIC_PROTECT. ++ */ ++struct armctrl_info { ++ void __iomem *base; ++ int irq; ++ u32 resume_sources; ++ u32 resume_irqs; ++ u32 int_select; ++ u32 int_enable; ++ u32 soft_int; ++ u32 protect; ++} armctrl; ++ ++static int armctrl_suspend(void) ++{ ++ return 0; ++} ++ ++static void armctrl_resume(void) ++{ ++ return; ++} ++ ++/** ++ * armctrl_pm_register - Register a VIC for later power management control ++ * @base: The base address of the VIC. ++ * @irq: The base IRQ for the VIC. ++ * @resume_sources: bitmask of interrupts allowed for resume sources. ++ * ++ * For older kernels (< 3.xx) do - ++ * Register the VIC with the system device tree so that it can be notified ++ * of suspend and resume requests and ensure that the correct actions are ++ * taken to re-instate the settings on resume. ++ */ ++static void __init armctrl_pm_register(void __iomem * base, unsigned int irq, ++ u32 resume_sources) ++{ ++ armctrl.base = base; ++ armctrl.resume_sources = resume_sources; ++ armctrl.irq = irq; ++} ++ ++static int armctrl_set_wake(struct irq_data *d, unsigned int on) ++{ ++ unsigned int off = d->irq & 31; ++ u32 bit = 1 << off; ++ ++ if (!(bit & armctrl.resume_sources)) ++ return -EINVAL; ++ ++ if (on) ++ armctrl.resume_irqs |= bit; ++ else ++ armctrl.resume_irqs &= ~bit; ++ ++ return 0; ++} ++ ++#else ++static inline void armctrl_pm_register(void __iomem * base, unsigned int irq, ++ u32 arg1) ++{ ++} ++ ++#define armctrl_suspend NULL ++#define armctrl_resume NULL ++#define armctrl_set_wake NULL ++#endif /* CONFIG_PM */ ++ ++static struct syscore_ops armctrl_syscore_ops = { ++ .suspend = armctrl_suspend, ++ .resume = armctrl_resume, ++}; ++ ++/** ++ * armctrl_syscore_init - initicall to register VIC pm functions ++ * ++ * This is called via late_initcall() to register ++ * the resources for the VICs due to the early ++ * nature of the VIC's registration. ++*/ ++static int __init armctrl_syscore_init(void) ++{ ++ register_syscore_ops(&armctrl_syscore_ops); ++ return 0; ++} ++ ++late_initcall(armctrl_syscore_init); ++ ++static struct irq_chip armctrl_chip = { ++ .name = "ARMCTRL", ++ .irq_ack = NULL, ++ .irq_mask = armctrl_mask_irq, ++ .irq_unmask = armctrl_unmask_irq, ++ .irq_set_wake = armctrl_set_wake, ++}; ++ ++/** ++ * armctrl_init - initialise a vectored interrupt controller ++ * @base: iomem base address ++ * @irq_start: starting interrupt number, must be muliple of 32 ++ * @armctrl_sources: bitmask of interrupt sources to allow ++ * @resume_sources: bitmask of interrupt sources to allow for resume ++ */ ++int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources) ++{ ++ unsigned int irq; ++ ++ for (irq = 0; irq < BCM2708_ALLOC_IRQS; irq++) { ++ unsigned int data = irq; ++ if (irq >= INTERRUPT_JPEG && irq <= INTERRUPT_ARASANSDIO) ++ data = remap_irqs[irq - INTERRUPT_JPEG]; ++ if (irq >= IRQ_ARM_LOCAL_CNTPSIRQ && irq <= IRQ_ARM_LOCAL_TIMER) { ++ irq_set_percpu_devid(irq); ++ irq_set_chip_and_handler(irq, &armctrl_chip, handle_percpu_devid_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); ++ } else { ++ irq_set_chip_and_handler(irq, &armctrl_chip, handle_level_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE | IRQF_DISABLED); ++ } ++ irq_set_chip_data(irq, (void *)data); ++ } ++ ++ armctrl_pm_register(base, irq_start, resume_sources); ++ init_FIQ(FIQ_START); ++ armctrl_dt_init(); ++ return 0; ++} +diff --git a/arch/arm/mach-bcm2709/armctrl.h b/arch/arm/mach-bcm2709/armctrl.h +new file mode 100644 +index 0000000..0aa916e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/armctrl.h +@@ -0,0 +1,27 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARMCTRL_H ++#define __BCM2708_ARMCTRL_H ++ ++extern int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/bcm2708_gpio.c b/arch/arm/mach-bcm2709/bcm2708_gpio.c +new file mode 100644 +index 0000000..c1e9254 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2708_gpio.c +@@ -0,0 +1,426 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++#define DRIVER_NAME BCM_GPIO_DRIVER_NAME ++#define BCM_GPIO_USE_IRQ 1 ++ ++#define GPIOFSEL(x) (0x00+(x)*4) ++#define GPIOSET(x) (0x1c+(x)*4) ++#define GPIOCLR(x) (0x28+(x)*4) ++#define GPIOLEV(x) (0x34+(x)*4) ++#define GPIOEDS(x) (0x40+(x)*4) ++#define GPIOREN(x) (0x4c+(x)*4) ++#define GPIOFEN(x) (0x58+(x)*4) ++#define GPIOHEN(x) (0x64+(x)*4) ++#define GPIOLEN(x) (0x70+(x)*4) ++#define GPIOAREN(x) (0x7c+(x)*4) ++#define GPIOAFEN(x) (0x88+(x)*4) ++#define GPIOUD(x) (0x94+(x)*4) ++#define GPIOUDCLK(x) (0x98+(x)*4) ++ ++#define GPIO_BANKS 2 ++ ++enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, ++ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, ++ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, ++ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, ++}; ++ ++ /* Each of the two spinlocks protects a different set of hardware ++ * regiters and data structurs. This decouples the code of the IRQ from ++ * the GPIO code. This also makes the case of a GPIO routine call from ++ * the IRQ code simpler. ++ */ ++static DEFINE_SPINLOCK(lock); /* GPIO registers */ ++ ++struct bcm2708_gpio { ++ struct list_head list; ++ void __iomem *base; ++ struct gpio_chip gc; ++ unsigned long rising[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long falling[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long high[(BCM2708_NR_GPIOS + 31) / 32]; ++ unsigned long low[(BCM2708_NR_GPIOS + 31) / 32]; ++}; ++ ++static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, ++ int function) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned long flags; ++ unsigned gpiodir; ++ unsigned gpio_bank = offset / 10; ++ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; ++ ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ gpiodir &= ~(7 << gpio_field_offset); ++ gpiodir |= function << gpio_field_offset; ++ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank)); ++ spin_unlock_irqrestore(&lock, flags); ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ ++ return 0; ++} ++ ++static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset) ++{ ++ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value); ++static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset, ++ int value) ++{ ++ int ret; ++ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT); ++ if (ret >= 0) ++ bcm2708_gpio_set(gc, offset, value); ++ return ret; ++} ++ ++static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++ unsigned lev; ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return 0; ++ lev = readl(gpio->base + GPIOLEV(gpio_bank)); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); ++ return 0x1 & (lev >> gpio_field_offset); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); ++ if (offset >= BCM2708_NR_GPIOS) ++ return; ++ if (value) ++ writel(1 << gpio_field_offset, gpio->base + GPIOSET(gpio_bank)); ++ else ++ writel(1 << gpio_field_offset, gpio->base + GPIOCLR(gpio_bank)); ++} ++ ++/********************** ++ * extension to configure pullups ++ */ ++int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, ++ bcm2708_gpio_pull_t value) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset / 32; ++ unsigned gpio_field_offset = (offset - 32 * gpio_bank); ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ switch (value) { ++ case BCM2708_PULL_UP: ++ writel(2, gpio->base + GPIOUD(0)); ++ break; ++ case BCM2708_PULL_DOWN: ++ writel(1, gpio->base + GPIOUD(0)); ++ break; ++ case BCM2708_PULL_OFF: ++ writel(0, gpio->base + GPIOUD(0)); ++ break; ++ } ++ ++ udelay(5); ++ writel(1 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); ++ udelay(5); ++ writel(0, gpio->base + GPIOUD(0)); ++ writel(0 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(bcm2708_gpio_setpull); ++ ++/************************************************************************************************************************* ++ * bcm2708 GPIO IRQ ++ */ ++ ++#if BCM_GPIO_USE_IRQ ++ ++static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) ++{ ++ return gpio_to_irq(gpio); ++} ++ ++static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned go = gn % 32; ++ ++ gpio->rising[gb] &= ~(1 << go); ++ gpio->falling[gb] &= ~(1 << go); ++ gpio->high[gb] &= ~(1 << go); ++ gpio->low[gb] &= ~(1 << go); ++ ++ if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) ++ return -EINVAL; ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ gpio->rising[gb] |= (1 << go); ++ if (type & IRQ_TYPE_EDGE_FALLING) ++ gpio->falling[gb] |= (1 << go); ++ if (type & IRQ_TYPE_LEVEL_HIGH) ++ gpio->high[gb] |= (1 << go); ++ if (type & IRQ_TYPE_LEVEL_LOW) ++ gpio->low[gb] |= (1 << go); ++ return 0; ++} ++ ++static void bcm2708_gpio_irq_mask(struct irq_data *d) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned long rising = readl(gpio->base + GPIOREN(gb)); ++ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); ++ unsigned long high = readl(gpio->base + GPIOHEN(gb)); ++ unsigned long low = readl(gpio->base + GPIOLEN(gb)); ++ ++ gn = gn % 32; ++ ++ writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); ++ writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); ++ writel(high & ~(1 << gn), gpio->base + GPIOHEN(gb)); ++ writel(low & ~(1 << gn), gpio->base + GPIOLEN(gb)); ++} ++ ++static void bcm2708_gpio_irq_unmask(struct irq_data *d) ++{ ++ unsigned irq = d->irq; ++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); ++ unsigned gn = irq_to_gpio(irq); ++ unsigned gb = gn / 32; ++ unsigned go = gn % 32; ++ unsigned long rising = readl(gpio->base + GPIOREN(gb)); ++ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); ++ unsigned long high = readl(gpio->base + GPIOHEN(gb)); ++ unsigned long low = readl(gpio->base + GPIOLEN(gb)); ++ ++ if (gpio->rising[gb] & (1 << go)) { ++ writel(rising | (1 << go), gpio->base + GPIOREN(gb)); ++ } else { ++ writel(rising & ~(1 << go), gpio->base + GPIOREN(gb)); ++ } ++ ++ if (gpio->falling[gb] & (1 << go)) { ++ writel(falling | (1 << go), gpio->base + GPIOFEN(gb)); ++ } else { ++ writel(falling & ~(1 << go), gpio->base + GPIOFEN(gb)); ++ } ++ ++ if (gpio->high[gb] & (1 << go)) { ++ writel(high | (1 << go), gpio->base + GPIOHEN(gb)); ++ } else { ++ writel(high & ~(1 << go), gpio->base + GPIOHEN(gb)); ++ } ++ ++ if (gpio->low[gb] & (1 << go)) { ++ writel(low | (1 << go), gpio->base + GPIOLEN(gb)); ++ } else { ++ writel(low & ~(1 << go), gpio->base + GPIOLEN(gb)); ++ } ++} ++ ++static struct irq_chip bcm2708_irqchip = { ++ .name = "GPIO", ++ .irq_enable = bcm2708_gpio_irq_unmask, ++ .irq_disable = bcm2708_gpio_irq_mask, ++ .irq_unmask = bcm2708_gpio_irq_unmask, ++ .irq_mask = bcm2708_gpio_irq_mask, ++ .irq_set_type = bcm2708_gpio_irq_set_type, ++}; ++ ++static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id) ++{ ++ unsigned long edsr; ++ unsigned bank; ++ int i; ++ unsigned gpio; ++ unsigned level_bits; ++ struct bcm2708_gpio *gpio_data = dev_id; ++ ++ for (bank = 0; bank < GPIO_BANKS; bank++) { ++ edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank)); ++ level_bits = gpio_data->high[bank] | gpio_data->low[bank]; ++ ++ for_each_set_bit(i, &edsr, 32) { ++ gpio = i + bank * 32; ++ /* ack edge triggered IRQs immediately */ ++ if (!(level_bits & (1<gc.to_irq = bcm2708_gpio_to_irq; ++ ++ for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) { ++ irq_set_chip_data(irq, ucb); ++ irq_set_chip_and_handler(irq, &bcm2708_irqchip, ++ handle_simple_irq); ++ set_irq_flags(irq, IRQF_VALID); ++ } ++ ++ bcm2708_gpio_irq.dev_id = ucb; ++ setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq); ++} ++ ++#else ++ ++static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) ++{ ++} ++ ++#endif /* #if BCM_GPIO_USE_IRQ ***************************************************************************************************************** */ ++ ++static int bcm2708_gpio_probe(struct platform_device *dev) ++{ ++ struct bcm2708_gpio *ucb; ++ struct resource *res; ++ int bank; ++ int err = 0; ++ ++ printk(KERN_INFO DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev); ++ ++ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL); ++ if (NULL == ucb) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ res = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ ++ platform_set_drvdata(dev, ucb); ++ ucb->base = __io_address(GPIO_BASE); ++ ++ ucb->gc.label = "bcm2708_gpio"; ++ ucb->gc.base = 0; ++ ucb->gc.ngpio = BCM2708_NR_GPIOS; ++ ucb->gc.owner = THIS_MODULE; ++ ++ ucb->gc.direction_input = bcm2708_gpio_dir_in; ++ ucb->gc.direction_output = bcm2708_gpio_dir_out; ++ ucb->gc.get = bcm2708_gpio_get; ++ ucb->gc.set = bcm2708_gpio_set; ++ ucb->gc.can_sleep = 0; ++ ++ for (bank = 0; bank < GPIO_BANKS; bank++) { ++ writel(0, ucb->base + GPIOREN(bank)); ++ writel(0, ucb->base + GPIOFEN(bank)); ++ writel(0, ucb->base + GPIOHEN(bank)); ++ writel(0, ucb->base + GPIOLEN(bank)); ++ writel(0, ucb->base + GPIOAREN(bank)); ++ writel(0, ucb->base + GPIOAFEN(bank)); ++ writel(~0, ucb->base + GPIOEDS(bank)); ++ } ++ ++ bcm2708_gpio_irq_init(ucb); ++ ++ err = gpiochip_add(&ucb->gc); ++ ++err: ++ return err; ++ ++} ++ ++static int bcm2708_gpio_remove(struct platform_device *dev) ++{ ++ int err = 0; ++ struct bcm2708_gpio *ucb = platform_get_drvdata(dev); ++ ++ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev); ++ ++ gpiochip_remove(&ucb->gc); ++ ++ platform_set_drvdata(dev, NULL); ++ kfree(ucb); ++ ++ return err; ++} ++ ++static struct platform_driver bcm2708_gpio_driver = { ++ .probe = bcm2708_gpio_probe, ++ .remove = bcm2708_gpio_remove, ++ .driver = { ++ .name = "bcm2708_gpio"}, ++}; ++ ++static int __init bcm2708_gpio_init(void) ++{ ++ return platform_driver_register(&bcm2708_gpio_driver); ++} ++ ++static void __exit bcm2708_gpio_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_gpio_driver); ++} ++ ++module_init(bcm2708_gpio_init); ++module_exit(bcm2708_gpio_exit); ++ ++MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +new file mode 100644 +index 0000000..c089f4a +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -0,0 +1,1257 @@ ++/* ++ * linux/arch/arm/mach-bcm2709/bcm2709.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "bcm2709.h" ++#include "armctrl.h" ++ ++#ifdef CONFIG_BCM_VC_CMA ++#include ++#endif ++ ++//#define SYSTEM_TIMER ++ ++/* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to ++ * give us IO access only to 64Mbytes of physical memory (26 bits). We could ++ * represent this window by setting our dmamasks to 26 bits but, in fact ++ * we're not going to use addresses outside this range (they're not in real ++ * memory) so we don't bother. ++ * ++ * In the future we might include code to use this IOMMU to remap other ++ * physical addresses onto VideoCore memory then the use of 32-bits would be ++ * more legitimate. ++ */ ++#define DMA_MASK_BITS_COMMON 32 ++ ++// use GPIO 4 for the one-wire GPIO pin, if enabled ++#define W1_GPIO 4 ++// ensure one-wire GPIO pullup is disabled by default ++#define W1_PULLUP -1 ++ ++/* command line parameters */ ++static unsigned boardrev, serial; ++static unsigned uart_clock = UART0_CLOCK; ++static unsigned disk_led_gpio = 16; ++static unsigned disk_led_active_low = 1; ++static unsigned reboot_part = 0; ++static unsigned w1_gpio_pin = W1_GPIO; ++static unsigned w1_gpio_pullup = W1_PULLUP; ++ ++static unsigned use_dt = 0; ++ ++static void __init bcm2709_init_led(void); ++ ++void __init bcm2709_init_irq(void) ++{ ++ armctrl_init(__io_address(ARMCTRL_IC_BASE), 0, 0, 0); ++} ++ ++static struct map_desc bcm2709_io_desc[] __initdata = { ++ { ++ .virtual = IO_ADDRESS(ARMCTRL_BASE), ++ .pfn = __phys_to_pfn(ARMCTRL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART0_BASE), ++ .pfn = __phys_to_pfn(UART0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART1_BASE), ++ .pfn = __phys_to_pfn(UART1_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(DMA_BASE), ++ .pfn = __phys_to_pfn(DMA_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(MCORE_BASE), ++ .pfn = __phys_to_pfn(MCORE_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(ST_BASE), ++ .pfn = __phys_to_pfn(ST_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(USB_BASE), ++ .pfn = __phys_to_pfn(USB_BASE), ++ .length = SZ_128K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(PM_BASE), ++ .pfn = __phys_to_pfn(PM_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(GPIO_BASE), ++ .pfn = __phys_to_pfn(GPIO_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(ARM_LOCAL_BASE), ++ .pfn = __phys_to_pfn(ARM_LOCAL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++}; ++ ++void __init bcm2709_map_io(void) ++{ ++ iotable_init(bcm2709_io_desc, ARRAY_SIZE(bcm2709_io_desc)); ++} ++ ++#ifdef SYSTEM_TIMER ++ ++/* The STC is a free running counter that increments at the rate of 1MHz */ ++#define STC_FREQ_HZ 1000000 ++ ++static inline uint32_t timer_read(void) ++{ ++ /* STC: a free running counter that increments at the rate of 1MHz */ ++ return readl(__io_address(ST_BASE + 0x04)); ++} ++ ++static unsigned long bcm2709_read_current_timer(void) ++{ ++ return timer_read(); ++} ++ ++static u64 notrace bcm2709_read_sched_clock(void) ++{ ++ return timer_read(); ++} ++ ++static cycle_t clksrc_read(struct clocksource *cs) ++{ ++ return timer_read(); ++} ++ ++static struct clocksource clocksource_stc = { ++ .name = "stc", ++ .rating = 300, ++ .read = clksrc_read, ++ .mask = CLOCKSOURCE_MASK(32), ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++}; ++ ++unsigned long frc_clock_ticks32(void) ++{ ++ return timer_read(); ++} ++ ++static void __init bcm2709_clocksource_init(void) ++{ ++ if (clocksource_register_hz(&clocksource_stc, STC_FREQ_HZ)) { ++ printk(KERN_ERR "timer: failed to initialize clock " ++ "source %s\n", clocksource_stc.name); ++ } ++} ++#endif ++ ++struct clk __init *bcm2709_clk_register(const char *name, unsigned long fixed_rate) ++{ ++ struct clk *clk; ++ ++ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, ++ fixed_rate); ++ if (IS_ERR(clk)) ++ pr_err("%s not registered\n", name); ++ ++ return clk; ++} ++ ++void __init bcm2709_register_clkdev(struct clk *clk, const char *name) ++{ ++ int ret; ++ ++ ret = clk_register_clkdev(clk, NULL, name); ++ if (ret) ++ pr_err("%s alias not registered\n", name); ++} ++ ++void __init bcm2709_init_clocks(void) ++{ ++ struct clk *clk; ++ ++ clk = bcm2709_clk_register("uart0_clk", uart_clock); ++ bcm2709_register_clkdev(clk, "dev:f1"); ++ ++ clk = bcm2709_clk_register("sdhost_clk", 250000000); ++ bcm2709_register_clkdev(clk, "bcm2708_spi.0"); ++ bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); ++ bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); ++} ++ ++#define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } ++#define UART0_DMA { 15, 14 } ++ ++AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); ++ ++static struct amba_device *amba_devs[] __initdata = { ++ &uart0_device, ++}; ++ ++static struct resource bcm2708_dmaman_resources[] = { ++ { ++ .start = DMA_BASE, ++ .end = DMA_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_dmaman_device = { ++ .name = BCM_DMAMAN_DRIVER_NAME, ++ .id = 0, /* first bcm2708_dma */ ++ .resource = bcm2708_dmaman_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), ++}; ++ ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++static struct w1_gpio_platform_data w1_gpio_pdata = { ++ .pin = W1_GPIO, ++ .ext_pullup_enable_pin = W1_PULLUP, ++ .is_open_drain = 0, ++}; ++ ++static struct platform_device w1_device = { ++ .name = "w1-gpio", ++ .id = -1, ++ .dev.platform_data = &w1_gpio_pdata, ++}; ++#endif ++ ++static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_fb_device = { ++ .name = "bcm2708_fb", ++ .id = -1, /* only one bcm2708_fb */ ++ .resource = NULL, ++ .num_resources = 0, ++ .dev = { ++ .dma_mask = &fb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { ++ { ++ .mapbase = UART1_BASE + 0x40, ++ .irq = IRQ_AUX, ++ .uartclk = 125000000, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, ++ .type = PORT_8250, ++ }, ++ {}, ++}; ++ ++static struct platform_device bcm2708_uart1_device = { ++ .name = "serial8250", ++ .id = PLAT8250_DEV_PLATFORM, ++ .dev = { ++ .platform_data = bcm2708_uart1_platform_data, ++ }, ++}; ++ ++static struct resource bcm2708_usb_resources[] = { ++ [0] = { ++ .start = USB_BASE, ++ .end = USB_BASE + SZ_128K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = MPHI_BASE, ++ .end = MPHI_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = IRQ_HOSTPORT, ++ .end = IRQ_HOSTPORT, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [3] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [4] = { ++ .start = ARM_LOCAL_BASE, ++ .end = ARM_LOCAL_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [5] = { ++ .start = IRQ_ARM_LOCAL_MAILBOX1, ++ .end = IRQ_ARM_LOCAL_MAILBOX1, ++ .flags = IORESOURCE_IRQ ++ }, ++}; ++ ++ ++static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_usb_device = { ++ .name = "bcm2708_usb", ++ .id = -1, /* only one bcm2708_usb */ ++ .resource = bcm2708_usb_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), ++ .dev = { ++ .dma_mask = &usb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct resource bcm2708_vcio_resources[] = { ++ [0] = { /* mailbox/semaphore/doorbell access */ ++ .start = MCORE_BASE, ++ .end = MCORE_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_vcio_device = { ++ .name = BCM_VCIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vcio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), ++ .dev = { ++ .dma_mask = &vcio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++#ifdef CONFIG_BCM2708_GPIO ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++ ++static struct resource bcm2708_gpio_resources[] = { ++ [0] = { /* general purpose I/O */ ++ .start = GPIO_BASE, ++ .end = GPIO_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_gpio_device = { ++ .name = BCM_GPIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_gpio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), ++ .dev = { ++ .dma_mask = &gpio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef SYSTEM_TIMER ++static struct resource bcm2708_systemtimer_resources[] = { ++ [0] = { /* system timer access */ ++ .start = ST_BASE, ++ .end = ST_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = IRQ_TIMER3, ++ .end = IRQ_TIMER3, ++ .flags = IORESOURCE_IRQ, ++ } ++ ++}; ++ ++static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_systemtimer_device = { ++ .name = "bcm2708_systemtimer", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_systemtimer_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), ++ .dev = { ++ .dma_mask = &systemtimer_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ ++static struct resource bcm2835_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2835_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2835_emmc_device = { ++ .name = "mmc-bcm2835", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), ++ .resource = bcm2835_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2835_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++#endif /* CONFIG_MMC_BCM2835 */ ++ ++static struct resource bcm2708_powerman_resources[] = { ++ [0] = { ++ .start = PM_BASE, ++ .end = PM_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++struct platform_device bcm2708_powerman_device = { ++ .name = "bcm2708_powerman", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), ++ .resource = bcm2708_powerman_resources, ++ .dev = { ++ .dma_mask = &powerman_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++ ++ ++static struct platform_device bcm2708_alsa_devices[] = { ++ [0] = { ++ .name = "bcm2835_AUD0", ++ .id = 0, /* first audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [1] = { ++ .name = "bcm2835_AUD1", ++ .id = 1, /* second audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [2] = { ++ .name = "bcm2835_AUD2", ++ .id = 2, /* third audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [3] = { ++ .name = "bcm2835_AUD3", ++ .id = 3, /* forth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [4] = { ++ .name = "bcm2835_AUD4", ++ .id = 4, /* fifth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [5] = { ++ .name = "bcm2835_AUD5", ++ .id = 5, /* sixth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [6] = { ++ .name = "bcm2835_AUD6", ++ .id = 6, /* seventh audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++ [7] = { ++ .name = "bcm2835_AUD7", ++ .id = 7, /* eighth audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, ++}; ++ ++static struct resource bcm2708_spi_resources[] = { ++ { ++ .start = SPI0_BASE, ++ .end = SPI0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SPI, ++ .end = IRQ_SPI, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++static struct platform_device bcm2708_spi_device = { ++ .name = "bcm2708_spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), ++ .resource = bcm2708_spi_resources, ++ .dev = { ++ .dma_mask = &bcm2708_spi_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, ++}; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++static struct spi_board_info bcm2708_spi_devices[] = { ++#ifdef CONFIG_SPI_SPIDEV ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = SPI_MODE_0, ++ }, { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 1, ++ .mode = SPI_MODE_0, ++ } ++#endif ++}; ++#endif ++ ++static struct resource bcm2708_bsc0_resources[] = { ++ { ++ .start = BSC0_BASE, ++ .end = BSC0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc0_device = { ++ .name = "bcm2708_i2c", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), ++ .resource = bcm2708_bsc0_resources, ++}; ++ ++ ++static struct resource bcm2708_bsc1_resources[] = { ++ { ++ .start = BSC1_BASE, ++ .end = BSC1_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc1_device = { ++ .name = "bcm2708_i2c", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), ++ .resource = bcm2708_bsc1_resources, ++}; ++ ++static struct platform_device bcm2835_hwmon_device = { ++ .name = "bcm2835_hwmon", ++}; ++ ++static struct platform_device bcm2835_thermal_device = { ++ .name = "bcm2835_thermal", ++}; ++ ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++static struct resource bcm2708_i2s_resources[] = { ++ { ++ .start = I2S_BASE, ++ .end = I2S_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PCM_CLOCK_BASE, ++ .end = PCM_CLOCK_BASE + 0x02, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_i2s_device = { ++ .name = "bcm2708-i2s", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), ++ .resource = bcm2708_i2s_resources, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++static struct platform_device snd_hifiberry_dac_device = { ++ .name = "snd-hifiberry-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm5102a_codec_device = { ++ .name = "pcm5102a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) ++static struct platform_device snd_rpi_hifiberry_dacplus_device = { ++ .name = "snd-rpi-hifiberry-dacplus", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_pcm512x_hbdacplus_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4d) ++ }, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++static struct platform_device snd_hifiberry_digi_device = { ++ .name = "snd-hifiberry-digi", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("wm8804", 0x3b) ++ }, ++}; ++ ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) ++static struct platform_device snd_hifiberry_amp_device = { ++ .name = "snd-hifiberry-amp", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_tas5713_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("tas5713", 0x1b) ++ }, ++}; ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++static struct platform_device snd_rpi_dac_device = { ++ .name = "snd-rpi-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm1794a_codec_device = { ++ .name = "pcm1794a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++static struct platform_device snd_rpi_iqaudio_dac_device = { ++ .name = "snd-rpi-iqaudio-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++// Use the actual device name rather than generic driver name ++static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4c) ++ }, ++}; ++#endif ++ ++int __init bcm_register_device(struct platform_device *pdev) ++{ ++ int ret; ++ ++ ret = platform_device_register(pdev); ++ if (ret) ++ pr_debug("Unable to register platform device '%s': %d\n", ++ pdev->name, ret); ++ ++ return ret; ++} ++ ++/* ++ * Use these macros for platform and i2c devices that are present in the ++ * Device Tree. This way the devices are only added on non-DT systems. ++ */ ++#define bcm_register_device_dt(pdev) \ ++ if (!use_dt) bcm_register_device(pdev) ++ ++#define i2c_register_board_info_dt(busnum, info, n) \ ++ if (!use_dt) i2c_register_board_info(busnum, info, n) ++ ++int calc_rsts(int partition) ++{ ++ return PM_PASSWORD | ++ ((partition & (1 << 0)) << 0) | ++ ((partition & (1 << 1)) << 1) | ++ ((partition & (1 << 2)) << 2) | ++ ((partition & (1 << 3)) << 3) | ++ ((partition & (1 << 4)) << 4) | ++ ((partition & (1 << 5)) << 5); ++} ++ ++static void bcm2709_restart(enum reboot_mode mode, const char *cmd) ++{ ++ extern char bcm2708_reboot_mode; ++ uint32_t pm_rstc, pm_wdog; ++ uint32_t timeout = 10; ++ uint32_t pm_rsts = 0; ++ ++ if(bcm2708_reboot_mode == 'q') ++ { ++ // NOOBS < 1.3 booting with reboot=q ++ pm_rsts = readl(__io_address(PM_RSTS)); ++ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRQ_SET; ++ } ++ else if(bcm2708_reboot_mode == 'p') ++ { ++ // NOOBS < 1.3 halting ++ pm_rsts = readl(__io_address(PM_RSTS)); ++ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRH_SET; ++ } ++ else ++ { ++ pm_rsts = calc_rsts(reboot_part); ++ } ++ ++ writel(pm_rsts, __io_address(PM_RSTS)); ++ ++ /* Setup watchdog for reset */ ++ pm_rstc = readl(__io_address(PM_RSTC)); ++ ++ pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0) ++ pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; ++ ++ writel(pm_wdog, __io_address(PM_WDOG)); ++ writel(pm_rstc, __io_address(PM_RSTC)); ++} ++ ++/* We can't really power off, but if we do the normal reset scheme, and indicate to bootcode.bin not to reboot, then most of the chip will be powered off */ ++static void bcm2709_power_off(void) ++{ ++ extern char bcm2708_reboot_mode; ++ if(bcm2708_reboot_mode == 'q') ++ { ++ // NOOBS < v1.3 ++ bcm2709_restart('p', ""); ++ } ++ else ++ { ++ /* partition 63 is special code for HALT the bootloader knows not to boot*/ ++ reboot_part = 63; ++ /* continue with normal reset mechanism */ ++ bcm2709_restart(0, ""); ++ } ++} ++ ++#ifdef CONFIG_OF ++static void __init bcm2709_dt_init(void) ++{ ++ int ret; ++ ++ of_clk_init(NULL); ++ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++ if (ret) { ++ pr_err("of_platform_populate failed: %d\n", ret); ++ /* Proceed as if CONFIG_OF was not defined */ ++ } else { ++ use_dt = 1; ++ } ++} ++#else ++static void __init bcm2709_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ ++void __init bcm2709_init(void) ++{ ++ int i; ++ ++#if defined(CONFIG_BCM_VC_CMA) ++ vc_cma_early_init(); ++#endif ++ printk("bcm2709.uart_clock = %d\n", uart_clock); ++ pm_power_off = bcm2709_power_off; ++ ++ bcm2709_init_clocks(); ++ bcm2709_dt_init(); ++ ++ bcm_register_device(&bcm2708_dmaman_device); ++ bcm_register_device(&bcm2708_vcio_device); ++#ifdef CONFIG_BCM2708_GPIO ++ bcm_register_device_dt(&bcm2708_gpio_device); ++#endif ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++ w1_gpio_pdata.pin = w1_gpio_pin; ++ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; ++ bcm_register_device_dt(&w1_device); ++#endif ++#ifdef SYSTEM_TIMER ++ bcm_register_device(&bcm2708_systemtimer_device); ++#endif ++ bcm_register_device(&bcm2708_fb_device); ++ bcm_register_device(&bcm2708_usb_device); ++ bcm_register_device(&bcm2708_uart1_device); ++ bcm_register_device(&bcm2708_powerman_device); ++ ++#ifdef CONFIG_MMC_BCM2835 ++ bcm_register_device(&bcm2835_emmc_device); ++#endif ++ bcm2709_init_led(); ++ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) ++ bcm_register_device(&bcm2708_alsa_devices[i]); ++ ++ bcm_register_device(&bcm2835_hwmon_device); ++ bcm_register_device(&bcm2835_thermal_device); ++ ++ bcm_register_device_dt(&bcm2708_spi_device); ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++ bcm_register_device_dt(&bcm2708_i2s_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_dac_device); ++ bcm_register_device_dt(&snd_pcm5102a_codec_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) ++ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_digi_device); ++ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_amp_device); ++ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_dac_device); ++ bcm_register_device_dt(&snd_pcm1794a_codec_device); ++#endif ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); ++#endif ++ ++ ++ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { ++ struct amba_device *d = amba_devs[i]; ++ amba_device_register(d, &iomem_resource); ++ } ++ system_rev = boardrev; ++ system_serial_low = serial; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++ if (!use_dt) ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); ++#endif ++} ++ ++#ifdef SYSTEM_TIMER ++static void timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *clk) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: /* Leave the timer disabled, .set_next_event will enable it */ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ break; ++ case CLOCK_EVT_MODE_PERIODIC: ++ ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_RESUME: ++ ++ default: ++ printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", ++ (int)mode); ++ break; ++ } ++ ++} ++ ++static int timer_set_next_event(unsigned long cycles, ++ struct clock_event_device *unused) ++{ ++ unsigned long stc; ++ do { ++ stc = readl(__io_address(ST_BASE + 0x04)); ++ /* We could take a FIQ here, which may push ST above STC3 */ ++ writel(stc + cycles, __io_address(ST_BASE + 0x18)); ++ } while ((signed long) cycles >= 0 && ++ (signed long) (readl(__io_address(ST_BASE + 0x04)) - stc) ++ >= (signed long) cycles); ++ return 0; ++} ++ ++static struct clock_event_device timer0_clockevent = { ++ .name = "timer0", ++ .shift = 32, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_mode = timer_set_mode, ++ .set_next_event = timer_set_next_event, ++}; ++ ++/* ++ * IRQ handler for the timer ++ */ ++static irqreturn_t bcm2709_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = &timer0_clockevent; ++ ++ writel(1 << 3, __io_address(ST_BASE + 0x00)); /* stcs clear timer int */ ++ ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction bcm2709_timer_irq = { ++ .name = "BCM2709 Timer Tick", ++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, ++ .handler = bcm2709_timer_interrupt, ++}; ++ ++/* ++ * Set up timer interrupt, and return the current time in seconds. ++ */ ++ ++static struct delay_timer bcm2709_delay_timer = { ++ .read_current_timer = bcm2709_read_current_timer, ++ .freq = STC_FREQ_HZ, ++}; ++ ++static void __init bcm2709_timer_init(void) ++{ ++ /* init high res timer */ ++ bcm2709_clocksource_init(); ++ ++ /* ++ * Make irqs happen for the system timer ++ */ ++ setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); ++ ++ sched_clock_register(bcm2709_read_sched_clock, 32, STC_FREQ_HZ); ++ ++ timer0_clockevent.mult = ++ div_sc(STC_FREQ_HZ, NSEC_PER_SEC, timer0_clockevent.shift); ++ timer0_clockevent.max_delta_ns = ++ clockevent_delta2ns(0xffffffff, &timer0_clockevent); ++ timer0_clockevent.min_delta_ns = ++ clockevent_delta2ns(0xf, &timer0_clockevent); ++ ++ timer0_clockevent.cpumask = cpumask_of(0); ++ clockevents_register_device(&timer0_clockevent); ++ ++ register_current_timer_delay(&bcm2708_delay_timer); ++} ++ ++#else ++ ++static void __init bcm2709_timer_init(void) ++{ ++ extern void dc4_arch_timer_init(void); ++ // timer control ++ writel(0, __io_address(ARM_LOCAL_CONTROL)); ++ // timer pre_scaler ++ writel(0x80000000, __io_address(ARM_LOCAL_PRESCALER)); // 19.2MHz ++ //writel(0x06AAAAAB, __io_address(ARM_LOCAL_PRESCALER)); // 1MHz ++ ++ if (use_dt) ++ { ++ of_clk_init(NULL); ++ clocksource_of_init(); ++ } ++ else ++ dc4_arch_timer_init(); ++} ++ ++#endif ++ ++#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) ++#include ++ ++static struct gpio_led bcm2709_leds[] = { ++ [0] = { ++ .gpio = 16, ++ .name = "led0", ++ .default_trigger = "mmc0", ++ .active_low = 1, ++ }, ++}; ++ ++static struct gpio_led_platform_data bcm2709_led_pdata = { ++ .num_leds = ARRAY_SIZE(bcm2709_leds), ++ .leds = bcm2709_leds, ++}; ++ ++static struct platform_device bcm2709_led_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &bcm2709_led_pdata, ++ }, ++}; ++ ++static void __init bcm2709_init_led(void) ++{ ++ bcm2709_leds[0].gpio = disk_led_gpio; ++ bcm2709_leds[0].active_low = disk_led_active_low; ++ bcm_register_device_dt(&bcm2709_led_device); ++} ++#else ++static inline void bcm2709_init_led(void) ++{ ++} ++#endif ++ ++void __init bcm2709_init_early(void) ++{ ++ /* ++ * Some devices allocate their coherent buffers from atomic ++ * context. Increase size of atomic coherent pool to make sure such ++ * the allocations won't fail. ++ */ ++ init_dma_coherent_pool_size(SZ_4M); ++} ++ ++static void __init board_reserve(void) ++{ ++#if defined(CONFIG_BCM_VC_CMA) ++ vc_cma_reserve(); ++#endif ++} ++ ++ ++#include ++ ++#include ++#include ++#include ++int dc4=0; ++//void dc4_log(unsigned x) { if (dc4) writel((x), __io_address(ST_BASE+10 + raw_smp_processor_id()*4)); } ++void dc4_log_dead(unsigned x) { if (dc4) writel((readl(__io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)) & 0xffff) | ((x)<<16), __io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)); } ++ ++static void bcm2835_send_doorbell(const struct cpumask *mask, unsigned int irq) ++{ ++ int cpu; ++ /* ++ * Ensure that stores to Normal memory are visible to the ++ * other CPUs before issuing the IPI. ++ */ ++ dsb(); ++ ++ /* Convert our logical CPU mask into a physical one. */ ++ for_each_cpu(cpu, mask) ++ { ++ /* submit softirq */ ++ writel(1<%x)\n", __FUNCTION__, (unsigned)virt_to_phys((void *)secondary_startup), (unsigned)__io_address(ST_BASE + 0x10)); ++ printk("[%s] ncores=%d\n", __FUNCTION__, ncores); ++ ++ for (i = 0; i < ncores; i++) { ++ set_cpu_possible(i, true); ++ /* enable IRQ (not FIQ) */ ++ writel(0x1, __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i)); ++ //writel(0xf, __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 0x4 * i)); ++ } ++ set_smp_cross_call(bcm2835_send_doorbell); ++} ++ ++/* ++ * for arch/arm/kernel/smp.c:smp_prepare_cpus(unsigned int max_cpus) ++ */ ++void __init bcm2709_smp_prepare_cpus(unsigned int max_cpus) ++{ ++ //void __iomem *scu_base; ++ ++ printk("[%s] enter\n", __FUNCTION__); ++ //scu_base = scu_base_addr(); ++ //scu_enable(scu_base); ++} ++ ++/* ++ * for linux/arch/arm/kernel/smp.c:secondary_start_kernel(void) ++ */ ++void __cpuinit bcm2709_secondary_init(unsigned int cpu) ++{ ++ printk("[%s] enter cpu:%d\n", __FUNCTION__, cpu); ++ //gic_secondary_init(0); ++} ++ ++/* ++ * for linux/arch/arm/kernel/smp.c:__cpu_up(..) ++ */ ++int __cpuinit bcm2709_boot_secondary(unsigned int cpu, struct task_struct *idle) ++{ ++ void secondary_startup(void); ++ void *mbox_set = __io_address(ARM_LOCAL_MAILBOX3_SET0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); ++ void *mbox_clr = __io_address(ARM_LOCAL_MAILBOX3_CLR0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); ++ unsigned secondary_boot = (unsigned)virt_to_phys((void *)secondary_startup); ++ int timeout=20; ++ unsigned t = -1; ++ //printk("[%s] enter cpu:%d (%x->%p) %x\n", __FUNCTION__, cpu, secondary_boot, wake, readl(wake)); ++ ++ dsb(); ++ BUG_ON(readl(mbox_clr) != 0); ++ writel(secondary_boot, mbox_set); ++ ++ while (--timeout > 0) { ++ t = readl(mbox_clr); ++ if (t == 0) break; ++ cpu_relax(); ++ } ++ if (timeout==0) ++ printk("[%s] cpu:%d failed to start (%x)\n", __FUNCTION__, cpu, t); ++ else ++ printk("[%s] cpu:%d started (%x) %d\n", __FUNCTION__, cpu, t, timeout); ++ ++ return 0; ++} ++ ++ ++struct smp_operations bcm2709_smp_ops __initdata = { ++ .smp_init_cpus = bcm2709_smp_init_cpus, ++ .smp_prepare_cpus = bcm2709_smp_prepare_cpus, ++ .smp_secondary_init = bcm2709_secondary_init, ++ .smp_boot_secondary = bcm2709_boot_secondary, ++}; ++ ++static const char * const bcm2709_compat[] = { ++ "brcm,bcm2709", ++ "brcm,bcm2708", /* Could use bcm2708 in a pinch */ ++ NULL ++}; ++ ++MACHINE_START(BCM2709, "BCM2709") ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .smp = smp_ops(bcm2709_smp_ops), ++ .map_io = bcm2709_map_io, ++ .init_irq = bcm2709_init_irq, ++ .init_time = bcm2709_timer_init, ++ .init_machine = bcm2709_init, ++ .init_early = bcm2709_init_early, ++ .reserve = board_reserve, ++ .restart = bcm2709_restart, ++ .dt_compat = bcm2709_compat, ++MACHINE_END ++ ++MACHINE_START(BCM2708, "BCM2709") ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .smp = smp_ops(bcm2709_smp_ops), ++ .map_io = bcm2709_map_io, ++ .init_irq = bcm2709_init_irq, ++ .init_time = bcm2709_timer_init, ++ .init_machine = bcm2709_init, ++ .init_early = bcm2709_init_early, ++ .reserve = board_reserve, ++ .restart = bcm2709_restart, ++ .dt_compat = bcm2709_compat, ++MACHINE_END ++ ++module_param(boardrev, uint, 0644); ++module_param(serial, uint, 0644); ++module_param(uart_clock, uint, 0644); ++module_param(disk_led_gpio, uint, 0644); ++module_param(disk_led_active_low, uint, 0644); ++module_param(reboot_part, uint, 0644); ++module_param(w1_gpio_pin, uint, 0644); ++module_param(w1_gpio_pullup, uint, 0644); +diff --git a/arch/arm/mach-bcm2709/bcm2709.h b/arch/arm/mach-bcm2709/bcm2709.h +new file mode 100644 +index 0000000..e339a93 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/bcm2709.h +@@ -0,0 +1,49 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708.h ++ * ++ * BCM2708 machine support header ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_BCM2708_H ++#define __BCM2708_BCM2708_H ++ ++#include ++ ++extern void __init bcm2708_init(void); ++extern void __init bcm2708_init_irq(void); ++extern void __init bcm2708_map_io(void); ++extern struct sys_timer bcm2708_timer; ++extern unsigned int mmc_status(struct device *dev); ++ ++#define AMBA_DEVICE(name, busid, base, plat) \ ++static struct amba_device name##_device = { \ ++ .dev = { \ ++ .coherent_dma_mask = ~0, \ ++ .init_name = busid, \ ++ .platform_data = plat, \ ++ }, \ ++ .res = { \ ++ .start = base##_BASE, \ ++ .end = (base##_BASE) + SZ_4K - 1,\ ++ .flags = IORESOURCE_MEM, \ ++ }, \ ++ .irq = base##_IRQ, \ ++} ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/clock.c b/arch/arm/mach-bcm2709/clock.c +new file mode 100644 +index 0000000..4fc556e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/clock.c +@@ -0,0 +1,61 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "clock.h" ++ ++int clk_enable(struct clk *clk) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++void clk_disable(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_disable); ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_round_rate); ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ return -EIO; ++} ++EXPORT_SYMBOL(clk_set_rate); +diff --git a/arch/arm/mach-bcm2709/clock.h b/arch/arm/mach-bcm2709/clock.h +new file mode 100644 +index 0000000..5f9d725 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/clock.h +@@ -0,0 +1,24 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++struct module; ++ ++struct clk { ++ unsigned long rate; ++}; +diff --git a/arch/arm/mach-bcm2709/delay.S b/arch/arm/mach-bcm2709/delay.S +new file mode 100644 +index 0000000..06f4780 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/delay.S +@@ -0,0 +1,21 @@ ++/* ++ * linux/arch/arm/lib/delay.S ++ * ++ * Copyright (C) 1995, 1996 Russell King ++ * ++ * 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 ++#include ++ ++ .text ++.align 3 @ 8 byte alignment seems to be needed to avoid fetching stalls ++@ Delay routine ++ENTRY(bcm2708_delay) ++ subs r0, r0, #1 ++ bhi bcm2708_delay ++ mov pc, lr ++ENDPROC(bcm2708_delay) +diff --git a/arch/arm/mach-bcm2709/dma.c b/arch/arm/mach-bcm2709/dma.c +new file mode 100644 +index 0000000..a5e58d1 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/dma.c +@@ -0,0 +1,409 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/dma.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++/*****************************************************************************\ ++ * * ++ * Configuration * ++ * * ++\*****************************************************************************/ ++ ++#define CACHE_LINE_MASK 31 ++#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME ++#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ ++ ++/* valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */ ++#define BCM2708_DMA_CHANIO(dma_base, n) \ ++ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n))) ++ ++ ++/*****************************************************************************\ ++ * * ++ * DMA Auxilliary Functions * ++ * * ++\*****************************************************************************/ ++ ++/* A DMA buffer on an arbitrary boundary may separate a cache line into a ++ section inside the DMA buffer and another section outside it. ++ Even if we flush DMA buffers from the cache there is always the chance that ++ during a DMA someone will access the part of a cache line that is outside ++ the DMA buffer - which will then bring in unwelcome data. ++ Without being able to dictate our own buffer pools we must insist that ++ DMA buffers consist of a whole number of cache lines. ++*/ ++ ++extern int ++bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) ++{ ++ int i; ++ ++ for (i = 0; i < sg_len; i++) { ++ if (sg_ptr[i].offset & CACHE_LINE_MASK || ++ sg_ptr[i].length & CACHE_LINE_MASK) ++ return 0; ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); ++ ++extern void ++bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block) ++{ ++ dsb(); /* ARM data synchronization (push) operation */ ++ ++ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); ++ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); ++} ++ ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) ++{ ++ dsb(); ++ ++ /* ugly busy wait only option for now */ ++ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE) ++ cpu_relax(); ++} ++ ++EXPORT_SYMBOL_GPL(bcm_dma_start); ++ ++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base) ++{ ++ dsb(); ++ ++ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_is_busy); ++ ++/* Complete an ongoing DMA (assuming its results are to be ignored) ++ Does nothing if there is no DMA in progress. ++ This routine waits for the current AXI transfer to complete before ++ terminating the current DMA. If the current transfer is hung on a DREQ used ++ by an uncooperative peripheral the AXI transfer may never complete. In this ++ case the routine times out and return a non-zero error code. ++ Use of this routine doesn't guarantee that the ongoing or aborted DMA ++ does not produce an interrupt. ++*/ ++extern int ++bcm_dma_abort(void __iomem *dma_chan_base) ++{ ++ unsigned long int cs; ++ int rc = 0; ++ ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (BCM2708_DMA_ACTIVE & cs) { ++ long int timeout = 10000; ++ ++ /* write 0 to the active bit - pause the DMA */ ++ writel(0, dma_chan_base + BCM2708_DMA_CS); ++ ++ /* wait for any current AXI transfer to complete */ ++ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (0 != (cs & BCM2708_DMA_ISPAUSED)) { ++ /* we'll un-pause when we set of our next DMA */ ++ rc = -ETIMEDOUT; ++ ++ } else if (BCM2708_DMA_ACTIVE & cs) { ++ /* terminate the control block chain */ ++ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); ++ ++ /* abort the whole DMA */ ++ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, ++ dma_chan_base + BCM2708_DMA_CS); ++ } ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_abort); ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Device Methods * ++ * * ++\*****************************************************************************/ ++ ++struct vc_dmaman { ++ void __iomem *dma_base; ++ u32 chan_available; /* bitmap of available channels */ ++ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ ++}; ++ ++static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, ++ u32 chans_available) ++{ ++ dmaman->dma_base = dma_base; ++ dmaman->chan_available = chans_available; ++ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ ++} ++ ++static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, ++ unsigned preferred_feature_set) ++{ ++ u32 chans; ++ int feature; ++ ++ chans = dmaman->chan_available; ++ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) ++ /* select the subset of available channels with the desired ++ feature so long as some of the candidate channels have that ++ feature */ ++ if ((preferred_feature_set & (1 << feature)) && ++ (chans & dmaman->has_feature[feature])) ++ chans &= dmaman->has_feature[feature]; ++ ++ if (chans) { ++ int chan = 0; ++ /* return the ordinal of the first channel in the bitmap */ ++ while (chans != 0 && (chans & 1) == 0) { ++ chans >>= 1; ++ chan++; ++ } ++ /* claim the channel */ ++ dmaman->chan_available &= ~(1 << chan); ++ return chan; ++ } else ++ return -ENOMEM; ++} ++ ++static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) ++{ ++ if (chan < 0) ++ return -EINVAL; ++ else if ((1 << chan) & dmaman->chan_available) ++ return -EIDRM; ++ else { ++ dmaman->chan_available |= (1 << chan); ++ return 0; ++ } ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA IRQs * ++ * * ++\*****************************************************************************/ ++ ++static unsigned char bcm_dma_irqs[] = { ++ IRQ_DMA0, ++ IRQ_DMA1, ++ IRQ_DMA2, ++ IRQ_DMA3, ++ IRQ_DMA4, ++ IRQ_DMA5, ++ IRQ_DMA6, ++ IRQ_DMA7, ++ IRQ_DMA8, ++ IRQ_DMA9, ++ IRQ_DMA10, ++ IRQ_DMA11, ++ IRQ_DMA12 ++}; ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Monitor * ++ * * ++\*****************************************************************************/ ++ ++static struct device *dmaman_dev; /* we assume there's only one! */ ++ ++extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, ++ void __iomem **out_dma_base, int *out_dma_irq) ++{ ++ if (!dmaman_dev) ++ return -ENODEV; ++ else { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); ++ if (rc >= 0) { ++ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, ++ rc); ++ *out_dma_irq = bcm_dma_irqs[rc]; ++ } ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); ++ ++extern int bcm_dma_chan_free(int channel) ++{ ++ if (dmaman_dev) { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_free(dmaman, channel); ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_free); ++ ++static int dev_dmaman_register(const char *dev_name, struct device *dev) ++{ ++ int rc = dmaman_dev ? -EINVAL : 0; ++ dmaman_dev = dev; ++ return rc; ++} ++ ++static void dev_dmaman_deregister(const char *dev_name, struct device *dev) ++{ ++ dmaman_dev = NULL; ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA Device * ++ * * ++\*****************************************************************************/ ++ ++static int dmachans = -1; /* module parameter */ ++ ++static int bcm_dmaman_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_dmaman *dmaman; ++ struct resource *dma_res = NULL; ++ void __iomem *dma_base = NULL; ++ int have_dma_region = 0; ++ ++ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); ++ if (NULL == dmaman) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "DMA management memory\n"); ++ ret = -ENOMEM; ++ } else { ++ ++ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (dma_res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ } else if (!request_mem_region(dma_res->start, ++ resource_size(dma_res), ++ DRIVER_NAME)) { ++ dev_err(&pdev->dev, "cannot obtain DMA region\n"); ++ ret = -EBUSY; ++ } else { ++ have_dma_region = 1; ++ dma_base = ioremap(dma_res->start, ++ resource_size(dma_res)); ++ if (!dma_base) { ++ dev_err(&pdev->dev, "cannot map DMA region\n"); ++ ret = -ENOMEM; ++ } else { ++ /* use module parameter if one was provided */ ++ if (dmachans > 0) ++ vc_dmaman_init(dmaman, dma_base, ++ dmachans); ++ else ++ vc_dmaman_init(dmaman, dma_base, ++ DEFAULT_DMACHAN_BITMAP); ++ ++ platform_set_drvdata(pdev, dmaman); ++ dev_dmaman_register(DRIVER_NAME, &pdev->dev); ++ ++ printk(KERN_INFO DRIVER_NAME ": DMA manager " ++ "at %p\n", dma_base); ++ } ++ } ++ } ++ if (ret != 0) { ++ if (dma_base) ++ iounmap(dma_base); ++ if (dma_res && have_dma_region) ++ release_mem_region(dma_res->start, ++ resource_size(dma_res)); ++ if (dmaman) ++ kfree(dmaman); ++ } ++ return ret; ++} ++ ++static int bcm_dmaman_remove(struct platform_device *pdev) ++{ ++ struct vc_dmaman *dmaman = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); ++ kfree(dmaman); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_dmaman_driver = { ++ .probe = bcm_dmaman_probe, ++ .remove = bcm_dmaman_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Driver init/exit * ++ * * ++\*****************************************************************************/ ++ ++static int __init bcm_dmaman_drv_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&bcm_dmaman_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_dmaman_drv_exit(void) ++{ ++ platform_driver_unregister(&bcm_dmaman_driver); ++} ++ ++module_init(bcm_dmaman_drv_init); ++module_exit(bcm_dmaman_drv_exit); ++ ++module_param(dmachans, int, 0644); ++ ++MODULE_AUTHOR("Gray Girling "); ++MODULE_DESCRIPTION("DMA channel manager driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); +diff --git a/arch/arm/mach-bcm2709/dmaer.c b/arch/arm/mach-bcm2709/dmaer.c +new file mode 100755 +index 0000000..5b0f0ff +--- /dev/null ++++ b/arch/arm/mach-bcm2709/dmaer.c +@@ -0,0 +1,886 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#ifdef ECLIPSE_IGNORE ++ ++#define __user ++#define __init ++#define __exit ++#define __iomem ++#define KERN_DEBUG ++#define KERN_ERR ++#define KERN_WARNING ++#define KERN_INFO ++#define _IOWR(a, b, c) b ++#define _IOW(a, b, c) b ++#define _IO(a, b) b ++ ++#endif ++ ++//#define inline ++ ++#define PRINTK(args...) printk(args) ++//#define PRINTK_VERBOSE(args...) printk(args) ++//#define PRINTK(args...) ++#define PRINTK_VERBOSE(args...) ++ ++/***** TYPES ****/ ++#define PAGES_PER_LIST 500 ++struct PageList ++{ ++ struct page *m_pPages[PAGES_PER_LIST]; ++ unsigned int m_used; ++ struct PageList *m_pNext; ++}; ++ ++struct VmaPageList ++{ ++ //each vma has a linked list of pages associated with it ++ struct PageList *m_pPageHead; ++ struct PageList *m_pPageTail; ++ unsigned int m_refCount; ++}; ++ ++struct DmaControlBlock ++{ ++ unsigned int m_transferInfo; ++ void __user *m_pSourceAddr; ++ void __user *m_pDestAddr; ++ unsigned int m_xferLen; ++ unsigned int m_tdStride; ++ struct DmaControlBlock *m_pNext; ++ unsigned int m_blank1, m_blank2; ++}; ++ ++/***** DEFINES ******/ ++//magic number defining the module ++#define DMA_MAGIC 0xdd ++ ++//do user virtual to physical translation of the CB chain ++#define DMA_PREPARE _IOWR(DMA_MAGIC, 0, struct DmaControlBlock *) ++ ++//kick the pre-prepared CB chain ++#define DMA_KICK _IOW(DMA_MAGIC, 1, struct DmaControlBlock *) ++ ++//prepare it, kick it, wait for it ++#define DMA_PREPARE_KICK_WAIT _IOWR(DMA_MAGIC, 2, struct DmaControlBlock *) ++ ++//prepare it, kick it, don't wait for it ++#define DMA_PREPARE_KICK _IOWR(DMA_MAGIC, 3, struct DmaControlBlock *) ++ ++//not currently implemented ++#define DMA_WAIT_ONE _IO(DMA_MAGIC, 4, struct DmaControlBlock *) ++ ++//wait on all kicked CB chains ++#define DMA_WAIT_ALL _IO(DMA_MAGIC, 5) ++ ++//in order to discover the largest AXI burst that should be programmed into the transfer params ++#define DMA_MAX_BURST _IO(DMA_MAGIC, 6) ++ ++//set the address range through which the user address is assumed to already by a physical address ++#define DMA_SET_MIN_PHYS _IOW(DMA_MAGIC, 7, unsigned long) ++#define DMA_SET_MAX_PHYS _IOW(DMA_MAGIC, 8, unsigned long) ++#define DMA_SET_PHYS_OFFSET _IOW(DMA_MAGIC, 9, unsigned long) ++ ++//used to define the size for the CMA-based allocation *in pages*, can only be done once once the file is opened ++#define DMA_CMA_SET_SIZE _IOW(DMA_MAGIC, 10, unsigned long) ++ ++//used to get the version of the module, to test for a capability ++#define DMA_GET_VERSION _IO(DMA_MAGIC, 99) ++ ++#define VERSION_NUMBER 1 ++ ++#define VIRT_TO_BUS_CACHE_SIZE 8 ++ ++/***** FILE OPS *****/ ++static int Open(struct inode *pInode, struct file *pFile); ++static int Release(struct inode *pInode, struct file *pFile); ++static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg); ++static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp); ++static int Mmap(struct file *pFile, struct vm_area_struct *pVma); ++ ++/***** VMA OPS ****/ ++static void VmaOpen4k(struct vm_area_struct *pVma); ++static void VmaClose4k(struct vm_area_struct *pVma); ++static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf); ++ ++/**** DMA PROTOTYPES */ ++static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError); ++static int DmaKick(struct DmaControlBlock __user *pUserCB); ++static void DmaWaitAll(void); ++ ++/**** GENERIC ****/ ++static int __init dmaer_init(void); ++static void __exit dmaer_exit(void); ++ ++/*** OPS ***/ ++static struct vm_operations_struct g_vmOps4k = { ++ .open = VmaOpen4k, ++ .close = VmaClose4k, ++ .fault = VmaFault4k, ++}; ++ ++static struct file_operations g_fOps = { ++ .owner = THIS_MODULE, ++ .llseek = 0, ++ .read = Read, ++ .write = 0, ++ .unlocked_ioctl = Ioctl, ++ .open = Open, ++ .release = Release, ++ .mmap = Mmap, ++}; ++ ++/***** GLOBALS ******/ ++static dev_t g_majorMinor; ++ ++//tracking usage of the two files ++static atomic_t g_oneLock4k = ATOMIC_INIT(1); ++ ++//device operations ++static struct cdev g_cDev; ++static int g_trackedPages = 0; ++ ++//dma control ++static unsigned int *g_pDmaChanBase; ++static int g_dmaIrq; ++static int g_dmaChan; ++ ++//cma allocation ++static int g_cmaHandle; ++ ++//user virtual to bus address translation acceleration ++static unsigned long g_virtAddr[VIRT_TO_BUS_CACHE_SIZE]; ++static unsigned long g_busAddr[VIRT_TO_BUS_CACHE_SIZE]; ++static unsigned long g_cbVirtAddr; ++static unsigned long g_cbBusAddr; ++static int g_cacheInsertAt; ++static int g_cacheHit, g_cacheMiss; ++ ++//off by default ++static void __user *g_pMinPhys; ++static void __user *g_pMaxPhys; ++static unsigned long g_physOffset; ++ ++/****** CACHE OPERATIONS ********/ ++static inline void FlushAddrCache(void) ++{ ++ int count = 0; ++ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) ++ g_virtAddr[count] = 0xffffffff; //never going to match as we always chop the bottom bits anyway ++ ++ g_cbVirtAddr = 0xffffffff; ++ ++ g_cacheInsertAt = 0; ++} ++ ++//translate from a user virtual address to a bus address by mapping the page ++//NB this won't lock a page in memory, so to avoid potential paging issues using kernel logical addresses ++static inline void __iomem *UserVirtualToBus(void __user *pUser) ++{ ++ int mapped; ++ struct page *pPage; ++ void *phys; ++ ++ //map it (requiring that the pointer points to something that does not hang off the page boundary) ++ mapped = get_user_pages(current, current->mm, ++ (unsigned long)pUser, 1, ++ 1, 0, ++ &pPage, ++ 0); ++ ++ if (mapped <= 0) //error ++ return 0; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "user virtual %p arm phys %p bus %p\n", ++ pUser, page_address(pPage), (void __iomem *)__virt_to_bus(page_address(pPage))); ++ ++ //get the arm physical address ++ phys = page_address(pPage) + offset_in_page(pUser); ++ page_cache_release(pPage); ++ ++ //and now the bus address ++ return (void __iomem *)__virt_to_bus(phys); ++} ++ ++static inline void __iomem *UserVirtualToBusViaCbCache(void __user *pUser) ++{ ++ unsigned long virtual_page = (unsigned long)pUser & ~4095; ++ unsigned long page_offset = (unsigned long)pUser & 4095; ++ unsigned long bus_addr; ++ ++ if (g_cbVirtAddr == virtual_page) ++ { ++ bus_addr = g_cbBusAddr + page_offset; ++ g_cacheHit++; ++ return (void __iomem *)bus_addr; ++ } ++ else ++ { ++ bus_addr = (unsigned long)UserVirtualToBus(pUser); ++ ++ if (!bus_addr) ++ return 0; ++ ++ g_cbVirtAddr = virtual_page; ++ g_cbBusAddr = bus_addr & ~4095; ++ g_cacheMiss++; ++ ++ return (void __iomem *)bus_addr; ++ } ++} ++ ++//do the same as above, by query our virt->bus cache ++static inline void __iomem *UserVirtualToBusViaCache(void __user *pUser) ++{ ++ int count; ++ //get the page and its offset ++ unsigned long virtual_page = (unsigned long)pUser & ~4095; ++ unsigned long page_offset = (unsigned long)pUser & 4095; ++ unsigned long bus_addr; ++ ++ if (pUser >= g_pMinPhys && pUser < g_pMaxPhys) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "user->phys passthrough on %p\n", pUser); ++ return (void __iomem *)((unsigned long)pUser + g_physOffset); ++ } ++ ++ //check the cache for our entry ++ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) ++ if (g_virtAddr[count] == virtual_page) ++ { ++ bus_addr = g_busAddr[count] + page_offset; ++ g_cacheHit++; ++ return (void __iomem *)bus_addr; ++ } ++ ++ //not found, look up manually and then insert its page address ++ bus_addr = (unsigned long)UserVirtualToBus(pUser); ++ ++ if (!bus_addr) ++ return 0; ++ ++ g_virtAddr[g_cacheInsertAt] = virtual_page; ++ g_busAddr[g_cacheInsertAt] = bus_addr & ~4095; ++ ++ //round robin ++ g_cacheInsertAt++; ++ if (g_cacheInsertAt == VIRT_TO_BUS_CACHE_SIZE) ++ g_cacheInsertAt = 0; ++ ++ g_cacheMiss++; ++ ++ return (void __iomem *)bus_addr; ++} ++ ++/***** FILE OPERATIONS ****/ ++static int Open(struct inode *pInode, struct file *pFile) ++{ ++ PRINTK(KERN_DEBUG "file opening: %d/%d\n", imajor(pInode), iminor(pInode)); ++ ++ //check which device we are ++ if (iminor(pInode) == 0) //4k ++ { ++ //only one at a time ++ if (!atomic_dec_and_test(&g_oneLock4k)) ++ { ++ atomic_inc(&g_oneLock4k); ++ return -EBUSY; ++ } ++ } ++ else ++ return -EINVAL; ++ ++ //todo there will be trouble if two different processes open the files ++ ++ //reset after any file is opened ++ g_pMinPhys = (void __user *)-1; ++ g_pMaxPhys = (void __user *)0; ++ g_physOffset = 0; ++ g_cmaHandle = 0; ++ ++ return 0; ++} ++ ++static int Release(struct inode *pInode, struct file *pFile) ++{ ++ PRINTK(KERN_DEBUG "file closing, %d pages tracked\n", g_trackedPages); ++ if (g_trackedPages) ++ PRINTK(KERN_ERR "we\'re leaking memory!\n"); ++ ++ //wait for any dmas to finish ++ DmaWaitAll(); ++ ++ //free this memory on the application closing the file or it crashing (implicitly closing the file) ++ if (g_cmaHandle) ++ { ++ PRINTK(KERN_DEBUG "unlocking vc memory\n"); ++ if (UnlockVcMemory(g_cmaHandle)) ++ PRINTK(KERN_ERR "uh-oh, unable to unlock vc memory!\n"); ++ PRINTK(KERN_DEBUG "releasing vc memory\n"); ++ if (ReleaseVcMemory(g_cmaHandle)) ++ PRINTK(KERN_ERR "uh-oh, unable to release vc memory!\n"); ++ } ++ ++ if (iminor(pInode) == 0) ++ atomic_inc(&g_oneLock4k); ++ else ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError) ++{ ++ struct DmaControlBlock kernCB; ++ struct DmaControlBlock __user *pUNext; ++ void __iomem *pSourceBus, __iomem *pDestBus; ++ ++ //get the control block into kernel memory so we can work on it ++ if (copy_from_user(&kernCB, pUserCB, sizeof(struct DmaControlBlock)) != 0) ++ { ++ PRINTK(KERN_ERR "copy_from_user failed for user cb %p\n", pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ if (kernCB.m_pSourceAddr == 0 || kernCB.m_pDestAddr == 0) ++ { ++ PRINTK(KERN_ERR "faulty source (%p) dest (%p) addresses for user cb %p\n", ++ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ pSourceBus = UserVirtualToBusViaCache(kernCB.m_pSourceAddr); ++ pDestBus = UserVirtualToBusViaCache(kernCB.m_pDestAddr); ++ ++ if (!pSourceBus || !pDestBus) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for source/dest %p/%p->%p/%p\n", ++ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, ++ pSourceBus, pDestBus); ++ *pError = 1; ++ return 0; ++ } ++ ++ //update the user structure with the new bus addresses ++ kernCB.m_pSourceAddr = pSourceBus; ++ kernCB.m_pDestAddr = pDestBus; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "final source %p dest %p\n", kernCB.m_pSourceAddr, kernCB.m_pDestAddr); ++ ++ //sort out the bus address for the next block ++ pUNext = kernCB.m_pNext; ++ ++ if (kernCB.m_pNext) ++ { ++ void __iomem *pNextBus; ++ pNextBus = UserVirtualToBusViaCbCache(kernCB.m_pNext); ++ ++ if (!pNextBus) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for m_pNext\n"); ++ *pError = 1; ++ return 0; ++ } ++ ++ //update the pointer with the bus address ++ kernCB.m_pNext = pNextBus; ++ } ++ ++ //write it back to user space ++ if (copy_to_user(pUserCB, &kernCB, sizeof(struct DmaControlBlock)) != 0) ++ { ++ PRINTK(KERN_ERR "copy_to_user failed for cb %p\n", pUserCB); ++ *pError = 1; ++ return 0; ++ } ++ ++ __cpuc_flush_dcache_area(pUserCB, 32); ++ ++ *pError = 0; ++ return pUNext; ++} ++ ++static int DmaKick(struct DmaControlBlock __user *pUserCB) ++{ ++ void __iomem *pBusCB; ++ ++ pBusCB = UserVirtualToBusViaCbCache(pUserCB); ++ if (!pBusCB) ++ { ++ PRINTK(KERN_ERR "virtual to bus translation failure for cb\n"); ++ return 1; ++ } ++ ++ //flush_cache_all(); ++ ++ bcm_dma_start(g_pDmaChanBase, (dma_addr_t)pBusCB); ++ ++ return 0; ++} ++ ++static void DmaWaitAll(void) ++{ ++ int counter = 0; ++ volatile int inner_count; ++ volatile unsigned int cs; ++ unsigned long time_before, time_after; ++ ++ time_before = jiffies; ++ //bcm_dma_wait_idle(g_pDmaChanBase); ++ dsb(); ++ ++ cs = readl(g_pDmaChanBase); ++ ++ while ((cs & 1) == 1) ++ { ++ cs = readl(g_pDmaChanBase); ++ counter++; ++ ++ for (inner_count = 0; inner_count < 32; inner_count++); ++ ++ asm volatile ("MCR p15,0,r0,c7,c0,4 \n"); ++ //cpu_do_idle(); ++ if (counter >= 1000000) ++ { ++ PRINTK(KERN_WARNING "DMA failed to finish in a timely fashion\n"); ++ break; ++ } ++ } ++ time_after = jiffies; ++ PRINTK_VERBOSE(KERN_DEBUG "done, counter %d, cs %08x", counter, cs); ++ PRINTK_VERBOSE(KERN_DEBUG "took %ld jiffies, %d HZ\n", time_after - time_before, HZ); ++} ++ ++static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg) ++{ ++ int error = 0; ++ PRINTK_VERBOSE(KERN_DEBUG "ioctl cmd %x arg %lx\n", cmd, arg); ++ ++ switch (cmd) ++ { ++ case DMA_PREPARE: ++ case DMA_PREPARE_KICK: ++ case DMA_PREPARE_KICK_WAIT: ++ { ++ struct DmaControlBlock __user *pUCB = (struct DmaControlBlock *)arg; ++ int steps = 0; ++ unsigned long start_time = jiffies; ++ (void)start_time; ++ ++ //flush our address cache ++ FlushAddrCache(); ++ ++ PRINTK_VERBOSE(KERN_DEBUG "dma prepare\n"); ++ ++ //do virtual to bus translation for each entry ++ do ++ { ++ pUCB = DmaPrepare(pUCB, &error); ++ } while (error == 0 && ++steps && pUCB); ++ PRINTK_VERBOSE(KERN_DEBUG "prepare done in %d steps, %ld\n", steps, jiffies - start_time); ++ ++ //carry straight on if we want to kick too ++ if (cmd == DMA_PREPARE || error) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "falling out\n"); ++ return error ? -EINVAL : 0; ++ } ++ } ++ case DMA_KICK: ++ PRINTK_VERBOSE(KERN_DEBUG "dma begin\n"); ++ ++ if (cmd == DMA_KICK) ++ FlushAddrCache(); ++ ++ DmaKick((struct DmaControlBlock __user *)arg); ++ ++ if (cmd != DMA_PREPARE_KICK_WAIT) ++ break; ++/* case DMA_WAIT_ONE: ++ //PRINTK(KERN_DEBUG "dma wait one\n"); ++ break;*/ ++ case DMA_WAIT_ALL: ++ //PRINTK(KERN_DEBUG "dma wait all\n"); ++ DmaWaitAll(); ++ break; ++ case DMA_MAX_BURST: ++ if (g_dmaChan == 0) ++ return 10; ++ else ++ return 5; ++ case DMA_SET_MIN_PHYS: ++ g_pMinPhys = (void __user *)arg; ++ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); ++ break; ++ case DMA_SET_MAX_PHYS: ++ g_pMaxPhys = (void __user *)arg; ++ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); ++ break; ++ case DMA_SET_PHYS_OFFSET: ++ g_physOffset = arg; ++ PRINTK(KERN_DEBUG "user/phys bypass offset set to %ld\n", g_physOffset); ++ break; ++ case DMA_CMA_SET_SIZE: ++ { ++ unsigned int pBusAddr; ++ ++ if (g_cmaHandle) ++ { ++ PRINTK(KERN_ERR "memory has already been allocated (handle %d)\n", g_cmaHandle); ++ return -EINVAL; ++ } ++ ++ PRINTK(KERN_INFO "allocating %ld bytes of VC memory\n", arg * 4096); ++ ++ //get the memory ++ if (AllocateVcMemory(&g_cmaHandle, arg * 4096, 4096, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_NO_INIT | MEM_FLAG_HINT_PERMALOCK)) ++ { ++ PRINTK(KERN_ERR "failed to allocate %ld bytes of VC memory\n", arg * 4096); ++ g_cmaHandle = 0; ++ return -EINVAL; ++ } ++ ++ //get an address for it ++ PRINTK(KERN_INFO "trying to map VC memory\n"); ++ ++ if (LockVcMemory(&pBusAddr, g_cmaHandle)) ++ { ++ PRINTK(KERN_ERR "failed to map CMA handle %d, releasing memory\n", g_cmaHandle); ++ ReleaseVcMemory(g_cmaHandle); ++ g_cmaHandle = 0; ++ } ++ ++ PRINTK(KERN_INFO "bus address for CMA memory is %x\n", pBusAddr); ++ return pBusAddr; ++ } ++ case DMA_GET_VERSION: ++ PRINTK(KERN_DEBUG "returning version number, %d\n", VERSION_NUMBER); ++ return VERSION_NUMBER; ++ default: ++ PRINTK(KERN_DEBUG "unknown ioctl: %d\n", cmd); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp) ++{ ++ return -EIO; ++} ++ ++static int Mmap(struct file *pFile, struct vm_area_struct *pVma) ++{ ++ struct PageList *pPages; ++ struct VmaPageList *pVmaList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "MMAP vma %p, length %ld (%s %d)\n", ++ pVma, pVma->vm_end - pVma->vm_start, ++ current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_DEBUG "MMAP %p %d (tracked %d)\n", pVma, current->pid, g_trackedPages); ++ ++ //make a new page list ++ pPages = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); ++ if (!pPages) ++ { ++ PRINTK(KERN_ERR "couldn\'t allocate a new page list (%s %d)\n", ++ current->comm, current->pid); ++ return -ENOMEM; ++ } ++ ++ //clear the page list ++ pPages->m_used = 0; ++ pPages->m_pNext = 0; ++ ++ //insert our vma and new page list somewhere ++ if (!pVma->vm_private_data) ++ { ++ struct VmaPageList *pList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "new vma list, making new one (%s %d)\n", ++ current->comm, current->pid); ++ ++ //make a new vma list ++ pList = (struct VmaPageList *)kmalloc(sizeof(struct VmaPageList), GFP_KERNEL); ++ if (!pList) ++ { ++ PRINTK(KERN_ERR "couldn\'t allocate vma page list (%s %d)\n", ++ current->comm, current->pid); ++ kfree(pPages); ++ return -ENOMEM; ++ } ++ ++ //clear this list ++ pVma->vm_private_data = (void *)pList; ++ pList->m_refCount = 0; ++ } ++ ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ //add it to the vma list ++ pVmaList->m_pPageHead = pPages; ++ pVmaList->m_pPageTail = pPages; ++ ++ pVma->vm_ops = &g_vmOps4k; ++ pVma->vm_flags |= VM_IO; ++ ++ VmaOpen4k(pVma); ++ ++ return 0; ++} ++ ++/****** VMA OPERATIONS ******/ ++ ++static void VmaOpen4k(struct vm_area_struct *pVma) ++{ ++ struct VmaPageList *pVmaList; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "vma open %p private %p (%s %d), %d live pages\n", pVma, pVma->vm_private_data, current->comm, current->pid, g_trackedPages); ++ PRINTK_VERBOSE(KERN_DEBUG "OPEN %p %d %ld pages (tracked pages %d)\n", ++ pVma, current->pid, (pVma->vm_end - pVma->vm_start) >> 12, ++ g_trackedPages); ++ ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ if (pVmaList) ++ { ++ pVmaList->m_refCount++; ++ PRINTK_VERBOSE(KERN_DEBUG "ref count is now %d\n", pVmaList->m_refCount); ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "err, open but no vma page list\n"); ++ } ++} ++ ++static void VmaClose4k(struct vm_area_struct *pVma) ++{ ++ struct VmaPageList *pVmaList; ++ int freed = 0; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "vma close %p private %p (%s %d)\n", pVma, pVma->vm_private_data, current->comm, current->pid); ++ ++ //wait for any dmas to finish ++ DmaWaitAll(); ++ ++ //find our vma in the list ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ //may be a fork ++ if (pVmaList) ++ { ++ struct PageList *pPages; ++ ++ pVmaList->m_refCount--; ++ ++ if (pVmaList->m_refCount == 0) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "found vma, freeing pages (%s %d)\n", ++ current->comm, current->pid); ++ ++ pPages = pVmaList->m_pPageHead; ++ ++ if (!pPages) ++ { ++ PRINTK(KERN_ERR "no page list (%s %d)!\n", ++ current->comm, current->pid); ++ return; ++ } ++ ++ while (pPages) ++ { ++ struct PageList *next; ++ int count; ++ ++ PRINTK_VERBOSE(KERN_DEBUG "page list (%s %d)\n", ++ current->comm, current->pid); ++ ++ next = pPages->m_pNext; ++ for (count = 0; count < pPages->m_used; count++) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "freeing page %p (%s %d)\n", ++ pPages->m_pPages[count], ++ current->comm, current->pid); ++ __free_pages(pPages->m_pPages[count], 0); ++ g_trackedPages--; ++ freed++; ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "freeing page list (%s %d)\n", ++ current->comm, current->pid); ++ kfree(pPages); ++ pPages = next; ++ } ++ ++ //remove our vma from the list ++ kfree(pVmaList); ++ pVma->vm_private_data = 0; ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "ref count is %d, not closing\n", pVmaList->m_refCount); ++ } ++ } ++ else ++ { ++ PRINTK_VERBOSE(KERN_ERR "uh-oh, vma %p not found (%s %d)!\n", pVma, current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_ERR "CLOSE ERR\n"); ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "CLOSE %p %d %d pages (tracked pages %d)", ++ pVma, current->pid, freed, g_trackedPages); ++ ++ PRINTK_VERBOSE(KERN_DEBUG "%d pages open\n", g_trackedPages); ++} ++ ++static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf) ++{ ++ PRINTK_VERBOSE(KERN_DEBUG "vma fault for vma %p private %p at offset %ld (%s %d)\n", pVma, pVma->vm_private_data, pVmf->pgoff, ++ current->comm, current->pid); ++ PRINTK_VERBOSE(KERN_DEBUG "FAULT\n"); ++ pVmf->page = alloc_page(GFP_KERNEL); ++ ++ if (pVmf->page) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "alloc page virtual %p\n", page_address(pVmf->page)); ++ } ++ ++ if (!pVmf->page) ++ { ++ PRINTK(KERN_ERR "vma fault oom (%s %d)\n", current->comm, current->pid); ++ return VM_FAULT_OOM; ++ } ++ else ++ { ++ struct VmaPageList *pVmaList; ++ ++ get_page(pVmf->page); ++ g_trackedPages++; ++ ++ //find our vma in the list ++ pVmaList = (struct VmaPageList *)pVma->vm_private_data; ++ ++ if (pVmaList) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "vma found (%s %d)\n", current->comm, current->pid); ++ ++ if (pVmaList->m_pPageTail->m_used == PAGES_PER_LIST) ++ { ++ PRINTK_VERBOSE(KERN_DEBUG "making new page list (%s %d)\n", current->comm, current->pid); ++ //making a new page list ++ pVmaList->m_pPageTail->m_pNext = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); ++ if (!pVmaList->m_pPageTail->m_pNext) ++ return -ENOMEM; ++ ++ //update the tail pointer ++ pVmaList->m_pPageTail = pVmaList->m_pPageTail->m_pNext; ++ pVmaList->m_pPageTail->m_used = 0; ++ pVmaList->m_pPageTail->m_pNext = 0; ++ } ++ ++ PRINTK_VERBOSE(KERN_DEBUG "adding page to list (%s %d)\n", current->comm, current->pid); ++ ++ pVmaList->m_pPageTail->m_pPages[pVmaList->m_pPageTail->m_used] = pVmf->page; ++ pVmaList->m_pPageTail->m_used++; ++ } ++ else ++ PRINTK(KERN_ERR "returned page for vma we don\'t know %p (%s %d)\n", pVma, current->comm, current->pid); ++ ++ return 0; ++ } ++} ++ ++/****** GENERIC FUNCTIONS ******/ ++static int __init dmaer_init(void) ++{ ++ int result = alloc_chrdev_region(&g_majorMinor, 0, 1, "dmaer"); ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "unable to get major device number\n"); ++ return result; ++ } ++ else ++ PRINTK(KERN_DEBUG "major device number %d\n", MAJOR(g_majorMinor)); ++ ++ PRINTK(KERN_DEBUG "vma list size %d, page list size %d, page size %ld\n", ++ sizeof(struct VmaPageList), sizeof(struct PageList), PAGE_SIZE); ++ ++ //get a dma channel to work with ++ result = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, (void **)&g_pDmaChanBase, &g_dmaIrq); ++ ++ //uncomment to force to channel 0 ++ //result = 0; ++ //g_pDmaChanBase = 0xce808000; ++ ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "failed to allocate dma channel\n"); ++ cdev_del(&g_cDev); ++ unregister_chrdev_region(g_majorMinor, 1); ++ } ++ ++ //reset the channel ++ PRINTK(KERN_DEBUG "allocated dma channel %d (%p), initial state %08x\n", result, g_pDmaChanBase, *g_pDmaChanBase); ++ *g_pDmaChanBase = 1 << 31; ++ PRINTK(KERN_DEBUG "post-reset %08x\n", *g_pDmaChanBase); ++ ++ g_dmaChan = result; ++ ++ //clear the cache stats ++ g_cacheHit = 0; ++ g_cacheMiss = 0; ++ ++ //register our device - after this we are go go go ++ cdev_init(&g_cDev, &g_fOps); ++ g_cDev.owner = THIS_MODULE; ++ g_cDev.ops = &g_fOps; ++ ++ result = cdev_add(&g_cDev, g_majorMinor, 1); ++ if (result < 0) ++ { ++ PRINTK(KERN_ERR "failed to add character device\n"); ++ unregister_chrdev_region(g_majorMinor, 1); ++ bcm_dma_chan_free(g_dmaChan); ++ return result; ++ } ++ ++ return 0; ++} ++ ++static void __exit dmaer_exit(void) ++{ ++ PRINTK(KERN_INFO "closing dmaer device, cache stats: %d hits %d misses\n", g_cacheHit, g_cacheMiss); ++ //unregister the device ++ cdev_del(&g_cDev); ++ unregister_chrdev_region(g_majorMinor, 1); ++ //free the dma channel ++ bcm_dma_chan_free(g_dmaChan); ++} ++ ++MODULE_LICENSE("Dual BSD/GPL"); ++MODULE_AUTHOR("Simon Hall"); ++module_init(dmaer_init); ++module_exit(dmaer_exit); +diff --git a/arch/arm/mach-bcm2709/include/mach/arm_control.h b/arch/arm/mach-bcm2709/include/mach/arm_control.h +new file mode 100644 +index 0000000..e346caf +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/arm_control.h +@@ -0,0 +1,493 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/arm_control.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARM_CONTROL_H ++#define __BCM2708_ARM_CONTROL_H ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define ARM_BASE 0x7E00B000 ++ ++/* Basic configuration */ ++#define ARM_CONTROL0 HW_REGISTER_RW(ARM_BASE+0x000) ++#define ARM_C0_SIZ128M 0x00000000 ++#define ARM_C0_SIZ256M 0x00000001 ++#define ARM_C0_SIZ512M 0x00000002 ++#define ARM_C0_SIZ1G 0x00000003 ++#define ARM_C0_BRESP0 0x00000000 ++#define ARM_C0_BRESP1 0x00000004 ++#define ARM_C0_BRESP2 0x00000008 ++#define ARM_C0_BOOTHI 0x00000010 ++#define ARM_C0_UNUSED05 0x00000020 /* free */ ++#define ARM_C0_FULLPERI 0x00000040 ++#define ARM_C0_UNUSED78 0x00000180 /* free */ ++#define ARM_C0_JTAGMASK 0x00000E00 ++#define ARM_C0_JTAGOFF 0x00000000 ++#define ARM_C0_JTAGBASH 0x00000800 /* Debug on GPIO off */ ++#define ARM_C0_JTAGGPIO 0x00000C00 /* Debug on GPIO on */ ++#define ARM_C0_APROTMSK 0x0000F000 ++#define ARM_C0_DBG0SYNC 0x00010000 /* VPU0 halt sync */ ++#define ARM_C0_DBG1SYNC 0x00020000 /* VPU1 halt sync */ ++#define ARM_C0_SWDBGREQ 0x00040000 /* HW debug request */ ++#define ARM_C0_PASSHALT 0x00080000 /* ARM halt passed to debugger */ ++#define ARM_C0_PRIO_PER 0x00F00000 /* per priority mask */ ++#define ARM_C0_PRIO_L2 0x0F000000 ++#define ARM_C0_PRIO_UC 0xF0000000 ++ ++#define ARM_C0_APROTPASS 0x0000A000 /* Translate 1:1 */ ++#define ARM_C0_APROTUSER 0x00000000 /* Only user mode */ ++#define ARM_C0_APROTSYST 0x0000F000 /* Only system mode */ ++ ++ ++#define ARM_CONTROL1 HW_REGISTER_RW(ARM_BASE+0x440) ++#define ARM_C1_TIMER 0x00000001 /* re-route timer IRQ to VC */ ++#define ARM_C1_MAIL 0x00000002 /* re-route Mail IRQ to VC */ ++#define ARM_C1_BELL0 0x00000004 /* re-route Doorbell 0 to VC */ ++#define ARM_C1_BELL1 0x00000008 /* re-route Doorbell 1 to VC */ ++#define ARM_C1_PERSON 0x00000100 /* peripherals on */ ++#define ARM_C1_REQSTOP 0x00000200 /* ASYNC bridge request stop */ ++ ++#define ARM_STATUS HW_REGISTER_RW(ARM_BASE+0x444) ++#define ARM_S_ACKSTOP 0x80000000 /* Bridge stopped */ ++#define ARM_S_READPEND 0x000003FF /* pending reads counter */ ++#define ARM_S_WRITPEND 0x000FFC00 /* pending writes counter */ ++ ++#define ARM_ERRHALT HW_REGISTER_RW(ARM_BASE+0x448) ++#define ARM_EH_PERIBURST 0x00000001 /* Burst write seen on peri bus */ ++#define ARM_EH_ILLADDRS1 0x00000002 /* Address bits 25-27 error */ ++#define ARM_EH_ILLADDRS2 0x00000004 /* Address bits 31-28 error */ ++#define ARM_EH_VPU0HALT 0x00000008 /* VPU0 halted & in debug mode */ ++#define ARM_EH_VPU1HALT 0x00000010 /* VPU1 halted & in debug mode */ ++#define ARM_EH_ARMHALT 0x00000020 /* ARM in halted debug mode */ ++ ++#define ARM_ID_SECURE HW_REGISTER_RW(ARM_BASE+0x00C) ++#define ARM_ID HW_REGISTER_RW(ARM_BASE+0x44C) ++#define ARM_IDVAL 0x364D5241 ++ ++/* Translation memory */ ++#define ARM_TRANSLATE HW_REGISTER_RW(ARM_BASE+0x100) ++/* 32 locations: 0x100.. 0x17F */ ++/* 32 spare means we CAN go to 64 pages.... */ ++ ++ ++/* Interrupts */ ++#define ARM_IRQ_PEND0 HW_REGISTER_RW(ARM_BASE+0x200) /* Top IRQ bits */ ++#define ARM_I0_TIMER 0x00000001 /* timer IRQ */ ++#define ARM_I0_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_I0_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_I0_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_I0_BANK1 0x00000100 /* Bank1 IRQ */ ++#define ARM_I0_BANK2 0x00000200 /* Bank2 IRQ */ ++ ++#define ARM_IRQ_PEND1 HW_REGISTER_RW(ARM_BASE+0x204) /* All bank1 IRQ bits */ ++/* todo: all I1_interrupt sources */ ++#define ARM_IRQ_PEND2 HW_REGISTER_RW(ARM_BASE+0x208) /* All bank2 IRQ bits */ ++/* todo: all I2_interrupt sources */ ++ ++#define ARM_IRQ_FAST HW_REGISTER_RW(ARM_BASE+0x20C) /* FIQ control */ ++#define ARM_IF_INDEX 0x0000007F /* FIQ select */ ++#define ARM_IF_ENABLE 0x00000080 /* FIQ enable */ ++#define ARM_IF_VCMASK 0x0000003F /* FIQ = (index from VC source) */ ++#define ARM_IF_TIMER 0x00000040 /* FIQ = ARM timer */ ++#define ARM_IF_MAIL 0x00000041 /* FIQ = ARM Mail */ ++#define ARM_IF_BELL0 0x00000042 /* FIQ = ARM Doorbell 0 */ ++#define ARM_IF_BELL1 0x00000043 /* FIQ = ARM Doorbell 1 */ ++#define ARM_IF_VP0HALT 0x00000044 /* FIQ = VPU0 Halt seen */ ++#define ARM_IF_VP1HALT 0x00000045 /* FIQ = VPU1 Halt seen */ ++#define ARM_IF_ILLEGAL 0x00000046 /* FIQ = Illegal access seen */ ++ ++#define ARM_IRQ_ENBL1 HW_REGISTER_RW(ARM_BASE+0x210) /* Bank1 enable bits */ ++#define ARM_IRQ_ENBL2 HW_REGISTER_RW(ARM_BASE+0x214) /* Bank2 enable bits */ ++#define ARM_IRQ_ENBL3 HW_REGISTER_RW(ARM_BASE+0x218) /* ARM irqs enable bits */ ++#define ARM_IRQ_DIBL1 HW_REGISTER_RW(ARM_BASE+0x21C) /* Bank1 disable bits */ ++#define ARM_IRQ_DIBL2 HW_REGISTER_RW(ARM_BASE+0x220) /* Bank2 disable bits */ ++#define ARM_IRQ_DIBL3 HW_REGISTER_RW(ARM_BASE+0x224) /* ARM irqs disable bits */ ++#define ARM_IE_TIMER 0x00000001 /* Timer IRQ */ ++#define ARM_IE_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_IE_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_IE_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_IE_VP0HALT 0x00000010 /* VPU0 Halt */ ++#define ARM_IE_VP1HALT 0x00000020 /* VPU1 Halt */ ++#define ARM_IE_ILLEGAL 0x00000040 /* Illegal access seen */ ++ ++/* Timer */ ++/* For reg. fields see sp804 spec. */ ++#define ARM_T_LOAD HW_REGISTER_RW(ARM_BASE+0x400) ++#define ARM_T_VALUE HW_REGISTER_RW(ARM_BASE+0x404) ++#define ARM_T_CONTROL HW_REGISTER_RW(ARM_BASE+0x408) ++#define ARM_T_IRQCNTL HW_REGISTER_RW(ARM_BASE+0x40C) ++#define ARM_T_RAWIRQ HW_REGISTER_RW(ARM_BASE+0x410) ++#define ARM_T_MSKIRQ HW_REGISTER_RW(ARM_BASE+0x414) ++#define ARM_T_RELOAD HW_REGISTER_RW(ARM_BASE+0x418) ++#define ARM_T_PREDIV HW_REGISTER_RW(ARM_BASE+0x41c) ++#define ARM_T_FREECNT HW_REGISTER_RW(ARM_BASE+0x420) ++ ++#define TIMER_CTRL_ONESHOT (1 << 0) ++#define TIMER_CTRL_32BIT (1 << 1) ++#define TIMER_CTRL_DIV1 (0 << 2) ++#define TIMER_CTRL_DIV16 (1 << 2) ++#define TIMER_CTRL_DIV256 (2 << 2) ++#define TIMER_CTRL_IE (1 << 5) ++#define TIMER_CTRL_PERIODIC (1 << 6) ++#define TIMER_CTRL_ENABLE (1 << 7) ++#define TIMER_CTRL_DBGHALT (1 << 8) ++#define TIMER_CTRL_ENAFREE (1 << 9) ++#define TIMER_CTRL_FREEDIV_SHIFT 16) ++#define TIMER_CTRL_FREEDIV_MASK 0xff ++ ++/* Semaphores, Doorbells, Mailboxes */ ++#define ARM_SBM_OWN0 (ARM_BASE+0x800) ++#define ARM_SBM_OWN1 (ARM_BASE+0x900) ++#define ARM_SBM_OWN2 (ARM_BASE+0xA00) ++#define ARM_SBM_OWN3 (ARM_BASE+0xB00) ++ ++/* MAILBOXES ++ * Register flags are common across all ++ * owner registers. See end of this section ++ * ++ * Semaphores, Doorbells, Mailboxes Owner 0 ++ * ++ */ ++ ++#define ARM_0_SEMS HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM0 HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM1 HW_REGISTER_RW(ARM_SBM_OWN0+0x04) ++#define ARM_0_SEM2 HW_REGISTER_RW(ARM_SBM_OWN0+0x08) ++#define ARM_0_SEM3 HW_REGISTER_RW(ARM_SBM_OWN0+0x0C) ++#define ARM_0_SEM4 HW_REGISTER_RW(ARM_SBM_OWN0+0x10) ++#define ARM_0_SEM5 HW_REGISTER_RW(ARM_SBM_OWN0+0x14) ++#define ARM_0_SEM6 HW_REGISTER_RW(ARM_SBM_OWN0+0x18) ++#define ARM_0_SEM7 HW_REGISTER_RW(ARM_SBM_OWN0+0x1C) ++#define ARM_0_BELL0 HW_REGISTER_RW(ARM_SBM_OWN0+0x40) ++#define ARM_0_BELL1 HW_REGISTER_RW(ARM_SBM_OWN0+0x44) ++#define ARM_0_BELL2 HW_REGISTER_RW(ARM_SBM_OWN0+0x48) ++#define ARM_0_BELL3 HW_REGISTER_RW(ARM_SBM_OWN0+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Some addresses should ONLY be used by owner 0 */ ++#define ARM_0_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) */ ++#define ARM_0_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) Normal read */ ++#define ARM_0_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN0+0x90) /* none-pop read */ ++#define ARM_0_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN0+0x94) /* Sender read (only LS 2 bits) */ ++#define ARM_0_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN0+0x98) /* Status read */ ++#define ARM_0_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0x9C) /* Config read/write */ ++/* MAILBOX 1 access in Owner 0 area */ ++/* Owner 0 should only WRITE to this mailbox */ ++#define ARM_0_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_0_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN0+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN0+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_0_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN0+0xB8) /* Status read */ ++/*#define ARM_0_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_0_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE0) /* semaphore clear/debug register */ ++#define ARM_0_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE4) /* Doorbells clear/debug register */ ++#define ARM_0_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xF8) /* ALL interrupts */ ++#define ARM_0_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xFC) /* IRQS pending for owner 0 */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 1 */ ++#define ARM_1_SEMS HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM0 HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM1 HW_REGISTER_RW(ARM_SBM_OWN1+0x04) ++#define ARM_1_SEM2 HW_REGISTER_RW(ARM_SBM_OWN1+0x08) ++#define ARM_1_SEM3 HW_REGISTER_RW(ARM_SBM_OWN1+0x0C) ++#define ARM_1_SEM4 HW_REGISTER_RW(ARM_SBM_OWN1+0x10) ++#define ARM_1_SEM5 HW_REGISTER_RW(ARM_SBM_OWN1+0x14) ++#define ARM_1_SEM6 HW_REGISTER_RW(ARM_SBM_OWN1+0x18) ++#define ARM_1_SEM7 HW_REGISTER_RW(ARM_SBM_OWN1+0x1C) ++#define ARM_1_BELL0 HW_REGISTER_RW(ARM_SBM_OWN1+0x40) ++#define ARM_1_BELL1 HW_REGISTER_RW(ARM_SBM_OWN1+0x44) ++#define ARM_1_BELL2 HW_REGISTER_RW(ARM_SBM_OWN1+0x48) ++#define ARM_1_BELL3 HW_REGISTER_RW(ARM_SBM_OWN1+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Owner 1 should only WRITE to this mailbox */ ++#define ARM_1_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_1_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN1+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN1+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN1+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_1_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN1+0x98) /* Status read */ ++/*#define ARM_1_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 0 area */ ++#define ARM_1_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) */ ++#define ARM_1_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) Normal read */ ++#define ARM_1_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN1+0xB0) /* none-pop read */ ++#define ARM_1_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN1+0xB4) /* Sender read (only LS 2 bits) */ ++#define ARM_1_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN1+0xB8) /* Status read */ ++#define ARM_1_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0xBC) ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_1_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE0) /* semaphore clear/debug register */ ++#define ARM_1_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE4) /* Doorbells clear/debug register */ ++#define ARM_1_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xFC) /* IRQS pending for owner 1 */ ++#define ARM_1_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 2 */ ++#define ARM_2_SEMS HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM0 HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM1 HW_REGISTER_RW(ARM_SBM_OWN2+0x04) ++#define ARM_2_SEM2 HW_REGISTER_RW(ARM_SBM_OWN2+0x08) ++#define ARM_2_SEM3 HW_REGISTER_RW(ARM_SBM_OWN2+0x0C) ++#define ARM_2_SEM4 HW_REGISTER_RW(ARM_SBM_OWN2+0x10) ++#define ARM_2_SEM5 HW_REGISTER_RW(ARM_SBM_OWN2+0x14) ++#define ARM_2_SEM6 HW_REGISTER_RW(ARM_SBM_OWN2+0x18) ++#define ARM_2_SEM7 HW_REGISTER_RW(ARM_SBM_OWN2+0x1C) ++#define ARM_2_BELL0 HW_REGISTER_RW(ARM_SBM_OWN2+0x40) ++#define ARM_2_BELL1 HW_REGISTER_RW(ARM_SBM_OWN2+0x44) ++#define ARM_2_BELL2 HW_REGISTER_RW(ARM_SBM_OWN2+0x48) ++#define ARM_2_BELL3 HW_REGISTER_RW(ARM_SBM_OWN2+0x4C) ++/* MAILBOX 0 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_2_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN2+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN2+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN2+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN2+0x98) /* Status read */ ++/*#define ARM_2_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_2_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN2+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN2+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN2+0xB8) /* Status read */ ++/*#define ARM_2_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_2_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE0) /* semaphore clear/debug register */ ++#define ARM_2_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE4) /* Doorbells clear/debug register */ ++#define ARM_2_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xFC) /* IRQS pending for owner 2 */ ++#define ARM_2_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 3 */ ++#define ARM_3_SEMS HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM0 HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM1 HW_REGISTER_RW(ARM_SBM_OWN3+0x04) ++#define ARM_3_SEM2 HW_REGISTER_RW(ARM_SBM_OWN3+0x08) ++#define ARM_3_SEM3 HW_REGISTER_RW(ARM_SBM_OWN3+0x0C) ++#define ARM_3_SEM4 HW_REGISTER_RW(ARM_SBM_OWN3+0x10) ++#define ARM_3_SEM5 HW_REGISTER_RW(ARM_SBM_OWN3+0x14) ++#define ARM_3_SEM6 HW_REGISTER_RW(ARM_SBM_OWN3+0x18) ++#define ARM_3_SEM7 HW_REGISTER_RW(ARM_SBM_OWN3+0x1C) ++#define ARM_3_BELL0 HW_REGISTER_RW(ARM_SBM_OWN3+0x40) ++#define ARM_3_BELL1 HW_REGISTER_RW(ARM_SBM_OWN3+0x44) ++#define ARM_3_BELL2 HW_REGISTER_RW(ARM_SBM_OWN3+0x48) ++#define ARM_3_BELL3 HW_REGISTER_RW(ARM_SBM_OWN3+0x4C) ++/* MAILBOX 0 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_3_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN3+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN3+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN3+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN3+0x98) /* Status read */ ++/*#define ARM_3_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_3_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN3+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN3+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN3+0xB8) /* Status read */ ++/*#define ARM_3_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_3_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE0) /* semaphore clear/debug register */ ++#define ARM_3_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE4) /* Doorbells clear/debug register */ ++#define ARM_3_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xFC) /* IRQS pending for owner 3 */ ++#define ARM_3_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xF8) /* ALL interrupts */ ++ ++ ++ ++/* Mailbox flags. Valid for all owners */ ++ ++/* Mailbox status register (...0x98) */ ++#define ARM_MS_FULL 0x80000000 ++#define ARM_MS_EMPTY 0x40000000 ++#define ARM_MS_LEVEL 0x400000FF /* Max. value depdnds on mailbox depth parameter */ ++ ++/* MAILBOX config/status register (...0x9C) */ ++/* ANY write to this register clears the error bits! */ ++#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mailbox irq enable: has data */ ++#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mailbox irq enable: has space */ ++#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mailbox irq enable: Opp. is empty */ ++#define ARM_MC_MAIL_CLEAR 0x00000008 /* mailbox clear write 1, then 0 */ ++#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mailbox irq pending: has space */ ++#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mailbox irq pending: Opp. is empty */ ++#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mailbox irq pending */ ++/* Bit 7 is unused */ ++#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ ++#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ ++#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ ++ ++/* Semaphore clear/debug register (...0xE0) */ ++#define ARM_SD_OWN0 0x00000003 /* Owner of sem 0 */ ++#define ARM_SD_OWN1 0x0000000C /* Owner of sem 1 */ ++#define ARM_SD_OWN2 0x00000030 /* Owner of sem 2 */ ++#define ARM_SD_OWN3 0x000000C0 /* Owner of sem 3 */ ++#define ARM_SD_OWN4 0x00000300 /* Owner of sem 4 */ ++#define ARM_SD_OWN5 0x00000C00 /* Owner of sem 5 */ ++#define ARM_SD_OWN6 0x00003000 /* Owner of sem 6 */ ++#define ARM_SD_OWN7 0x0000C000 /* Owner of sem 7 */ ++#define ARM_SD_SEM0 0x00010000 /* Status of sem 0 */ ++#define ARM_SD_SEM1 0x00020000 /* Status of sem 1 */ ++#define ARM_SD_SEM2 0x00040000 /* Status of sem 2 */ ++#define ARM_SD_SEM3 0x00080000 /* Status of sem 3 */ ++#define ARM_SD_SEM4 0x00100000 /* Status of sem 4 */ ++#define ARM_SD_SEM5 0x00200000 /* Status of sem 5 */ ++#define ARM_SD_SEM6 0x00400000 /* Status of sem 6 */ ++#define ARM_SD_SEM7 0x00800000 /* Status of sem 7 */ ++ ++/* Doorbells clear/debug register (...0xE4) */ ++#define ARM_BD_OWN0 0x00000003 /* Owner of doorbell 0 */ ++#define ARM_BD_OWN1 0x0000000C /* Owner of doorbell 1 */ ++#define ARM_BD_OWN2 0x00000030 /* Owner of doorbell 2 */ ++#define ARM_BD_OWN3 0x000000C0 /* Owner of doorbell 3 */ ++#define ARM_BD_BELL0 0x00000100 /* Status of doorbell 0 */ ++#define ARM_BD_BELL1 0x00000200 /* Status of doorbell 1 */ ++#define ARM_BD_BELL2 0x00000400 /* Status of doorbell 2 */ ++#define ARM_BD_BELL3 0x00000800 /* Status of doorbell 3 */ ++ ++/* MY IRQS register (...0xF8) */ ++#define ARM_MYIRQ_BELL 0x00000001 /* This owner has a doorbell IRQ */ ++#define ARM_MYIRQ_MAIL 0x00000002 /* This owner has a mailbox IRQ */ ++ ++/* ALL IRQS register (...0xF8) */ ++#define ARM_AIS_BELL0 0x00000001 /* Doorbell 0 IRQ pending */ ++#define ARM_AIS_BELL1 0x00000002 /* Doorbell 1 IRQ pending */ ++#define ARM_AIS_BELL2 0x00000004 /* Doorbell 2 IRQ pending */ ++#define ARM_AIS_BELL3 0x00000008 /* Doorbell 3 IRQ pending */ ++#define ARM_AIS0_HAVEDATA 0x00000010 /* MAIL 0 has data IRQ pending */ ++#define ARM_AIS0_HAVESPAC 0x00000020 /* MAIL 0 has space IRQ pending */ ++#define ARM_AIS0_OPPEMPTY 0x00000040 /* MAIL 0 opposite is empty IRQ */ ++#define ARM_AIS1_HAVEDATA 0x00000080 /* MAIL 1 has data IRQ pending */ ++#define ARM_AIS1_HAVESPAC 0x00000100 /* MAIL 1 has space IRQ pending */ ++#define ARM_AIS1_OPPEMPTY 0x00000200 /* MAIL 1 opposite is empty IRQ */ ++/* Note that bell-0, bell-1 and MAIL0 IRQ go only to the ARM */ ++/* Whilst that bell-2, bell-3 and MAIL1 IRQ go only to the VC */ ++/* */ ++/* ARM JTAG BASH */ ++/* */ ++#define AJB_BASE 0x7e2000c0 ++ ++#define AJBCONF HW_REGISTER_RW(AJB_BASE+0x00) ++#define AJB_BITS0 0x000000 ++#define AJB_BITS4 0x000004 ++#define AJB_BITS8 0x000008 ++#define AJB_BITS12 0x00000C ++#define AJB_BITS16 0x000010 ++#define AJB_BITS20 0x000014 ++#define AJB_BITS24 0x000018 ++#define AJB_BITS28 0x00001C ++#define AJB_BITS32 0x000020 ++#define AJB_BITS34 0x000022 ++#define AJB_OUT_MS 0x000040 ++#define AJB_OUT_LS 0x000000 ++#define AJB_INV_CLK 0x000080 ++#define AJB_D0_RISE 0x000100 ++#define AJB_D0_FALL 0x000000 ++#define AJB_D1_RISE 0x000200 ++#define AJB_D1_FALL 0x000000 ++#define AJB_IN_RISE 0x000400 ++#define AJB_IN_FALL 0x000000 ++#define AJB_ENABLE 0x000800 ++#define AJB_HOLD0 0x000000 ++#define AJB_HOLD1 0x001000 ++#define AJB_HOLD2 0x002000 ++#define AJB_HOLD3 0x003000 ++#define AJB_RESETN 0x004000 ++#define AJB_CLKSHFT 16 ++#define AJB_BUSY 0x80000000 ++#define AJBTMS HW_REGISTER_RW(AJB_BASE+0x04) ++#define AJBTDI HW_REGISTER_RW(AJB_BASE+0x08) ++#define AJBTDO HW_REGISTER_RW(AJB_BASE+0x0c) ++ ++#define ARM_LOCAL_BASE 0x40000000 ++#define ARM_LOCAL_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x000) ++#define ARM_LOCAL_PRESCALER HW_REGISTER_RW(ARM_LOCAL_BASE+0x008) ++#define ARM_LOCAL_GPU_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x00C) ++#define ARM_LOCAL_PM_ROUTING_SET HW_REGISTER_RW(ARM_LOCAL_BASE+0x010) ++#define ARM_LOCAL_PM_ROUTING_CLR HW_REGISTER_RW(ARM_LOCAL_BASE+0x014) ++#define ARM_LOCAL_TIMER_LS HW_REGISTER_RW(ARM_LOCAL_BASE+0x01C) ++#define ARM_LOCAL_TIMER_MS HW_REGISTER_RW(ARM_LOCAL_BASE+0x020) ++#define ARM_LOCAL_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x024) ++#define ARM_LOCAL_AXI_COUNT HW_REGISTER_RW(ARM_LOCAL_BASE+0x02C) ++#define ARM_LOCAL_AXI_IRQ HW_REGISTER_RW(ARM_LOCAL_BASE+0x030) ++#define ARM_LOCAL_TIMER_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x034) ++#define ARM_LOCAL_TIMER_WRITE HW_REGISTER_RW(ARM_LOCAL_BASE+0x038) ++ ++#define ARM_LOCAL_TIMER_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x040) ++#define ARM_LOCAL_TIMER_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x044) ++#define ARM_LOCAL_TIMER_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x048) ++#define ARM_LOCAL_TIMER_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x04C) ++ ++#define ARM_LOCAL_MAILBOX_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x050) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x054) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x058) ++#define ARM_LOCAL_MAILBOX_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x05C) ++ ++#define ARM_LOCAL_IRQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x060) ++#define ARM_LOCAL_IRQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x064) ++#define ARM_LOCAL_IRQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x068) ++#define ARM_LOCAL_IRQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x06C) ++ ++#define ARM_LOCAL_FIQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x070) ++#define ARM_LOCAL_FIQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x074) ++#define ARM_LOCAL_FIQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x078) ++#define ARM_LOCAL_FIQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x07C) ++ ++#define ARM_LOCAL_MAILBOX0_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x080) ++#define ARM_LOCAL_MAILBOX1_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x084) ++#define ARM_LOCAL_MAILBOX2_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x088) ++#define ARM_LOCAL_MAILBOX3_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x08C) ++ ++#define ARM_LOCAL_MAILBOX0_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x090) ++#define ARM_LOCAL_MAILBOX1_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x094) ++#define ARM_LOCAL_MAILBOX2_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x098) ++#define ARM_LOCAL_MAILBOX3_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x09C) ++ ++#define ARM_LOCAL_MAILBOX0_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A0) ++#define ARM_LOCAL_MAILBOX1_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A4) ++#define ARM_LOCAL_MAILBOX2_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A8) ++#define ARM_LOCAL_MAILBOX3_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0AC) ++ ++#define ARM_LOCAL_MAILBOX0_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B0) ++#define ARM_LOCAL_MAILBOX1_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B4) ++#define ARM_LOCAL_MAILBOX2_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B8) ++#define ARM_LOCAL_MAILBOX3_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0BC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C0) ++#define ARM_LOCAL_MAILBOX1_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C4) ++#define ARM_LOCAL_MAILBOX2_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C8) ++#define ARM_LOCAL_MAILBOX3_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0CC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D0) ++#define ARM_LOCAL_MAILBOX1_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D4) ++#define ARM_LOCAL_MAILBOX2_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D8) ++#define ARM_LOCAL_MAILBOX3_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0DC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E0) ++#define ARM_LOCAL_MAILBOX1_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E4) ++#define ARM_LOCAL_MAILBOX2_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E8) ++#define ARM_LOCAL_MAILBOX3_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0EC) ++ ++#define ARM_LOCAL_MAILBOX0_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F0) ++#define ARM_LOCAL_MAILBOX1_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F4) ++#define ARM_LOCAL_MAILBOX2_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F8) ++#define ARM_LOCAL_MAILBOX3_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0FC) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/arm_power.h b/arch/arm/mach-bcm2709/include/mach/arm_power.h +new file mode 100644 +index 0000000..d3bf245 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/arm_power.h +@@ -0,0 +1,62 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/arm_power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _ARM_POWER_H ++#define _ARM_POWER_H ++ ++/* Use meaningful names on each side */ ++#ifdef __VIDEOCORE__ ++#define PREFIX(x) ARM_##x ++#else ++#define PREFIX(x) BCM_##x ++#endif ++ ++enum { ++ PREFIX(POWER_SDCARD_BIT), ++ PREFIX(POWER_UART_BIT), ++ PREFIX(POWER_MINIUART_BIT), ++ PREFIX(POWER_USB_BIT), ++ PREFIX(POWER_I2C0_BIT), ++ PREFIX(POWER_I2C1_BIT), ++ PREFIX(POWER_I2C2_BIT), ++ PREFIX(POWER_SPI_BIT), ++ PREFIX(POWER_CCP2TX_BIT), ++ PREFIX(POWER_DSI_BIT), ++ ++ PREFIX(POWER_MAX) ++}; ++ ++enum { ++ PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)), ++ PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)), ++ PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)), ++ PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)), ++ PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)), ++ PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)), ++ PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)), ++ PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)), ++ PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)), ++ PREFIX(POWER_DSI) = (1 << PREFIX(POWER_DSI_BIT)), ++ ++ PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1, ++ PREFIX(POWER_NONE) = 0 ++}; ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/barriers.h b/arch/arm/mach-bcm2709/include/mach/barriers.h +new file mode 100644 +index 0000000..723cdad +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/barriers.h +@@ -0,0 +1,3 @@ ++#define mb() dsb() ++#define rmb() dsb() ++#define wmb() mb() +diff --git a/arch/arm/mach-bcm2709/include/mach/clkdev.h b/arch/arm/mach-bcm2709/include/mach/clkdev.h +new file mode 100644 +index 0000000..04b37a8 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/clkdev.h +@@ -0,0 +1,7 @@ ++#ifndef __ASM_MACH_CLKDEV_H ++#define __ASM_MACH_CLKDEV_H ++ ++#define __clk_get(clk) ({ 1; }) ++#define __clk_put(clk) do { } while (0) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/debug-macro.S b/arch/arm/mach-bcm2709/include/mach/debug-macro.S +new file mode 100644 +index 0000000..b24304a +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/debug-macro.S +@@ -0,0 +1,22 @@ ++/* arch/arm/mach-bcm2708/include/mach/debug-macro.S ++ * ++ * Debugging macro include header ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 1994-1999 Russell King ++ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks ++ * ++ * 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 ++ ++ .macro addruart, rp, rv, tmp ++ ldr \rp, =UART0_BASE ++ ldr \rv, =IO_ADDRESS(UART0_BASE) ++ .endm ++ ++#include +diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h +new file mode 100644 +index 0000000..d03e7b5 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/dma.h +@@ -0,0 +1,94 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/dma.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ */ ++ ++ ++#ifndef _MACH_BCM2708_DMA_H ++#define _MACH_BCM2708_DMA_H ++ ++#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" ++ ++/* DMA CS Control and Status bits */ ++#define BCM2708_DMA_ACTIVE (1 << 0) ++#define BCM2708_DMA_INT (1 << 2) ++#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ ++#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ ++#define BCM2708_DMA_ERR (1 << 8) ++#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ ++#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ ++ ++/* DMA control block "info" field bits */ ++#define BCM2708_DMA_INT_EN (1 << 0) ++#define BCM2708_DMA_TDMODE (1 << 1) ++#define BCM2708_DMA_WAIT_RESP (1 << 3) ++#define BCM2708_DMA_D_INC (1 << 4) ++#define BCM2708_DMA_D_WIDTH (1 << 5) ++#define BCM2708_DMA_D_DREQ (1 << 6) ++#define BCM2708_DMA_S_INC (1 << 8) ++#define BCM2708_DMA_S_WIDTH (1 << 9) ++#define BCM2708_DMA_S_DREQ (1 << 10) ++ ++#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) ++#define BCM2708_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) ++ ++#define BCM2708_DMA_DREQ_EMMC 11 ++#define BCM2708_DMA_DREQ_SDHOST 13 ++ ++#define BCM2708_DMA_CS 0x00 /* Control and Status */ ++#define BCM2708_DMA_ADDR 0x04 ++/* the current control block appears in the following registers - read only */ ++#define BCM2708_DMA_INFO 0x08 ++#define BCM2708_DMA_SOURCE_AD 0x0c ++#define BCM2708_DMA_DEST_AD 0x10 ++#define BCM2708_DMA_NEXTCB 0x1C ++#define BCM2708_DMA_DEBUG 0x20 ++ ++#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) ++#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) ++ ++#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) ++ ++struct bcm2708_dma_cb { ++ unsigned long info; ++ unsigned long src; ++ unsigned long dst; ++ unsigned long length; ++ unsigned long stride; ++ unsigned long next; ++ unsigned long pad[2]; ++}; ++struct scatterlist; ++ ++extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); ++extern void bcm_dma_start(void __iomem *dma_chan_base, ++ dma_addr_t control_block); ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); ++extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); ++extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); ++ ++/* When listing features we can ask for when allocating DMA channels give ++ those with higher priority smaller ordinal numbers */ ++#define BCM_DMA_FEATURE_FAST_ORD 0 ++#define BCM_DMA_FEATURE_BULK_ORD 1 ++#define BCM_DMA_FEATURE_NORMAL_ORD 2 ++#define BCM_DMA_FEATURE_LITE_ORD 3 ++#define BCM_DMA_FEATURE_FAST (1< ++#include ++ ++ .macro disable_fiq ++ .endm ++ ++ .macro get_irqnr_preamble, base, tmp ++ ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE) ++ .endm ++ ++ .macro arch_ret_to_user, tmp1, tmp2 ++ .endm ++ ++ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ ++ /* get core's local interrupt controller */ ++ ldr \irqstat, = __io_address(ARM_LOCAL_IRQ_PENDING0) @ local interrupt source ++ add \irqstat, \irqstat, \tmp, lsl #2 ++ ldr \tmp, [\irqstat] ++ /* ignore gpu interrupt */ ++ bic \tmp, #0x100 ++ /* ignore mailbox interrupts */ ++ bics \tmp, #0xf0 ++ beq 1005f ++ ++ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) ++ @ N.B. CLZ is an ARM5 instruction. ++ mov \irqnr, #(ARM_IRQ_LOCAL_BASE + 31) ++ sub \irqstat, \tmp, #1 ++ eor \irqstat, \irqstat, \tmp ++ clz \tmp, \irqstat ++ sub \irqnr, \tmp ++ b 1020f ++1005: ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ ++ cmp \tmp, #1 ++ beq 1020f ++ cmp \tmp, #2 ++ beq 1020f ++ cmp \tmp, #3 ++ beq 1020f ++ ++ /* get masked status */ ++ ldr \irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] ++ mov \irqnr, #(ARM_IRQ0_BASE + 31) ++ and \tmp, \irqstat, #0x300 @ save bits 8 and 9 ++ /* clear bits 8 and 9, and test */ ++ bics \irqstat, \irqstat, #0x300 ++ bne 1010f ++ ++ tst \tmp, #0x100 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ1_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<7) | (1<<9) | (1<<10)) ++ bicne \irqstat, #((1<<18) | (1<<19)) ++ bne 1010f ++ ++ tst \tmp, #0x200 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ2_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25)) ++ bicne \irqstat, #((1<<30)) ++ beq 1020f ++ ++1010: ++ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) ++ @ N.B. CLZ is an ARM5 instruction. ++ sub \tmp, \irqstat, #1 ++ eor \irqstat, \irqstat, \tmp ++ clz \tmp, \irqstat ++ sub \irqnr, \tmp ++ ++1020: @ EQ will be set if no irqs pending ++ ++ .endm ++ ++ .macro test_for_ipi, irqnr, irqstat, base, tmp ++ /* get core number */ ++ mrc p15, 0, \tmp, c0, c0, 5 ++ ubfx \tmp, \tmp, #0, #2 ++ /* get core's mailbox interrupt control */ ++ ldr \irqstat, = __io_address(ARM_LOCAL_MAILBOX0_CLR0) @ mbox_clr ++ add \irqstat, \irqstat, \tmp, lsl #4 ++ ldr \tmp, [\irqstat] ++ cmp \tmp, #0 ++ beq 1030f ++ clz \tmp, \tmp ++ rsb \irqnr, \tmp, #31 ++ mov \tmp, #1 ++ lsl \tmp, \irqnr ++ str \tmp, [\irqstat] @ clear interrupt source ++ dsb ++1030: @ EQ will be set if no irqs pending ++ .endm +diff --git a/arch/arm/mach-bcm2709/include/mach/frc.h b/arch/arm/mach-bcm2709/include/mach/frc.h +new file mode 100644 +index 0000000..dd51e07 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/frc.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 free running counter (timer) ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _MACH_FRC_H ++#define _MACH_FRC_H ++ ++#define FRC_TICK_RATE (1000000) ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ (slightly faster than frc_clock_ticks63() ++ */ ++extern unsigned long frc_clock_ticks32(void); ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ * Note - top bit should be ignored (see cnt32_to_63) ++ */ ++extern unsigned long long frc_clock_ticks63(void); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/gpio.h b/arch/arm/mach-bcm2709/include/mach/gpio.h +new file mode 100644 +index 0000000..7965a97 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/gpio.h +@@ -0,0 +1,17 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/gpio.h ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#ifndef __ASM_ARCH_GPIO_H ++#define __ASM_ARCH_GPIO_H ++ ++#define BCM2708_NR_GPIOS 54 // number of gpio lines ++ ++#define gpio_to_irq(x) ((x) + GPIO_IRQ_START) ++#define irq_to_gpio(x) ((x) - GPIO_IRQ_START) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/hardware.h b/arch/arm/mach-bcm2709/include/mach/hardware.h +new file mode 100644 +index 0000000..c2954e8 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/hardware.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/hardware.h ++ * ++ * This file contains the hardware definitions of the BCM2708 devices. ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_HARDWARE_H ++#define __ASM_ARCH_HARDWARE_H ++ ++#include ++#include ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/io.h b/arch/arm/mach-bcm2709/include/mach/io.h +new file mode 100644 +index 0000000..e6eb84d +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/io.h +@@ -0,0 +1,27 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/io.h ++ * ++ * Copyright (C) 2003 ARM Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARM_ARCH_IO_H ++#define __ASM_ARM_ARCH_IO_H ++ ++#define IO_SPACE_LIMIT 0xffffffff ++ ++#define __io(a) __typesafe_io(a) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/irqs.h b/arch/arm/mach-bcm2709/include/mach/irqs.h +new file mode 100644 +index 0000000..d301f06c +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/irqs.h +@@ -0,0 +1,225 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/irqs.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_IRQS_H_ ++#define _BCM2708_IRQS_H_ ++ ++#include ++ ++/* ++ * IRQ interrupts definitions are the same as the INT definitions ++ * held within platform.h ++ */ ++#define IRQ_ARMCTRL_START 0 ++#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) ++#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) ++#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) ++#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) ++#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) ++#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) ++#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) ++#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) ++#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) ++#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) ++#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) ++#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) ++#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) ++#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) ++#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) ++#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) ++#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) ++#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) ++#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) ++#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) ++#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) ++#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) ++#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) ++#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) ++#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) ++#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) ++#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) ++#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) ++#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) ++#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) ++#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) ++#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) ++#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) ++#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) ++#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) ++#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) ++#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) ++#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) ++#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) ++#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) ++#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) ++#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) ++#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) ++#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) ++#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) ++#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) ++#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) ++#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) ++#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) ++#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) ++#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) ++#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) ++#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) ++#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) ++#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) ++#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) ++#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) ++#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) ++#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) ++#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) ++#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) ++#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) ++#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) ++#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) ++ ++#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) ++#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) ++#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) ++#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) ++#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) ++#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) ++#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) ++#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) ++#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) ++#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) ++ ++#define IRQ_ARM_LOCAL_CNTPSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) ++#define IRQ_ARM_LOCAL_CNTPNSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) ++#define IRQ_ARM_LOCAL_CNTHPIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) ++#define IRQ_ARM_LOCAL_CNTVIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) ++#define IRQ_ARM_LOCAL_MAILBOX0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) ++#define IRQ_ARM_LOCAL_MAILBOX1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) ++#define IRQ_ARM_LOCAL_MAILBOX2 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) ++#define IRQ_ARM_LOCAL_MAILBOX3 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) ++#define IRQ_ARM_LOCAL_GPU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) ++#define IRQ_ARM_LOCAL_PMU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) ++#define IRQ_ARM_LOCAL_ZERO (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) ++#define IRQ_ARM_LOCAL_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) ++ ++#define FIQ_START HARD_IRQS ++ ++/* ++ * FIQ interrupts definitions are the same as the INT definitions. ++ */ ++#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) ++#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) ++#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) ++#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) ++#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) ++#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) ++#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) ++#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) ++#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) ++#define FIQ_USB (FIQ_START+INTERRUPT_USB) ++#define FIQ_3D (FIQ_START+INTERRUPT_3D) ++#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) ++#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) ++#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) ++#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) ++#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) ++#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) ++#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) ++#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) ++#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) ++#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) ++#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) ++#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) ++#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) ++#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) ++#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) ++#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) ++#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) ++#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) ++#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) ++#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) ++#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) ++#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) ++#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) ++#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) ++#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) ++#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) ++#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) ++#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) ++#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) ++#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) ++#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) ++#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) ++#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) ++#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) ++#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) ++#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) ++#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) ++#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) ++#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) ++#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) ++#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) ++#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) ++#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) ++#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) ++#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) ++#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) ++#define FIQ_UART (FIQ_START+INTERRUPT_UART) ++#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) ++#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) ++#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) ++#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) ++#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) ++#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) ++ ++#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) ++#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) ++#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) ++#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) ++#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) ++#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) ++#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) ++#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) ++#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) ++#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) ++ ++#define FIQ_ARM_LOCAL_CNTPSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) ++#define FIQ_ARM_LOCAL_CNTPNSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) ++#define FIQ_ARM_LOCAL_CNTHPIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) ++#define FIQ_ARM_LOCAL_CNTVIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) ++#define FIQ_ARM_LOCAL_MAILBOX0 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) ++#define FIQ_ARM_LOCAL_MAILBOX1 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) ++#define FIQ_ARM_LOCAL_MAILBOX2 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) ++#define FIQ_ARM_LOCAL_MAILBOX3 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) ++#define FIQ_ARM_LOCAL_GPU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) ++#define FIQ_ARM_LOCAL_PMU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) ++#define FIQ_ARM_LOCAL_ZERO (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) ++#define FIQ_ARM_LOCAL_TIMER (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) ++ ++#define HARD_IRQS (128) ++#define FIQ_IRQS (128) ++#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) ++#define GPIO_IRQS (32*5) ++#define SPARE_ALLOC_IRQS 64 ++#define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) ++#define FREE_IRQS 128 ++#define NR_IRQS (BCM2708_ALLOC_IRQS+FREE_IRQS) ++ ++#endif /* _BCM2708_IRQS_H_ */ +diff --git a/arch/arm/mach-bcm2709/include/mach/memory.h b/arch/arm/mach-bcm2709/include/mach/memory.h +new file mode 100644 +index 0000000..7548a52 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/memory.h +@@ -0,0 +1,57 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/memory.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_MEMORY_H ++#define __ASM_ARCH_MEMORY_H ++ ++/* Memory overview: ++ ++ [ARMcore] <--virtual addr--> ++ [ARMmmu] <--physical addr--> ++ [GERTmap] <--bus add--> ++ [VCperiph] ++ ++*/ ++ ++/* ++ * Physical DRAM offset. ++ */ ++#define BCM_PLAT_PHYS_OFFSET UL(0x00000000) ++#define VC_ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ ++ ++#ifdef CONFIG_BCM2708_NOL2CACHE ++ #define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ ++#else ++ #define _REAL_BUS_OFFSET UL(0x40000000) /* use L2 cache */ ++#endif ++ ++/* We're using the memory at 64M in the VideoCore for Linux - this adjustment ++ * will provide the offset into this area as well as setting the bits that ++ * stop the L1 and L2 cache from being used ++ * ++ * WARNING: this only works because the ARM is given memory at a fixed location ++ * (ARMMEM_OFFSET) ++ */ ++#define BUS_OFFSET (VC_ARMMEM_OFFSET + _REAL_BUS_OFFSET) ++#define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) ++#define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) ++#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) ++#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/platform.h b/arch/arm/mach-bcm2709/include/mach/platform.h +new file mode 100644 +index 0000000..7157f38 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/platform.h +@@ -0,0 +1,225 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/platform.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_PLATFORM_H ++#define _BCM2708_PLATFORM_H ++ ++ ++/* macros to get at IO space when running virtually */ ++#define IO_ADDRESS(x) (((x) & 0x00ffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) ++ ++#define __io_address(n) IOMEM(IO_ADDRESS(n)) ++ ++ ++/* ++ * SDRAM ++ */ ++#define BCM2708_SDRAM_BASE 0x00000000 ++ ++/* ++ * Logic expansion modules ++ * ++ */ ++ ++ ++/* ------------------------------------------------------------------------ ++ * BCM2708 ARMCTRL Registers ++ * ------------------------------------------------------------------------ ++ */ ++ ++#define HW_REGISTER_RW(addr) (addr) ++#define HW_REGISTER_RO(addr) (addr) ++ ++#include "arm_control.h" ++#undef ARM_BASE ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define BCM2708_PERI_BASE 0x3F000000 ++#define IC0_BASE (BCM2708_PERI_BASE + 0x2000) ++#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ ++#define MPHI_BASE (BCM2708_PERI_BASE + 0x6000) /* Message -based Parallel Host Interface */ ++#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ ++#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ ++#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ ++#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ ++#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ ++#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ ++#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ ++#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ ++#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */ ++#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */ ++#define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */ ++#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ ++#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ ++#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ ++#define BSC1_BASE (BCM2708_PERI_BASE + 0x804000) /* BSC1 I2C/TWI */ ++#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ ++#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device (actually the multicore sync block*/ ++ ++#define ARMCTRL_BASE (ARM_BASE + 0x000) ++#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ ++#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ ++#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ ++ ++ ++/* ++ * Interrupt assignments ++ */ ++ ++#define ARM_IRQ1_BASE 0 ++#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) ++#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) ++#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) ++#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) ++#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) ++#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) ++#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) ++#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) ++#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) ++#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) ++#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) ++#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) ++#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) ++#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) ++#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) ++#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) ++#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) ++#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) ++#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) ++#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) ++#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) ++#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) ++#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) ++#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) ++#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) ++#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) ++#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) ++#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) ++#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) ++#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) ++#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) ++#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) ++ ++#define ARM_IRQ2_BASE 32 ++#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) ++#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) ++#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) ++#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) ++#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) ++#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) ++#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) ++#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) ++#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) ++#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) ++#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) ++#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) ++#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) ++#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) ++#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) ++#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) ++#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) ++#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) ++#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) ++#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) ++#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) ++#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) ++#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) ++#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) ++#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) ++#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) ++#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) ++#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) ++#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) ++#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) ++#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) ++#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) ++ ++#define ARM_IRQ0_BASE 64 ++#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) ++#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) ++#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) ++#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) ++#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) ++#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) ++#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) ++#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) ++#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) ++#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) ++#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) ++#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) ++#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) ++#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) ++#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) ++#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) ++#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) ++#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) ++#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) ++#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) ++#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) ++ ++#define ARM_IRQ_LOCAL_BASE 96 ++#define INTERRUPT_ARM_LOCAL_CNTPSIRQ (ARM_IRQ_LOCAL_BASE + 0) ++#define INTERRUPT_ARM_LOCAL_CNTPNSIRQ (ARM_IRQ_LOCAL_BASE + 1) ++#define INTERRUPT_ARM_LOCAL_CNTHPIRQ (ARM_IRQ_LOCAL_BASE + 2) ++#define INTERRUPT_ARM_LOCAL_CNTVIRQ (ARM_IRQ_LOCAL_BASE + 3) ++#define INTERRUPT_ARM_LOCAL_MAILBOX0 (ARM_IRQ_LOCAL_BASE + 4) ++#define INTERRUPT_ARM_LOCAL_MAILBOX1 (ARM_IRQ_LOCAL_BASE + 5) ++#define INTERRUPT_ARM_LOCAL_MAILBOX2 (ARM_IRQ_LOCAL_BASE + 6) ++#define INTERRUPT_ARM_LOCAL_MAILBOX3 (ARM_IRQ_LOCAL_BASE + 7) ++#define INTERRUPT_ARM_LOCAL_GPU_FAST (ARM_IRQ_LOCAL_BASE + 8) ++#define INTERRUPT_ARM_LOCAL_PMU_FAST (ARM_IRQ_LOCAL_BASE + 9) ++#define INTERRUPT_ARM_LOCAL_ZERO (ARM_IRQ_LOCAL_BASE + 10) ++#define INTERRUPT_ARM_LOCAL_TIMER (ARM_IRQ_LOCAL_BASE + 11) ++ ++/* ++ * Watchdog ++ */ ++#define PM_RSTC (PM_BASE+0x1c) ++#define PM_RSTS (PM_BASE+0x20) ++#define PM_WDOG (PM_BASE+0x24) ++ ++#define PM_WDOG_RESET 0000000000 ++#define PM_PASSWORD 0x5a000000 ++#define PM_WDOG_TIME_SET 0x000fffff ++#define PM_RSTC_WRCFG_CLR 0xffffffcf ++#define PM_RSTC_WRCFG_SET 0x00000030 ++#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 ++#define PM_RSTC_RESET 0x00000102 ++ ++#define PM_RSTS_HADPOR_SET 0x00001000 ++#define PM_RSTS_HADSRH_SET 0x00000400 ++#define PM_RSTS_HADSRF_SET 0x00000200 ++#define PM_RSTS_HADSRQ_SET 0x00000100 ++#define PM_RSTS_HADWRH_SET 0x00000040 ++#define PM_RSTS_HADWRF_SET 0x00000020 ++#define PM_RSTS_HADWRQ_SET 0x00000010 ++#define PM_RSTS_HADDRH_SET 0x00000004 ++#define PM_RSTS_HADDRF_SET 0x00000002 ++#define PM_RSTS_HADDRQ_SET 0x00000001 ++ ++#define UART0_CLOCK 3000000 ++ ++#endif ++ ++/* END */ +diff --git a/arch/arm/mach-bcm2709/include/mach/power.h b/arch/arm/mach-bcm2709/include/mach/power.h +new file mode 100644 +index 0000000..52b3b02 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/power.h +@@ -0,0 +1,26 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#ifndef _MACH_BCM2708_POWER_H ++#define _MACH_BCM2708_POWER_H ++ ++#include ++#include ++ ++typedef unsigned int BCM_POWER_HANDLE_T; ++ ++extern int bcm_power_open(BCM_POWER_HANDLE_T *handle); ++extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request); ++extern int bcm_power_close(BCM_POWER_HANDLE_T handle); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/system.h b/arch/arm/mach-bcm2709/include/mach/system.h +new file mode 100644 +index 0000000..2d0b821 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/system.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/system.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_SYSTEM_H ++#define __ASM_ARCH_SYSTEM_H ++ ++#include ++#include ++#include ++ ++static inline void arch_idle(void) ++{ ++ /* ++ * This should do all the clock switching ++ * and wait for interrupt tricks ++ */ ++ cpu_do_idle(); ++} ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/timex.h b/arch/arm/mach-bcm2709/include/mach/timex.h +new file mode 100644 +index 0000000..64a660c +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/timex.h +@@ -0,0 +1,23 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 sysem clock frequency ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#define CLOCK_TICK_RATE (1000000) +diff --git a/arch/arm/mach-bcm2709/include/mach/uncompress.h b/arch/arm/mach-bcm2709/include/mach/uncompress.h +new file mode 100644 +index 0000000..d634813 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/uncompress.h +@@ -0,0 +1,84 @@ ++/* ++ * arch/arm/mach-bcn2708/include/mach/uncompress.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++ ++#define UART_BAUD 115200 ++ ++#define BCM2708_UART_DR __io(UART0_BASE + UART01x_DR) ++#define BCM2708_UART_FR __io(UART0_BASE + UART01x_FR) ++#define BCM2708_UART_IBRD __io(UART0_BASE + UART011_IBRD) ++#define BCM2708_UART_FBRD __io(UART0_BASE + UART011_FBRD) ++#define BCM2708_UART_LCRH __io(UART0_BASE + UART011_LCRH) ++#define BCM2708_UART_CR __io(UART0_BASE + UART011_CR) ++ ++/* ++ * This does not append a newline ++ */ ++static inline void putc(int c) ++{ ++ while (__raw_readl(BCM2708_UART_FR) & UART01x_FR_TXFF) ++ barrier(); ++ ++ __raw_writel(c, BCM2708_UART_DR); ++} ++ ++static inline void flush(void) ++{ ++ int fr; ++ ++ do { ++ fr = __raw_readl(BCM2708_UART_FR); ++ barrier(); ++ } while ((fr & (UART011_FR_TXFE | UART01x_FR_BUSY)) != UART011_FR_TXFE); ++} ++ ++static inline void arch_decomp_setup(void) ++{ ++ int temp, div, rem, frac; ++ ++ temp = 16 * UART_BAUD; ++ div = UART0_CLOCK / temp; ++ rem = UART0_CLOCK % temp; ++ temp = (8 * rem) / UART_BAUD; ++ frac = (temp >> 1) + (temp & 1); ++ ++ /* Make sure the UART is disabled before we start */ ++ __raw_writel(0, BCM2708_UART_CR); ++ ++ /* Set the baud rate */ ++ __raw_writel(div, BCM2708_UART_IBRD); ++ __raw_writel(frac, BCM2708_UART_FBRD); ++ ++ /* Set the UART to 8n1, FIFO enabled */ ++ __raw_writel(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, BCM2708_UART_LCRH); ++ ++ /* Enable the UART */ ++ __raw_writel(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, ++ BCM2708_UART_CR); ++} ++ ++/* ++ * nothing to do ++ */ ++#define arch_decomp_wdog() +diff --git a/arch/arm/mach-bcm2709/include/mach/vc_mem.h b/arch/arm/mach-bcm2709/include/mach/vc_mem.h +new file mode 100644 +index 0000000..4a4a338 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vc_mem.h +@@ -0,0 +1,35 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#if !defined( VC_MEM_H ) ++#define VC_MEM_H ++ ++#include ++ ++#define VC_MEM_IOC_MAGIC 'v' ++ ++#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) ++#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) ++#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int ) ++#define VC_MEM_IOC_MEM_LOAD _IOR( VC_MEM_IOC_MAGIC, 3, unsigned int ) ++ ++#if defined( __KERNEL__ ) ++#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF ++ ++extern unsigned long mm_vc_mem_phys_addr; ++extern unsigned int mm_vc_mem_size; ++extern int vc_mem_get_current_size( void ); ++#endif ++ ++#endif /* VC_MEM_H */ +diff --git a/arch/arm/mach-bcm2709/include/mach/vc_support.h b/arch/arm/mach-bcm2709/include/mach/vc_support.h +new file mode 100755 +index 0000000..70e809f +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vc_support.h +@@ -0,0 +1,69 @@ ++#ifndef _VC_SUPPORT_H_ ++#define _VC_SUPPORT_H_ ++ ++/* ++ * vc_support.h ++ * ++ * Created on: 25 Nov 2012 ++ * Author: Simon ++ */ ++ ++enum { ++/* ++ If a MEM_HANDLE_T is discardable, the memory manager may resize it to size ++ 0 at any time when it is not locked or retained. ++ */ ++ MEM_FLAG_DISCARDABLE = 1 << 0, ++ ++ /* ++ If a MEM_HANDLE_T is allocating (or normal), its block of memory will be ++ accessed in an allocating fashion through the cache. ++ */ ++ MEM_FLAG_NORMAL = 0 << 2, ++ MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, ++ ++ /* ++ If a MEM_HANDLE_T is direct, its block of memory will be accessed ++ directly, bypassing the cache. ++ */ ++ MEM_FLAG_DIRECT = 1 << 2, ++ ++ /* ++ If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a ++ non-allocating fashion through the cache. ++ */ ++ MEM_FLAG_COHERENT = 2 << 2, ++ ++ /* ++ If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by ++ the VPU in a fashion which is allocating in L2, but only coherent in L1. ++ */ ++ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), ++ ++ /* ++ If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than ++ MEM_HANDLE_INVALID on allocation and resize up. ++ */ ++ MEM_FLAG_ZERO = 1 << 4, ++ ++ /* ++ If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value ++ (either zero, or all 1's) on allocation. ++ */ ++ MEM_FLAG_NO_INIT = 1 << 5, ++ ++ /* ++ Hints. ++ */ ++ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ ++}; ++ ++unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags); ++unsigned int ReleaseVcMemory(unsigned int handle); ++unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle); ++unsigned int UnlockVcMemory(unsigned int handle); ++ ++unsigned int ExecuteVcCode(unsigned int code, ++ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5); ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/vcio.h b/arch/arm/mach-bcm2709/include/mach/vcio.h +new file mode 100644 +index 0000000..8e11d67e +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vcio.h +@@ -0,0 +1,165 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vcio.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _MACH_BCM2708_VCIO_H ++#define _MACH_BCM2708_VCIO_H ++ ++/* Routines to handle I/O via the VideoCore "ARM control" registers ++ * (semaphores, doorbells, mailboxes) ++ */ ++ ++#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio" ++ ++/* Constants shared with the ARM identifying separate mailbox channels */ ++#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ ++#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ ++#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ ++#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ ++#define MBOX_CHAN_COUNT 9 ++ ++enum { ++ VCMSG_PROCESS_REQUEST = 0x00000000 ++}; ++enum { ++ VCMSG_REQUEST_SUCCESSFUL = 0x80000000, ++ VCMSG_REQUEST_FAILED = 0x80000001 ++}; ++/* Mailbox property tags */ ++enum { ++ VCMSG_PROPERTY_END = 0x00000000, ++ VCMSG_GET_FIRMWARE_REVISION = 0x00000001, ++ VCMSG_GET_BOARD_MODEL = 0x00010001, ++ VCMSG_GET_BOARD_REVISION = 0x00010002, ++ VCMSG_GET_BOARD_MAC_ADDRESS = 0x00010003, ++ VCMSG_GET_BOARD_SERIAL = 0x00010004, ++ VCMSG_GET_ARM_MEMORY = 0x00010005, ++ VCMSG_GET_VC_MEMORY = 0x00010006, ++ VCMSG_GET_CLOCKS = 0x00010007, ++ VCMSG_GET_COMMAND_LINE = 0x00050001, ++ VCMSG_GET_DMA_CHANNELS = 0x00060001, ++ VCMSG_GET_POWER_STATE = 0x00020001, ++ VCMSG_GET_TIMING = 0x00020002, ++ VCMSG_SET_POWER_STATE = 0x00028001, ++ VCMSG_GET_CLOCK_STATE = 0x00030001, ++ VCMSG_SET_CLOCK_STATE = 0x00038001, ++ VCMSG_GET_CLOCK_RATE = 0x00030002, ++ VCMSG_SET_CLOCK_RATE = 0x00038002, ++ VCMSG_GET_VOLTAGE = 0x00030003, ++ VCMSG_SET_VOLTAGE = 0x00038003, ++ VCMSG_GET_MAX_CLOCK = 0x00030004, ++ VCMSG_GET_MAX_VOLTAGE = 0x00030005, ++ VCMSG_GET_TEMPERATURE = 0x00030006, ++ VCMSG_GET_MIN_CLOCK = 0x00030007, ++ VCMSG_GET_MIN_VOLTAGE = 0x00030008, ++ VCMSG_GET_TURBO = 0x00030009, ++ VCMSG_GET_MAX_TEMPERATURE = 0x0003000a, ++ VCMSG_GET_STC = 0x0003000b, ++ VCMSG_SET_TURBO = 0x00038009, ++ VCMSG_SET_ALLOCATE_MEM = 0x0003000c, ++ VCMSG_SET_LOCK_MEM = 0x0003000d, ++ VCMSG_SET_UNLOCK_MEM = 0x0003000e, ++ VCMSG_SET_RELEASE_MEM = 0x0003000f, ++ VCMSG_SET_EXECUTE_CODE = 0x00030010, ++ VCMSG_SET_EXECUTE_QPU = 0x00030011, ++ VCMSG_SET_ENABLE_QPU = 0x00030012, ++ VCMSG_GET_RESOURCE_HANDLE = 0x00030014, ++ VCMSG_GET_EDID_BLOCK = 0x00030020, ++ VCMSG_GET_CUSTOMER_OTP = 0x00030021, ++ VCMSG_SET_CUSTOMER_OTP = 0x00038021, ++ VCMSG_SET_ALLOCATE_BUFFER = 0x00040001, ++ VCMSG_SET_RELEASE_BUFFER = 0x00048001, ++ VCMSG_SET_BLANK_SCREEN = 0x00040002, ++ VCMSG_TST_BLANK_SCREEN = 0x00044002, ++ VCMSG_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, ++ VCMSG_TST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, ++ VCMSG_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, ++ VCMSG_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, ++ VCMSG_TST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, ++ VCMSG_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, ++ VCMSG_GET_DEPTH = 0x00040005, ++ VCMSG_TST_DEPTH = 0x00044005, ++ VCMSG_SET_DEPTH = 0x00048005, ++ VCMSG_GET_PIXEL_ORDER = 0x00040006, ++ VCMSG_TST_PIXEL_ORDER = 0x00044006, ++ VCMSG_SET_PIXEL_ORDER = 0x00048006, ++ VCMSG_GET_ALPHA_MODE = 0x00040007, ++ VCMSG_TST_ALPHA_MODE = 0x00044007, ++ VCMSG_SET_ALPHA_MODE = 0x00048007, ++ VCMSG_GET_PITCH = 0x00040008, ++ VCMSG_TST_PITCH = 0x00044008, ++ VCMSG_SET_PITCH = 0x00048008, ++ VCMSG_GET_VIRTUAL_OFFSET = 0x00040009, ++ VCMSG_TST_VIRTUAL_OFFSET = 0x00044009, ++ VCMSG_SET_VIRTUAL_OFFSET = 0x00048009, ++ VCMSG_GET_OVERSCAN = 0x0004000a, ++ VCMSG_TST_OVERSCAN = 0x0004400a, ++ VCMSG_SET_OVERSCAN = 0x0004800a, ++ VCMSG_GET_PALETTE = 0x0004000b, ++ VCMSG_TST_PALETTE = 0x0004400b, ++ VCMSG_SET_PALETTE = 0x0004800b, ++ VCMSG_GET_LAYER = 0x0004000c, ++ VCMSG_TST_LAYER = 0x0004400c, ++ VCMSG_SET_LAYER = 0x0004800c, ++ VCMSG_GET_TRANSFORM = 0x0004000d, ++ VCMSG_TST_TRANSFORM = 0x0004400d, ++ VCMSG_SET_TRANSFORM = 0x0004800d, ++ VCMSG_TST_VSYNC = 0x0004400e, ++ VCMSG_SET_VSYNC = 0x0004800e, ++ VCMSG_SET_CURSOR_INFO = 0x00008010, ++ VCMSG_SET_CURSOR_STATE = 0x00008011, ++}; ++ ++extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28); ++extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28); ++extern int /*rc*/ bcm_mailbox_property(void *data, int size); ++ ++#include ++ ++/* ++ * The major device number. We can't rely on dynamic ++ * registration any more, because ioctls need to know ++ * it. ++ */ ++#define MAJOR_NUM 100 ++ ++/* ++ * Set the message of the device driver ++ */ ++#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) ++/* ++ * _IOWR means that we're creating an ioctl command ++ * number for passing information from a user process ++ * to the kernel module and from the kernel module to user process ++ * ++ * The first arguments, MAJOR_NUM, is the major device ++ * number we're using. ++ * ++ * The second argument is the number of the command ++ * (there could be several with different meanings). ++ * ++ * The third argument is the type we want to get from ++ * the process to the kernel. ++ */ ++ ++/* ++ * The name of the device file ++ */ ++#define DEVICE_FILE_NAME "vcio" ++ ++#endif +diff --git a/arch/arm/mach-bcm2709/include/mach/vmalloc.h b/arch/arm/mach-bcm2709/include/mach/vmalloc.h +new file mode 100644 +index 0000000..6aa6826 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/include/mach/vmalloc.h +@@ -0,0 +1,20 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vmalloc.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#define VMALLOC_END (0xff000000) +diff --git a/arch/arm/mach-bcm2709/power.c b/arch/arm/mach-bcm2709/power.c +new file mode 100644 +index 0000000..3421057 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/power.c +@@ -0,0 +1,195 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "bcm2708_power" ++ ++#define BCM_POWER_MAXCLIENTS 4 ++#define BCM_POWER_NOCLIENT (1<<31) ++ ++/* Some drivers expect there devices to be permanently powered */ ++#ifdef CONFIG_USB ++#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB) ++#endif ++ ++#if 1 ++#define DPRINTK printk ++#else ++#define DPRINTK if (0) printk ++#endif ++ ++struct state_struct { ++ uint32_t global_request; ++ uint32_t client_request[BCM_POWER_MAXCLIENTS]; ++ struct semaphore client_mutex; ++ struct semaphore mutex; ++} g_state; ++ ++int bcm_power_open(BCM_POWER_HANDLE_T *handle) ++{ ++ BCM_POWER_HANDLE_T i; ++ int ret = -EBUSY; ++ ++ down(&g_state.client_mutex); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (g_state.client_request[i] == BCM_POWER_NOCLIENT) { ++ g_state.client_request[i] = BCM_POWER_NONE; ++ *handle = i; ++ ret = 0; ++ break; ++ } ++ } ++ ++ up(&g_state.client_mutex); ++ ++ DPRINTK("bcm_power_open() -> %d\n", *handle); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(bcm_power_open); ++ ++int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request) ++{ ++ int rc = 0; ++ ++ DPRINTK("bcm_power_request(%d, %x)\n", handle, request); ++ ++ if ((handle < BCM_POWER_MAXCLIENTS) && ++ (g_state.client_request[handle] != BCM_POWER_NOCLIENT)) { ++ if (down_interruptible(&g_state.mutex) != 0) { ++ DPRINTK("bcm_power_request -> interrupted\n"); ++ return -EINTR; ++ } ++ ++ if (request != g_state.client_request[handle]) { ++ uint32_t others_request = 0; ++ uint32_t global_request; ++ BCM_POWER_HANDLE_T i; ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (i != handle) ++ others_request |= ++ g_state.client_request[i]; ++ } ++ others_request &= ~BCM_POWER_NOCLIENT; ++ ++ global_request = request | others_request; ++ if (global_request != g_state.global_request) { ++ uint32_t actual; ++ ++ /* Send a request to VideoCore */ ++ bcm_mailbox_write(MBOX_CHAN_POWER, ++ global_request << 4); ++ ++ /* Wait for a response during power-up */ ++ if (global_request & ~g_state.global_request) { ++ rc = bcm_mailbox_read(MBOX_CHAN_POWER, ++ &actual); ++ DPRINTK ++ ("bcm_mailbox_read -> %08x, %d\n", ++ actual, rc); ++ actual >>= 4; ++ } else { ++ rc = 0; ++ actual = global_request; ++ } ++ ++ if (rc == 0) { ++ if (actual != global_request) { ++ printk(KERN_ERR ++ "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", ++ __func__, ++ g_state.global_request, ++ global_request, actual, request, others_request); ++ /* A failure */ ++ BUG_ON((others_request & actual) ++ != others_request); ++ request &= actual; ++ rc = -EIO; ++ } ++ ++ g_state.global_request = actual; ++ g_state.client_request[handle] = ++ request; ++ } ++ } ++ } ++ up(&g_state.mutex); ++ } else { ++ rc = -EINVAL; ++ } ++ DPRINTK("bcm_power_request -> %d\n", rc); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_request); ++ ++int bcm_power_close(BCM_POWER_HANDLE_T handle) ++{ ++ int rc; ++ ++ DPRINTK("bcm_power_close(%d)\n", handle); ++ ++ rc = bcm_power_request(handle, BCM_POWER_NONE); ++ if (rc == 0) ++ g_state.client_request[handle] = BCM_POWER_NOCLIENT; ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_close); ++ ++static int __init bcm_power_init(void) ++{ ++#if defined(BCM_POWER_ALWAYS_ON) ++ BCM_POWER_HANDLE_T always_on_handle; ++#endif ++ int rc = 0; ++ int i; ++ ++ printk(KERN_INFO "bcm_power: Broadcom power driver\n"); ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) ++ g_state.client_request[i] = BCM_POWER_NOCLIENT; ++ ++ sema_init(&g_state.client_mutex, 1); ++ sema_init(&g_state.mutex, 1); ++ ++ g_state.global_request = 0; ++#if defined(BCM_POWER_ALWAYS_ON) ++ if (BCM_POWER_ALWAYS_ON) { ++ bcm_power_open(&always_on_handle); ++ bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON); ++ } ++#endif ++ ++ return rc; ++} ++ ++static void __exit bcm_power_exit(void) ++{ ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); ++} ++ ++arch_initcall(bcm_power_init); /* Initialize early */ ++module_exit(bcm_power_exit); ++ ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_DESCRIPTION("Interface to BCM2708 power management"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-bcm2709/vc_mem.c b/arch/arm/mach-bcm2709/vc_mem.c +new file mode 100644 +index 0000000..ac578db +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vc_mem.c +@@ -0,0 +1,431 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_ARCH_KONA ++#include ++#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) ++#else ++#include ++#endif ++ ++#include "mach/vc_mem.h" ++#include ++ ++#define DRIVER_NAME "vc-mem" ++ ++// Device (/dev) related variables ++static dev_t vc_mem_devnum = 0; ++static struct class *vc_mem_class = NULL; ++static struct cdev vc_mem_cdev; ++static int vc_mem_inited = 0; ++ ++#ifdef CONFIG_DEBUG_FS ++static struct dentry *vc_mem_debugfs_entry; ++#endif ++ ++/* ++ * Videocore memory addresses and size ++ * ++ * Drivers that wish to know the videocore memory addresses and sizes should ++ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in ++ * headers. This allows the other drivers to not be tied down to a a certain ++ * address/size at compile time. ++ * ++ * In the future, the goal is to have the videocore memory virtual address and ++ * size be calculated at boot time rather than at compile time. The decision of ++ * where the videocore memory resides and its size would be in the hands of the ++ * bootloader (and/or kernel). When that happens, the values of these variables ++ * would be calculated and assigned in the init function. ++ */ ++// in the 2835 VC in mapped above ARM, but ARM has full access to VC space ++unsigned long mm_vc_mem_phys_addr = 0x00000000; ++unsigned int mm_vc_mem_size = 0; ++unsigned int mm_vc_mem_base = 0; ++ ++EXPORT_SYMBOL(mm_vc_mem_phys_addr); ++EXPORT_SYMBOL(mm_vc_mem_size); ++EXPORT_SYMBOL(mm_vc_mem_base); ++ ++static uint phys_addr = 0; ++static uint mem_size = 0; ++static uint mem_base = 0; ++ ++ ++/**************************************************************************** ++* ++* vc_mem_open ++* ++***************************************************************************/ ++ ++static int ++vc_mem_open(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_release ++* ++***************************************************************************/ ++ ++static int ++vc_mem_release(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_size ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_size(void) ++{ ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_base ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_base(void) ++{ ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_current_size ++* ++***************************************************************************/ ++ ++int ++vc_mem_get_current_size(void) ++{ ++ return mm_vc_mem_size; ++} ++ ++EXPORT_SYMBOL_GPL(vc_mem_get_current_size); ++ ++/**************************************************************************** ++* ++* vc_mem_ioctl ++* ++***************************************************************************/ ++ ++static long ++vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int rc = 0; ++ ++ (void) cmd; ++ (void) arg; ++ ++ pr_debug("%s: called file = 0x%p\n", __func__, file); ++ ++ switch (cmd) { ++ case VC_MEM_IOC_MEM_PHYS_ADDR: ++ { ++ pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n", ++ __func__, (void *) mm_vc_mem_phys_addr); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr, ++ sizeof (mm_vc_mem_phys_addr)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_SIZE: ++ { ++ // Get the videocore memory size first ++ vc_mem_get_size(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%u\n", __func__, ++ mm_vc_mem_size); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_size, ++ sizeof (mm_vc_mem_size)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_BASE: ++ { ++ // Get the videocore memory base ++ vc_mem_get_base(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_BASE=%u\n", __func__, ++ mm_vc_mem_base); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_base, ++ sizeof (mm_vc_mem_base)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_LOAD: ++ { ++ // Get the videocore memory base ++ vc_mem_get_base(); ++ ++ pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%u\n", __func__, ++ mm_vc_mem_base); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_base, ++ sizeof (mm_vc_mem_base)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ default: ++ { ++ return -ENOTTY; ++ } ++ } ++ pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc); ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_mmap ++* ++***************************************************************************/ ++ ++static int ++vc_mem_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ int rc = 0; ++ unsigned long length = vma->vm_end - vma->vm_start; ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ ++ pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n", ++ __func__, (long) vma->vm_start, (long) vma->vm_end, ++ (long) vma->vm_pgoff); ++ ++ if (offset + length > mm_vc_mem_size) { ++ pr_err("%s: length %ld is too big\n", __func__, length); ++ return -EINVAL; ++ } ++ // Do not cache the memory map ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ rc = remap_pfn_range(vma, vma->vm_start, ++ (mm_vc_mem_phys_addr >> PAGE_SHIFT) + ++ vma->vm_pgoff, length, vma->vm_page_prot); ++ if (rc != 0) { ++ pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc); ++ } ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* File Operations for the driver. ++* ++***************************************************************************/ ++ ++static const struct file_operations vc_mem_fops = { ++ .owner = THIS_MODULE, ++ .open = vc_mem_open, ++ .release = vc_mem_release, ++ .unlocked_ioctl = vc_mem_ioctl, ++ .mmap = vc_mem_mmap, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++static void vc_mem_debugfs_deinit(void) ++{ ++ debugfs_remove_recursive(vc_mem_debugfs_entry); ++ vc_mem_debugfs_entry = NULL; ++} ++ ++ ++static int vc_mem_debugfs_init( ++ struct device *dev) ++{ ++ vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL); ++ if (!vc_mem_debugfs_entry) { ++ dev_warn(dev, "could not create debugfs entry\n"); ++ return -EFAULT; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_phys_addr", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_phys_addr)) { ++ dev_warn(dev, "%s:could not create vc_mem_phys entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_size", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_size)) { ++ dev_warn(dev, "%s:could not create vc_mem_size entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ if (!debugfs_create_x32("vc_mem_base", ++ 0444, ++ vc_mem_debugfs_entry, ++ (u32 *)&mm_vc_mem_base)) { ++ dev_warn(dev, "%s:could not create vc_mem_base entry\n", ++ __func__); ++ goto fail; ++ } ++ ++ return 0; ++ ++fail: ++ vc_mem_debugfs_deinit(); ++ return -EFAULT; ++} ++ ++#endif /* CONFIG_DEBUG_FS */ ++ ++ ++/**************************************************************************** ++* ++* vc_mem_init ++* ++***************************************************************************/ ++ ++static int __init ++vc_mem_init(void) ++{ ++ int rc = -EFAULT; ++ struct device *dev; ++ ++ pr_debug("%s: called\n", __func__); ++ ++ mm_vc_mem_phys_addr = phys_addr; ++ mm_vc_mem_size = mem_size; ++ mm_vc_mem_base = mem_base; ++ ++ vc_mem_get_size(); ++ ++ pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n", ++ mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024)); ++ ++ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) { ++ pr_err("%s: alloc_chrdev_region failed (rc=%d)\n", ++ __func__, rc); ++ goto out_err; ++ } ++ ++ cdev_init(&vc_mem_cdev, &vc_mem_fops); ++ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) { ++ pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc); ++ goto out_unregister; ++ } ++ ++ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME); ++ if (IS_ERR(vc_mem_class)) { ++ rc = PTR_ERR(vc_mem_class); ++ pr_err("%s: class_create failed (rc=%d)\n", __func__, rc); ++ goto out_cdev_del; ++ } ++ ++ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL, ++ DRIVER_NAME); ++ if (IS_ERR(dev)) { ++ rc = PTR_ERR(dev); ++ pr_err("%s: device_create failed (rc=%d)\n", __func__, rc); ++ goto out_class_destroy; ++ } ++ ++#ifdef CONFIG_DEBUG_FS ++ /* don't fail if the debug entries cannot be created */ ++ vc_mem_debugfs_init(dev); ++#endif ++ ++ vc_mem_inited = 1; ++ return 0; ++ ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ ++ out_class_destroy: ++ class_destroy(vc_mem_class); ++ vc_mem_class = NULL; ++ ++ out_cdev_del: ++ cdev_del(&vc_mem_cdev); ++ ++ out_unregister: ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ ++ out_err: ++ return -1; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_exit ++* ++***************************************************************************/ ++ ++static void __exit ++vc_mem_exit(void) ++{ ++ pr_debug("%s: called\n", __func__); ++ ++ if (vc_mem_inited) { ++#if CONFIG_DEBUG_FS ++ vc_mem_debugfs_deinit(); ++#endif ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ class_destroy(vc_mem_class); ++ cdev_del(&vc_mem_cdev); ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ } ++} ++ ++module_init(vc_mem_init); ++module_exit(vc_mem_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); ++ ++module_param(phys_addr, uint, 0644); ++module_param(mem_size, uint, 0644); ++module_param(mem_base, uint, 0644); +diff --git a/arch/arm/mach-bcm2709/vc_support.c b/arch/arm/mach-bcm2709/vc_support.c +new file mode 100755 +index 0000000..0bc41c4 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vc_support.c +@@ -0,0 +1,318 @@ ++/* ++ * vc_support.c ++ * ++ * Created on: 25 Nov 2012 ++ * Author: Simon ++ */ ++ ++#include ++#include ++ ++#ifdef ECLIPSE_IGNORE ++ ++#define __user ++#define __init ++#define __exit ++#define __iomem ++#define KERN_DEBUG ++#define KERN_ERR ++#define KERN_WARNING ++#define KERN_INFO ++#define _IOWR(a, b, c) b ++#define _IOW(a, b, c) b ++#define _IO(a, b) b ++ ++#endif ++ ++/****** VC MAILBOX FUNCTIONALITY ******/ ++unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_size; ++ unsigned int m_handle; ++ }; ++ unsigned int m_alignment; ++ unsigned int m_flags; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the allocation command ++ msg.m_tag.m_tagId = 0x3000c; ++ msg.m_tag.m_sendBufferSize = 12; ++ msg.m_tag.m_sendDataSize = 12; ++ ++ //fill in our args ++ msg.m_tag.m_args.m_size = size; ++ msg.m_tag.m_args.m_alignment = alignment; ++ msg.m_tag.m_args.m_flags = flags; ++ ++ //run the command ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ { ++ *pHandle = msg.m_tag.m_args.m_handle; ++ return 0; ++ } ++ else ++ { ++ printk(KERN_ERR "failed to allocate vc memory: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} ++ ++unsigned int ReleaseVcMemory(unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_error; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the release command ++ msg.m_tag.m_tagId = 0x3000f; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) ++ return 0; ++ else ++ { ++ printk(KERN_ERR "failed to release vc memory: s=%d response=%08x recv data size=%08x error=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); ++ return 1; ++ } ++} ++ ++unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_busAddress; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the lock command ++ msg.m_tag.m_tagId = 0x3000d; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ { ++ //pick out the bus address ++ *pBusAddress = msg.m_tag.m_args.m_busAddress; ++ return 0; ++ } ++ else ++ { ++ printk(KERN_ERR "failed to lock vc memory: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} ++ ++unsigned int UnlockVcMemory(unsigned int handle) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_handle; ++ unsigned int m_error; ++ }; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the unlock command ++ msg.m_tag.m_tagId = 0x3000e; ++ msg.m_tag.m_sendBufferSize = 4; ++ msg.m_tag.m_sendDataSize = 4; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_handle = handle; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ //check the error code too ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) ++ return 0; ++ else ++ { ++ printk(KERN_ERR "failed to unlock vc memory: s=%d response=%08x recv data size=%08x error%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); ++ return 1; ++ } ++} ++ ++unsigned int ExecuteVcCode(unsigned int code, ++ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5) ++{ ++ struct vc_msg ++ { ++ unsigned int m_msgSize; ++ unsigned int m_response; ++ ++ struct vc_tag ++ { ++ unsigned int m_tagId; ++ unsigned int m_sendBufferSize; ++ union { ++ unsigned int m_sendDataSize; ++ unsigned int m_recvDataSize; ++ }; ++ ++ struct args ++ { ++ union { ++ unsigned int m_pCode; ++ unsigned int m_return; ++ }; ++ unsigned int m_r0; ++ unsigned int m_r1; ++ unsigned int m_r2; ++ unsigned int m_r3; ++ unsigned int m_r4; ++ unsigned int m_r5; ++ } m_args; ++ } m_tag; ++ ++ unsigned int m_endTag; ++ } msg; ++ int s; ++ ++ msg.m_msgSize = sizeof(msg); ++ msg.m_response = 0; ++ msg.m_endTag = 0; ++ ++ //fill in the tag for the unlock command ++ msg.m_tag.m_tagId = 0x30010; ++ msg.m_tag.m_sendBufferSize = 28; ++ msg.m_tag.m_sendDataSize = 28; ++ ++ //pass across the handle ++ msg.m_tag.m_args.m_pCode = code; ++ msg.m_tag.m_args.m_r0 = r0; ++ msg.m_tag.m_args.m_r1 = r1; ++ msg.m_tag.m_args.m_r2 = r2; ++ msg.m_tag.m_args.m_r3 = r3; ++ msg.m_tag.m_args.m_r4 = r4; ++ msg.m_tag.m_args.m_r5 = r5; ++ ++ s = bcm_mailbox_property(&msg, sizeof(msg)); ++ ++ //check the error code too ++ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) ++ return msg.m_tag.m_args.m_return; ++ else ++ { ++ printk(KERN_ERR "failed to execute: s=%d response=%08x recv data size=%08x\n", ++ s, msg.m_response, msg.m_tag.m_recvDataSize); ++ return 1; ++ } ++} +diff --git a/arch/arm/mach-bcm2709/vcio.c b/arch/arm/mach-bcm2709/vcio.c +new file mode 100644 +index 0000000..5e43e85 +--- /dev/null ++++ b/arch/arm/mach-bcm2709/vcio.c +@@ -0,0 +1,474 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/vcio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for writing to the mailboxes, ++ * semaphores, doorbells etc. that are shared between the ARM and the ++ * VideoCore processor ++ */ ++ ++#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++ ++#define DRIVER_NAME BCM_VCIO_DRIVER_NAME ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox ++ * -------------------------------------------------------------------- */ ++ ++/* offsets from a mail box base address */ ++#define MAIL_WRT 0x00 /* write - and next 4 words */ ++#define MAIL_RD 0x00 /* read - and next 4 words */ ++#define MAIL_POL 0x10 /* read without popping the fifo */ ++#define MAIL_SND 0x14 /* sender ID (bottom two bits) */ ++#define MAIL_STA 0x18 /* status */ ++#define MAIL_CNF 0x1C /* configuration */ ++ ++#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) ++#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) ++#define MBOX_CHAN(msg) ((msg) & 0xf) ++#define MBOX_DATA28(msg) ((msg) & ~0xf) ++#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) ++ ++#define MBOX_MAGIC 0xd0d0c0de ++ ++struct vc_mailbox { ++ struct device *dev; /* parent device */ ++ void __iomem *status; ++ void __iomem *config; ++ void __iomem *read; ++ void __iomem *write; ++ uint32_t msg[MBOX_CHAN_COUNT]; ++ struct semaphore sema[MBOX_CHAN_COUNT]; ++ uint32_t magic; ++}; ++ ++static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev, ++ uint32_t addr_mbox) ++{ ++ int i; ++ ++ mbox_out->dev = dev; ++ mbox_out->status = __io_address(addr_mbox + MAIL_STA); ++ mbox_out->config = __io_address(addr_mbox + MAIL_CNF); ++ mbox_out->read = __io_address(addr_mbox + MAIL_RD); ++ /* Write to the other mailbox */ ++ mbox_out->write = ++ __io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) + ++ MAIL_WRT); ++ ++ for (i = 0; i < MBOX_CHAN_COUNT; i++) { ++ mbox_out->msg[i] = 0; ++ sema_init(&mbox_out->sema[i], 0); ++ } ++ ++ /* Enable the interrupt on data reception */ ++ writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->config); ++ ++ mbox_out->magic = MBOX_MAGIC; ++} ++ ++static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ /* wait for the mailbox FIFO to have some space in it */ ++ while (0 != (readl(mbox->status) & ARM_MS_FULL)) ++ cpu_relax(); ++ ++ writel(MBOX_MSG(chan, data28), mbox->write); ++ rc = 0; ++ } ++ return rc; ++} ++ ++static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ down(&mbox->sema[chan]); ++ *data28 = MBOX_DATA28(mbox->msg[chan]); ++ mbox->msg[chan] = 0; ++ rc = 0; ++ } ++ return rc; ++} ++ ++static irqreturn_t mbox_irq(int irq, void *dev_id) ++{ ++ /* wait for the mailbox FIFO to have some data in it */ ++ struct vc_mailbox *mbox = (struct vc_mailbox *) dev_id; ++ int status = readl(mbox->status); ++ int ret = IRQ_NONE; ++ ++ while (!(status & ARM_MS_EMPTY)) { ++ uint32_t msg = readl(mbox->read); ++ int chan = MBOX_CHAN(msg); ++ if (chan < MBOX_CHAN_COUNT) { ++ if (mbox->msg[chan]) { ++ /* Overflow */ ++ printk(KERN_ERR DRIVER_NAME ++ ": mbox chan %d overflow - drop %08x\n", ++ chan, msg); ++ } else { ++ mbox->msg[chan] = (msg | 0xf); ++ up(&mbox->sema[chan]); ++ } ++ } else { ++ printk(KERN_ERR DRIVER_NAME ++ ": invalid channel selector (msg %08x)\n", msg); ++ } ++ ret = IRQ_HANDLED; ++ status = readl(mbox->status); ++ } ++ return ret; ++} ++ ++static struct irqaction mbox_irqaction = { ++ .name = "ARM Mailbox IRQ", ++ .flags = IRQF_DISABLED | IRQF_IRQPOLL, ++ .handler = mbox_irq, ++}; ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox Methods ++ * -------------------------------------------------------------------- */ ++ ++static struct device *mbox_dev; /* we assume there's only one! */ ++ ++static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_write(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_read(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++extern int bcm_mailbox_write(unsigned chan, uint32_t data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_write(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_write); ++ ++extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_read(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_read); ++ ++static void dev_mbox_register(const char *dev_name, struct device *dev) ++{ ++ mbox_dev = dev; ++} ++ ++static int mbox_copy_from_user(void *dst, const void *src, int size) ++{ ++ if ( (uint32_t)src < TASK_SIZE) ++ { ++ return copy_from_user(dst, src, size); ++ } ++ else ++ { ++ memcpy( dst, src, size ); ++ return 0; ++ } ++} ++ ++static int mbox_copy_to_user(void *dst, const void *src, int size) ++{ ++ if ( (uint32_t)dst < TASK_SIZE) ++ { ++ return copy_to_user(dst, src, size); ++ } ++ else ++ { ++ memcpy( dst, src, size ); ++ return 0; ++ } ++} ++ ++static DEFINE_MUTEX(mailbox_lock); ++extern int bcm_mailbox_property(void *data, int size) ++{ ++ uint32_t success; ++ dma_addr_t mem_bus; /* the memory address accessed from videocore */ ++ void *mem_kern; /* the memory address accessed from driver */ ++ int s = 0; ++ ++ mutex_lock(&mailbox_lock); ++ /* allocate some memory for the messages communicating with GPU */ ++ mem_kern = dma_alloc_coherent(NULL, PAGE_ALIGN(size), &mem_bus, GFP_ATOMIC); ++ if (mem_kern) { ++ /* create the message */ ++ mbox_copy_from_user(mem_kern, data, size); ++ ++ /* send the message */ ++ wmb(); ++ s = bcm_mailbox_write(MBOX_CHAN_PROPERTY, (uint32_t)mem_bus); ++ if (s == 0) { ++ s = bcm_mailbox_read(MBOX_CHAN_PROPERTY, &success); ++ } ++ if (s == 0) { ++ /* copy the response */ ++ rmb(); ++ mbox_copy_to_user(data, mem_kern, size); ++ } ++ dma_free_coherent(NULL, PAGE_ALIGN(size), mem_kern, mem_bus); ++ } else { ++ s = -ENOMEM; ++ } ++ if (s != 0) ++ printk(KERN_ERR DRIVER_NAME ": %s failed (%d)\n", __func__, s); ++ ++ mutex_unlock(&mailbox_lock); ++ return s; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_property); ++ ++/* ---------------------------------------------------------------------- ++ * Platform Device for Mailbox ++ * -------------------------------------------------------------------- */ ++ ++/* ++ * Is the device open right now? Used to prevent ++ * concurent access into the same device ++ */ ++static int Device_Open = 0; ++ ++/* ++ * This is called whenever a process attempts to open the device file ++ */ ++static int device_open(struct inode *inode, struct file *file) ++{ ++ /* ++ * We don't want to talk to two processes at the same time ++ */ ++ if (Device_Open) ++ return -EBUSY; ++ ++ Device_Open++; ++ /* ++ * Initialize the message ++ */ ++ try_module_get(THIS_MODULE); ++ return 0; ++} ++ ++static int device_release(struct inode *inode, struct file *file) ++{ ++ /* ++ * We're now ready for our next caller ++ */ ++ Device_Open--; ++ ++ module_put(THIS_MODULE); ++ return 0; ++} ++ ++/* ++ * This function is called whenever a process tries to do an ioctl on our ++ * device file. We get two extra parameters (additional to the inode and file ++ * structures, which all device functions get): the number of the ioctl called ++ * and the parameter given to the ioctl function. ++ * ++ * If the ioctl is write or read/write (meaning output is returned to the ++ * calling process), the ioctl call returns the output of this function. ++ * ++ */ ++static long device_ioctl(struct file *file, /* see include/linux/fs.h */ ++ unsigned int ioctl_num, /* number and param for ioctl */ ++ unsigned long ioctl_param) ++{ ++ unsigned size; ++ /* ++ * Switch according to the ioctl called ++ */ ++ switch (ioctl_num) { ++ case IOCTL_MBOX_PROPERTY: ++ /* ++ * Receive a pointer to a message (in user space) and set that ++ * to be the device's message. Get the parameter given to ++ * ioctl by the process. ++ */ ++ mbox_copy_from_user(&size, (void *)ioctl_param, sizeof size); ++ return bcm_mailbox_property((void *)ioctl_param, size); ++ break; ++ default: ++ printk(KERN_ERR DRIVER_NAME "unknown ioctl: %d\n", ioctl_num); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/* Module Declarations */ ++ ++/* ++ * This structure will hold the functions to be called ++ * when a process does something to the device we ++ * created. Since a pointer to this structure is kept in ++ * the devices table, it can't be local to ++ * init_module. NULL is for unimplemented functios. ++ */ ++struct file_operations fops = { ++ .unlocked_ioctl = device_ioctl, ++ .open = device_open, ++ .release = device_release, /* a.k.a. close */ ++}; ++ ++static int bcm_vcio_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_mailbox *mailbox; ++ ++ mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); ++ if (NULL == mailbox) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ ret = -ENOMEM; ++ } else { ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ kfree(mailbox); ++ } else { ++ /* should be based on the registers from res really */ ++ mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD); ++ ++ platform_set_drvdata(pdev, mailbox); ++ dev_mbox_register(DRIVER_NAME, &pdev->dev); ++ ++ mbox_irqaction.dev_id = mailbox; ++ setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction); ++ printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n", ++ __io_address(ARM_0_MAIL0_RD)); ++ } ++ } ++ ++ if (ret == 0) { ++ /* ++ * Register the character device ++ */ ++ ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops); ++ ++ /* ++ * Negative values signify an error ++ */ ++ if (ret < 0) { ++ printk(KERN_ERR DRIVER_NAME ++ "Failed registering the character device %d\n", ret); ++ return ret; ++ } ++ } ++ return ret; ++} ++ ++static int bcm_vcio_remove(struct platform_device *pdev) ++{ ++ struct vc_mailbox *mailbox = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ kfree(mailbox); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_mbox_driver = { ++ .probe = bcm_vcio_probe, ++ .remove = bcm_vcio_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init bcm_mbox_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n"); ++ ++ ret = platform_driver_register(&bcm_mbox_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_mbox_exit(void) ++{ ++ platform_driver_unregister(&bcm_mbox_driver); ++} ++ ++arch_initcall(bcm_mbox_init); /* Initialize early */ ++module_exit(bcm_mbox_exit); ++ ++MODULE_AUTHOR("Gray Girling"); ++MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm-mbox"); +diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S +index 8b4ee5e..80fa74b 100644 +--- a/arch/arm/mm/proc-v7.S ++++ b/arch/arm/mm/proc-v7.S +@@ -441,6 +441,7 @@ __v7_setup: + orr r0, r0, r6 @ set them + THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions + ret lr @ return to head.S:__ret ++ .space 256 + ENDPROC(__v7_setup) + + .align 2 +diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types +index c9ddd87..bfc397c 100644 +--- a/arch/arm/tools/mach-types ++++ b/arch/arm/tools/mach-types +@@ -523,6 +523,7 @@ prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103 + paz00 MACH_PAZ00 PAZ00 3128 + acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129 + bcm2708 MACH_BCM2708 BCM2708 3138 ++bcm2709 MACH_BCM2709 BCM2709 3139 + ag5evm MACH_AG5EVM AG5EVM 3189 + ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 + wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 +diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c +index a3025e7..4a2a601 100644 +--- a/drivers/clocksource/arm_arch_timer.c ++++ b/drivers/clocksource/arm_arch_timer.c +@@ -804,3 +804,39 @@ static void __init arch_timer_mem_init(struct device_node *np) + } + CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", + arch_timer_mem_init); ++ ++int __init dc4_arch_timer_init(void) ++{ ++ if (arch_timers_present & ARCH_CP15_TIMER) { ++ pr_warn("arch_timer: multiple nodes in dt, skipping\n"); ++ return -1; ++ } ++ ++ arch_timers_present |= ARCH_CP15_TIMER; ++ ++ /* Try to determine the frequency from the device tree or CNTFRQ */ ++ arch_timer_rate = 19200000; ++ ++ arch_timer_ppi[PHYS_SECURE_PPI] = IRQ_ARM_LOCAL_CNTPSIRQ; ++ arch_timer_ppi[PHYS_NONSECURE_PPI] = IRQ_ARM_LOCAL_CNTPNSIRQ; ++ arch_timer_ppi[VIRT_PPI] = IRQ_ARM_LOCAL_CNTVIRQ; ++ arch_timer_ppi[HYP_PPI] = IRQ_ARM_LOCAL_CNTHPIRQ; ++ ++ /* ++ * If HYP mode is available, we know that the physical timer ++ * has been configured to be accessible from PL1. Use it, so ++ * that a guest can use the virtual timer instead. ++ * ++ * If no interrupt provided for virtual timer, we'll have to ++ * stick to the physical timer. It'd better be accessible... ++ */ ++ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) { ++ arch_timer_use_virtual = false; ++ } ++ ++ arch_timer_c3stop = 0; ++ ++ arch_timer_register(); ++ arch_timer_common_init(); ++ return 0; ++} + +From 711ff7723bd270aff011ad323f9249486512bf6d Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 8 Oct 2014 18:50:05 +0100 -Subject: [PATCH 002/172] Add bcm2708_gpio driver +Subject: [PATCH 003/104] Add bcm2708_gpio driver Signed-off-by: popcornmix @@ -5715,226 +12587,10 @@ index 0000000..fb69624 + +#endif -From 559f0c63be158be62310100cbe3e45d224be9592 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 7 May 2013 22:20:24 +0100 -Subject: [PATCH 003/172] Add quick config. - -This is designed for quick compiling when developing. -No modules are needed and it includes all Pi specific drivers ---- - arch/arm/configs/bcmrpi_quick_defconfig | 197 ++++++++++++++++++++++++++++++++ - 1 file changed, 197 insertions(+) - create mode 100644 arch/arm/configs/bcmrpi_quick_defconfig - -diff --git a/arch/arm/configs/bcmrpi_quick_defconfig b/arch/arm/configs/bcmrpi_quick_defconfig -new file mode 100644 -index 0000000..e5efe75 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_quick_defconfig -@@ -0,0 +1,197 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_USB_USBNET=y -+# CONFIG_USB_NET_AX8817X is not set -+# CONFIG_USB_NET_CDCETHER is not set -+# CONFIG_USB_NET_CDC_NCM is not set -+CONFIG_USB_NET_SMSC95XX=y -+# CONFIG_USB_NET_NET1080 is not set -+# CONFIG_USB_NET_CDC_SUBSET is not set -+# CONFIG_USB_NET_ZAURUS is not set -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=y -+CONFIG_RAW_DRIVER=y -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_SND_BCM2835=y -+# CONFIG_SND_USB is not set -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_DWCOTG=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+CONFIG_DETECT_HUNG_TASK=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y - -From 08e12db8a1dcba12eeabca2b8eca4b4efa30f439 Mon Sep 17 00:00:00 2001 +From 950ea05f0c4da1ae960869be689d21dcef50365b Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:46:17 +0100 -Subject: [PATCH 004/172] Add dwc_otg driver +Subject: [PATCH 004/104] Add dwc_otg driver Signed-off-by: popcornmix @@ -5945,7 +12601,416 @@ Signed-off-by: Kari Suvanto usb: dwc: fix inconsistent lock state Signed-off-by: Kari Suvanto + +Add FIQ patch to dwc_otg driver. Enable with dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. +Thanks to Gordon and Costas + +Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005. + +Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh + +Make sure we wait for the reset to finish + +dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel + memory corruption, escalating to OOPS under high USB load. + +dwc_otg: Fix unsafe access of QTD during URB enqueue + +In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the +transaction could complete almost immediately after the qtd was assigned +to a host channel during URB enqueue, which meant the qtd pointer was no +longer valid having been completed and removed. Usually, this resulted in +an OOPS during URB submission. By predetermining whether transactions +need to be queued or not, this unsafe pointer access is avoided. + +This bug was only evident on the Pi model A where a device was attached +that had no periodic endpoints (e.g. USB pendrive or some wlan devices). + +dwc_otg: Fix incorrect URB allocation error handling + +If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS +because for some reason a member of the *unallocated* struct was set to +zero. Error handling changed to fail correctly. + +dwc_otg: fix potential use-after-free case in interrupt handler + +If a transaction had previously aborted, certain interrupts are +enabled to track error counts and reset where necessary. On IN +endpoints the host generates an ACK interrupt near-simultaneously +with completion of transfer. In the case where this transfer had +previously had an error, this results in a use-after-free on +the QTD memory space with a 1-byte length being overwritten to +0x00. + +dwc_otg: add handling of SPLIT transaction data toggle errors + +Previously a data toggle error on packets from a USB1.1 device behind +a TT would result in the Pi locking up as the driver never handled +the associated interrupt. Patch adds basic retry mechanism and +interrupt acknowledgement to cater for either a chance toggle error or +for devices that have a broken initial toggle state (FT8U232/FT232BM). + +dwc_otg: implement tasklet for returning URBs to usbcore hcd layer + +The dwc_otg driver interrupt handler for transfer completion will spend +a very long time with interrupts disabled when a URB is completed - +this is because usb_hcd_giveback_urb is called from within the handler +which for a USB device driver with complicated processing (e.g. webcam) +will take an exorbitant amount of time to complete. This results in +missed completion interrupts for other USB packets which lead to them +being dropped due to microframe overruns. + +This patch splits returning the URB to the usb hcd layer into a +high-priority tasklet. This will have most benefit for isochronous IN +transfers but will also have incidental benefit where multiple periodic +devices are active at once. + +dwc_otg: fix NAK holdoff and allow on split transactions only + +This corrects a bug where if a single active non-periodic endpoint +had at least one transaction in its qh, on frnum == MAX_FRNUM the qh +would get skipped and never get queued again. This would result in +a silent device until error detection (automatic or otherwise) would +either reset the device or flush and requeue the URBs. + +Additionally the NAK holdoff was enabled for all transactions - this +would potentially stall a HS endpoint for 1ms if a previous error state +enabled this interrupt and the next response was a NAK. Fix so that +only split transactions get held off. + +dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler + +usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it +asynchronously in the tasklet was not safe (regression in +c4564d4a1a0a9b10d4419e48239f5d99e88d2667). + +This change unlinks it from the endpoint prior to queueing it for handling in +the tasklet, and also adds a check to ensure the urb is OK to be unlinked +before doing so. + +NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb +when a USB device was unplugged/replugged during data transfer. This effect +was reproduced using automated USB port power control, hundreds of replug +events were performed during active transfers to confirm that the problem was +eliminated. + +USB fix using a FIQ to implement split transactions + +This commit adds a FIQ implementaion that schedules +the split transactions using a FIQ so we don't get +held off by the interrupt latency of Linux + +dwc_otg: fix device attributes and avoid kernel warnings on boot + +dcw_otg: avoid logging function that can cause panics + +See: https://github.com/raspberrypi/firmware/issues/21 +Thanks to cleverca22 for fix + +dwc_otg: mask correct interrupts after transaction error recovery + +The dwc_otg driver will unmask certain interrupts on a transaction +that previously halted in the error state in order to reset the +QTD error count. The various fine-grained interrupt handlers do not +consider that other interrupts besides themselves were unmasked. + +By disabling the two other interrupts only ever enabled in DMA mode +for this purpose, we can avoid unnecessary function calls in the +IRQ handler. This will also prevent an unneccesary FIQ interrupt +from being generated if the FIQ is enabled. + +dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ + +In the case of a transaction to a device that had previously aborted +due to an error, several interrupts are enabled to reset the error +count when a device responds. This has the side-effect of making the +FIQ thrash because the hardware will generate multiple instances of +a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK +on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the +associated interrupts. + +Additionally, on non-split transactions make sure that only unmasked +interrupts are cleared. This caused a hard-to-trigger but serious +race condition when you had the combination of an endpoint awaiting +error recovery and a transaction completed on an endpoint - due to +the sequencing and timing of interrupts generated by the dwc_otg core, +it was possible to confuse the IRQ handler. + +Fix function tracing + +dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue + +dwc_otg: prevent OOPSes during device disconnects + +The dwc_otg_urb_enqueue function is thread-unsafe. In particular the +access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and +friends does not occur within a critical section and so if a device +was unplugged during activity there was a high chance that the +usbcore hub_thread would try to disable the endpoint with partially- +formed entries in the URB queue. This would result in BUG() or null +pointer dereferences. + +Fix so that access of urb->hcpriv, enqueuing to the hardware and +adding to usbcore endpoint URB lists is contained within a single +critical section. + +dwc_otg: prevent BUG() in TT allocation if hub address is > 16 + +A fixed-size array is used to track TT allocation. This was +previously set to 16 which caused a crash because +dwc_otg_hcd_allocate_port would read past the end of the array. + +This was hit if a hub was plugged in which enumerated as addr > 16, +due to previous device resets or unplugs. + +Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows +to a large size if 128 hub addresses are supported. This field is +for debug only for tracking which frame an allocate happened in. + +dwc_otg: make channel halts with unknown state less damaging + +If the IRQ received a channel halt interrupt through the FIQ +with no other bits set, the IRQ would not release the host +channel and never complete the URB. + +Add catchall handling to treat as a transaction error and retry. + +dwc_otg: fiq_split: use TTs with more granularity + +This fixes certain issues with split transaction scheduling. + +- Isochronous multi-packet OUT transactions now hog the TT until + they are completed - this prevents hubs aborting transactions + if they get a periodic start-split out-of-order +- Don't perform TT allocation on non-periodic endpoints - this + allows simultaneous use of the TT's bulk/control and periodic + transaction buffers + +This commit will mainly affect USB audio playback. + +dwc_otg: fix potential sleep while atomic during urb enqueue + +Fixes a regression introduced with eb1b482a. Kmalloc called from +dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have +the GPF_ATOMIC flag set. Force this flag when inside the larger +critical section. + +dwc_otg: make fiq_split_enable imply fiq_fix_enable + +Failing to set up the FIQ correctly would result in +"IRQ 32: nobody cared" errors in dmesg. + +dwc_otg: prevent crashes on host port disconnects + +Fix several issues resulting in crashes or inconsistent state +if a Model A root port was disconnected. + +- Clean up queue heads properly in kill_urbs_in_qh_list by + removing the empty QHs from the schedule lists +- Set the halt status properly to prevent IRQ handlers from + using freed memory +- Add fiq_split related cleanup for saved registers +- Make microframe scheduling reclaim host channels if + active during a disconnect +- Abort URBs with -ESHUTDOWN status response, informing + device drivers so they respond in a more correct fashion + and don't try to resubmit URBs +- Prevent IRQ handlers from attempting to handle channel + interrupts if the associated URB was dequeued (and the + driver state was cleared) + +dwc_otg: prevent leaking URBs during enqueue + +A dwc_otg_urb would get leaked if the HCD enqueue function +failed for any reason. Free the URB at the appropriate points. + +dwc_otg: Enable NAK holdoff for control split transactions + +Certain low-speed devices take a very long time to complete a +data or status stage of a control transaction, producing NAK +responses until they complete internal processing - the USB2.0 +spec limit is up to 500mS. This causes the same type of interrupt +storm as seen with USB-serial dongles prior to c8edb238. + +In certain circumstances, usually while booting, this interrupt +storm could cause SD card timeouts. + +dwc_otg: Fix for occasional lockup on boot when doing a USB reset + +dwc_otg: Don't issue traffic to LS devices in FS mode + +Issuing low-speed packets when the root port is in full-speed mode +causes the root port to stop responding. Explicitly fail when +enqueuing URBs to a LS endpoint on a FS bus. + +Fix ARM architecture issue with local_irq_restore() + +If local_fiq_enable() is called before a local_irq_restore(flags) where +the flags variable has the F bit set, the FIQ will be erroneously disabled. + +Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR. + +Also fix some of the hacks previously implemented for previous dwc_otg +incarnations. + +dwc_otg: fiq_fsm: Base commit for driver rewrite + +This commit removes the previous FIQ fixes entirely and adds fiq_fsm. + +This rewrite features much more complete support for split transactions +and takes into account several OTG hardware bugs. High-speed +isochronous transactions are also capable of being performed by fiq_fsm. + +All driver options have been removed and replaced with: + - dwc_otg.fiq_enable (bool) + - dwc_otg.fiq_fsm_enable (bool) + - dwc_otg.fiq_fsm_mask (bitmask) + - dwc_otg.nak_holdoff (unsigned int) + +Defaults are specified such that fiq_fsm behaves similarly to the +previously implemented FIQ fixes. + +fiq_fsm: Push error recovery into the FIQ when fiq_fsm is used + +If the transfer associated with a QTD failed due to a bus error, the HCD +would retry the transfer up to 3 times (implementing the USB2.0 +three-strikes retry in software). + +Due to the masking mechanism used by fiq_fsm, it is only possible to pass +a single interrupt through to the HCD per-transfer. + +In this instance host channels would fall off the radar because the error +reset would function, but the subsequent channel halt would be lost. + +Push the error count reset into the FIQ handler. + +fiq_fsm: Implement timeout mechanism + +For full-speed endpoints with a large packet size, interrupt latency +runs the risk of the FIQ starting a transaction too late in a full-speed +frame. If the device is still transmitting data when EOF2 for the +downstream frame occurs, the hub will disable the port. This change is +not reflected in the hub status endpoint and the device becomes +unresponsive. + +Prevent high-bandwidth transactions from being started too late in a +frame. The mechanism is not guaranteed: a combination of bit stuffing +and hub latency may still result in a device overrunning. + +fiq_fsm: fix bounce buffer utilisation for Isochronous OUT + +Multi-packet isochronous OUT transactions were subject to a few bounday +bugs. Fix them. + +Audio playback is now much more robust: however, an issue stands with +devices that have adaptive sinks - ALSA plays samples too fast. + +dwc_otg: Return full-speed frame numbers in HS mode + +The frame counter increments on every *microframe* in high-speed mode. +Most device drivers expect this number to be in full-speed frames - this +caused considerable confusion to e.g. snd_usb_audio which uses the +frame counter to estimate the number of samples played. + +fiq_fsm: save PID on completion of interrupt OUT transfers + +Also add edge case handling for interrupt transports. + +Note that for periodic split IN, data toggles are unimplemented in the +OTG host hardware - it unconditionally accepts any PID. + +fiq_fsm: add missing case for fiq_fsm_tt_in_use() + +Certain combinations of bitrate and endpoint activity could +result in a periodic transaction erroneously getting started +while the previous Isochronous OUT was still active. + +fiq_fsm: clear hcintmsk for aborted transactions + +Prevents the FIQ from erroneously handling interrupts +on a timed out channel. + +fiq_fsm: enable by default + +fiq_fsm: fix dequeues for non-periodic split transactions + +If a dequeue happened between the SSPLIT and CSPLIT phases of the +transaction, the HCD would never receive an interrupt. + +fiq_fsm: Disable by default + +fiq_fsm: Handle HC babble errors + +The HCTSIZ transfer size field raises a babble interrupt if +the counter wraps. Handle the resulting interrupt in this case. + +dwc_otg: fix interrupt registration for fiq_enable=0 + +Additionally make the module parameter conditional for wherever +hcd->fiq_state is touched. + +fiq_fsm: Enable by default + +dwc_otg: Fix various issues with root port and transaction errors + +Process the host port interrupts correctly (and don't trample them). +Root port hotplug now functional again. + +Fix a few thinkos with the transaction error passthrough for fiq_fsm. + +fiq_fsm: Implement hack for Split Interrupt transactions + +Hubs aren't too picky about which endpoint we send Control type split +transactions to. By treating Interrupt transfers as Control, it is +possible to use the non-periodic queue in the OTG core as well as the +non-periodic FIFOs in the hub itself. This massively reduces the +microframe exclusivity/contention that periodic split transactions +otherwise have to enforce. + +It goes without saying that this is a fairly egregious USB specification +violation, but it works. + +Original idea by Hans Petter Selasky @ FreeBSD.org. + +dwc_otg: FIQ support on SMP. Set up FIQ stack and handler on Core 0 only. + +dwc_otg: introduce fiq_fsm_spin(un|)lock() + +SMP safety for the FIQ relies on register read-modify write cycles being +completed in the correct order. Several places in the DWC code modify +registers also touched by the FIQ. Protect these by a bare-bones lock +mechanism. + +This also makes it possible to run the FIQ and IRQ handlers on different +cores. + +fiq_fsm: fix build on bcm2708 and bcm2709 platforms + +dwc_otg: put some barriers back where they should be for UP + +bcm2709/dwc_otg: Setup FIQ on core 1 if >1 core active + +dwc_otg: fixup read-modify-write in critical paths + +Be more careful about read-modify-write on registers that the FIQ +also touches. + +Guard fiq_fsm_spin_lock with fiq_enable check + +fiq_fsm: Falling out of the state machine isn't fatal + +This edge case can be hit if the port is disabled while the FIQ is +in the middle of a transaction. Make the effects less severe. + +Also get rid of the useless return value. --- + arch/arm/Kconfig | 1 + + arch/arm/include/asm/irqflags.h | 16 +- + arch/arm/kernel/fiqasm.S | 4 + + arch/arm/mach-bcm2708/armctrl.c | 19 +- + arch/arm/mach-bcm2708/bcm2708.c | 15 +- + arch/arm/mach-bcm2708/include/mach/irqs.h | 153 +- + arch/arm/mach-bcm2709/armctrl.c | 10 +- drivers/usb/Makefile | 1 + drivers/usb/core/generic.c | 1 + drivers/usb/core/message.c | 79 + @@ -5961,7 +13026,7 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_common_port/dwc_cc.c | 532 ++ drivers/usb/host/dwc_common_port/dwc_cc.h | 224 + drivers/usb/host/dwc_common_port/dwc_common_fbsd.c | 1308 ++++ - .../usb/host/dwc_common_port/dwc_common_linux.c | 1429 ++++ + .../usb/host/dwc_common_port/dwc_common_linux.c | 1434 ++++ drivers/usb/host/dwc_common_port/dwc_common_nbsd.c | 1275 ++++ drivers/usb/host/dwc_common_port/dwc_crypto.c | 308 + drivers/usb/host/dwc_common_port/dwc_crypto.h | 111 + @@ -5973,9 +13038,9 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_common_port/dwc_modpow.h | 34 + drivers/usb/host/dwc_common_port/dwc_notifier.c | 319 + drivers/usb/host/dwc_common_port/dwc_notifier.h | 122 + - drivers/usb/host/dwc_common_port/dwc_os.h | 1274 ++++ + drivers/usb/host/dwc_common_port/dwc_os.h | 1276 ++++ drivers/usb/host/dwc_common_port/usb.h | 946 +++ - drivers/usb/host/dwc_otg/Makefile | 80 + + drivers/usb/host/dwc_otg/Makefile | 82 + drivers/usb/host/dwc_otg/doc/doxygen.cfg | 224 + drivers/usb/host/dwc_otg/dummy_audio.c | 1575 +++++ drivers/usb/host/dwc_otg/dwc_cfi_common.h | 142 + @@ -5985,32 +13050,35 @@ Signed-off-by: Kari Suvanto drivers/usb/host/dwc_otg/dwc_otg_attr.h | 89 + drivers/usb/host/dwc_otg/dwc_otg_cfi.c | 1876 +++++ drivers/usb/host/dwc_otg/dwc_otg_cfi.h | 320 + - drivers/usb/host/dwc_otg/dwc_otg_cil.c | 7151 ++++++++++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_cil.c | 7141 ++++++++++++++++++++ drivers/usb/host/dwc_otg/dwc_otg_cil.h | 1464 ++++ - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 1563 +++++ + drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 1594 +++++ drivers/usb/host/dwc_otg/dwc_otg_core_if.h | 705 ++ - drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 116 + - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1700 +++++ + drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 117 + + drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1749 +++++ drivers/usb/host/dwc_otg/dwc_otg_driver.h | 86 + - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 3479 ++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 824 +++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1133 ++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 412 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2106 ++++++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 893 +++ - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 923 +++ - drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 185 + + drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1346 ++++ + drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 367 + + drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 + + drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 4252 ++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 862 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1132 ++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 417 ++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2713 ++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 994 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 957 +++ + drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 188 + drivers/usb/host/dwc_otg/dwc_otg_pcd.c | 2712 ++++++++ drivers/usb/host/dwc_otg/dwc_otg_pcd.h | 266 + drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h | 360 + drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 5147 ++++++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1358 ++++ + drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1360 ++++ drivers/usb/host/dwc_otg/dwc_otg_regs.h | 2550 +++++++ drivers/usb/host/dwc_otg/test/Makefile | 16 + drivers/usb/host/dwc_otg/test/dwc_otg_test.pm | 337 + drivers/usb/host/dwc_otg/test/test_mod_param.pl | 133 + drivers/usb/host/dwc_otg/test/test_sysfs.pl | 193 + - 64 files changed, 56458 insertions(+), 12 deletions(-) + 74 files changed, 60026 insertions(+), 97 deletions(-) create mode 100644 drivers/usb/gadget/file_storage.c create mode 100644 drivers/usb/host/dwc_common_port/Makefile create mode 100644 drivers/usb/host/dwc_common_port/Makefile.fbsd @@ -6051,6 +13119,9 @@ Signed-off-by: Kari Suvanto create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_dbg.h create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.c create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.c create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.h create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c @@ -6070,18 +13141,336 @@ Signed-off-by: Kari Suvanto create mode 100644 drivers/usb/host/dwc_otg/test/test_mod_param.pl create mode 100644 drivers/usb/host/dwc_otg/test/test_sysfs.pl +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 4018fea..ee1bb5e 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -382,6 +382,7 @@ config ARCH_BCM2708 + select ARM_ERRATA_411920 + select MACH_BCM2708 + select VC4 ++ select FIQ + help + This enables support for Broadcom BCM2708 boards. + +diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h +index 3b763d6..5770408 100644 +--- a/arch/arm/include/asm/irqflags.h ++++ b/arch/arm/include/asm/irqflags.h +@@ -145,12 +145,22 @@ static inline unsigned long arch_local_save_flags(void) + } + + /* +- * restore saved IRQ & FIQ state ++ * restore saved IRQ state + */ + static inline void arch_local_irq_restore(unsigned long flags) + { +- asm volatile( +- " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore" ++ unsigned long temp = 0; ++ flags &= ~(1 << 6); ++ asm volatile ( ++ " mrs %0, cpsr" ++ : "=r" (temp) ++ : ++ : "memory", "cc"); ++ /* Preserve FIQ bit */ ++ temp &= (1 << 6); ++ flags = flags | temp; ++ asm volatile ( ++ " msr cpsr_c, %0 @ local_irq_restore" + : + : "r" (flags) + : "memory", "cc"); +diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S +index 8dd26e1..eef4847 100644 +--- a/arch/arm/kernel/fiqasm.S ++++ b/arch/arm/kernel/fiqasm.S +@@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs) + mov r0, r0 @ avoid hazard prior to ARMv4 + ret lr + ENDPROC(__get_fiq_regs) ++ ++ENTRY(__FIQ_Branch) ++ mov pc, r8 ++ENDPROC(__FIQ_Branch) +diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c +index ef1c8d5..96fa9b9 100644 +--- a/arch/arm/mach-bcm2708/armctrl.c ++++ b/arch/arm/mach-bcm2708/armctrl.c +@@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_data *d) + 0 + }; + +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); +- writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ if (d->irq >= FIQ_START) { ++ writel(0, __io_address(ARM_IRQ_FAST)); ++ } else { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); ++ } + } + + static void armctrl_unmask_irq(struct irq_data *d) +@@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct irq_data *d) + 0 + }; + +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); +- writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ if (d->irq >= FIQ_START) { ++ unsigned int data = ++ (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); ++ } else { ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); ++ } + } + + #if defined(CONFIG_PM) +@@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, + } + + armctrl_pm_register(base, irq_start, resume_sources); ++ init_FIQ(FIQ_START); + return 0; + } +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 7503649..d9db081 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -291,12 +291,23 @@ static struct resource bcm2708_usb_resources[] = { + .flags = IORESOURCE_MEM, + }, + [1] = { +- .start = IRQ_USB, +- .end = IRQ_USB, ++ .start = MPHI_BASE, ++ .end = MPHI_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [2] = { ++ .start = IRQ_HOSTPORT, ++ .end = IRQ_HOSTPORT, + .flags = IORESOURCE_IRQ, + }, ++ [3] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, + }; + ++ + static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_usb_device = { +diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h +index 3a88a1a..45152ed 100644 +--- a/arch/arm/mach-bcm2708/include/mach/irqs.h ++++ b/arch/arm/mach-bcm2708/include/mach/irqs.h +@@ -106,87 +106,90 @@ + #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) + #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) + ++#define FIQ_START HARD_IRQS ++ + /* + * FIQ interrupts definitions are the same as the INT definitions. + */ +-#define FIQ_TIMER0 INT_TIMER0 +-#define FIQ_TIMER1 INT_TIMER1 +-#define FIQ_TIMER2 INT_TIMER2 +-#define FIQ_TIMER3 INT_TIMER3 +-#define FIQ_CODEC0 INT_CODEC0 +-#define FIQ_CODEC1 INT_CODEC1 +-#define FIQ_CODEC2 INT_CODEC2 +-#define FIQ_JPEG INT_JPEG +-#define FIQ_ISP INT_ISP +-#define FIQ_USB INT_USB +-#define FIQ_3D INT_3D +-#define FIQ_TRANSPOSER INT_TRANSPOSER +-#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 +-#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 +-#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 +-#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 +-#define FIQ_DMA0 INT_DMA0 +-#define FIQ_DMA1 INT_DMA1 +-#define FIQ_DMA2 INT_DMA2 +-#define FIQ_DMA3 INT_DMA3 +-#define FIQ_DMA4 INT_DMA4 +-#define FIQ_DMA5 INT_DMA5 +-#define FIQ_DMA6 INT_DMA6 +-#define FIQ_DMA7 INT_DMA7 +-#define FIQ_DMA8 INT_DMA8 +-#define FIQ_DMA9 INT_DMA9 +-#define FIQ_DMA10 INT_DMA10 +-#define FIQ_DMA11 INT_DMA11 +-#define FIQ_DMA12 INT_DMA12 +-#define FIQ_AUX INT_AUX +-#define FIQ_ARM INT_ARM +-#define FIQ_VPUDMA INT_VPUDMA +-#define FIQ_HOSTPORT INT_HOSTPORT +-#define FIQ_VIDEOSCALER INT_VIDEOSCALER +-#define FIQ_CCP2TX INT_CCP2TX +-#define FIQ_SDC INT_SDC +-#define FIQ_DSI0 INT_DSI0 +-#define FIQ_AVE INT_AVE +-#define FIQ_CAM0 INT_CAM0 +-#define FIQ_CAM1 INT_CAM1 +-#define FIQ_HDMI0 INT_HDMI0 +-#define FIQ_HDMI1 INT_HDMI1 +-#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 +-#define FIQ_I2CSPISLV INT_I2CSPISLV +-#define FIQ_DSI1 INT_DSI1 +-#define FIQ_PWA0 INT_PWA0 +-#define FIQ_PWA1 INT_PWA1 +-#define FIQ_CPR INT_CPR +-#define FIQ_SMI INT_SMI +-#define FIQ_GPIO0 INT_GPIO0 +-#define FIQ_GPIO1 INT_GPIO1 +-#define FIQ_GPIO2 INT_GPIO2 +-#define FIQ_GPIO3 INT_GPIO3 +-#define FIQ_I2C INT_I2C +-#define FIQ_SPI INT_SPI +-#define FIQ_I2SPCM INT_I2SPCM +-#define FIQ_SDIO INT_SDIO +-#define FIQ_UART INT_UART +-#define FIQ_SLIMBUS INT_SLIMBUS +-#define FIQ_VEC INT_VEC +-#define FIQ_CPG INT_CPG +-#define FIQ_RNG INT_RNG +-#define FIQ_ARASANSDIO INT_ARASANSDIO +-#define FIQ_AVSPMON INT_AVSPMON ++#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) ++#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) ++#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) ++#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) ++#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) ++#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) ++#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) ++#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) ++#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) ++#define FIQ_USB (FIQ_START+INTERRUPT_USB) ++#define FIQ_3D (FIQ_START+INTERRUPT_3D) ++#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) ++#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) ++#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) ++#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) ++#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) ++#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) ++#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) ++#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) ++#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) ++#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) ++#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) ++#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) ++#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) ++#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) ++#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) ++#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) ++#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) ++#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) ++#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) ++#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) ++#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) ++#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) ++#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) ++#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) ++#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) ++#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) ++#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) ++#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) ++#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) ++#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) ++#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) ++#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) ++#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) ++#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) ++#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) ++#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) ++#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) ++#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) ++#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) ++#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) ++#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) ++#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) ++#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) ++#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) ++#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) ++#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) ++#define FIQ_UART (FIQ_START+INTERRUPT_UART) ++#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) ++#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) ++#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) ++#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) ++#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) ++#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) + +-#define FIQ_ARM_TIMER INT_ARM_TIMER +-#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX +-#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 +-#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 +-#define FIQ_VPU0_HALTED INT_VPU0_HALTED +-#define FIQ_VPU1_HALTED INT_VPU1_HALTED +-#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 +-#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 +-#define FIQ_PENDING1 INT_PENDING1 +-#define FIQ_PENDING2 INT_PENDING2 ++#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) ++#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) ++#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) ++#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) ++#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) ++#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) ++#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) ++#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) ++#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) ++#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) + + #define HARD_IRQS (64 + 21) +-#define GPIO_IRQ_START (HARD_IRQS) ++#define FIQ_IRQS (64 + 21) ++#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) + #define GPIO_IRQS (32*5) + #define SPARE_ALLOC_IRQS 64 + #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) +diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c +index 2815b64..fc6cb8b 100644 +--- a/arch/arm/mach-bcm2709/armctrl.c ++++ b/arch/arm/mach-bcm2709/armctrl.c +@@ -91,7 +91,15 @@ static void armctrl_unmask_irq(struct irq_data *d) + }; + int i; + if (d->irq >= FIQ_START) { +- unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; ++ unsigned int data; ++ if (num_online_cpus() > 1) { ++ data = readl(__io_address(ARM_LOCAL_GPU_INT_ROUTING)); ++ data &= ~0xc; ++ data |= (1 << 2); ++ writel(data, __io_address(ARM_LOCAL_GPU_INT_ROUTING)); ++ } ++ /* Unmask in ARMCTRL block after routing it properly */ ++ data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; + writel(0x80 | data, __io_address(ARM_IRQ_FAST)); + } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { + #if 1 diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile -index d7be717..f48b630 100644 +index 2f1e2aa..887ca56 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile -@@ -24,6 +24,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/ - obj-$(CONFIG_USB_R8A66597_HCD) += host/ - obj-$(CONFIG_USB_HWA_HCD) += host/ - obj-$(CONFIG_USB_ISP1760_HCD) += host/ +@@ -6,6 +6,7 @@ + + obj-$(CONFIG_USB) += core/ + +obj-$(CONFIG_USB_DWCOTG) += host/ - obj-$(CONFIG_USB_IMX21_HCD) += host/ - obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ - obj-$(CONFIG_USB_FUSBH200_HCD) += host/ + obj-$(CONFIG_USB_DWC3) += dwc3/ + obj-$(CONFIG_USB_DWC2) += dwc2/ + obj-$(CONFIG_USB_ISP1760) += isp1760/ diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 358ca8d..abaac7c 100644 --- a/drivers/usb/core/generic.c @@ -10024,10 +17413,10 @@ index 0000000..a896d73 +} +module_exit(fsg_cleanup); diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig -index fafc628..83c34af 100644 +index 5ad60e4..fb5b986 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig -@@ -752,6 +752,19 @@ config USB_HWA_HCD +@@ -738,6 +738,19 @@ config USB_HWA_HCD To compile this driver a module, choose M here: the module will be called "hwa-hc". @@ -10048,12 +17437,12 @@ index fafc628..83c34af 100644 tristate "i.MX21 HCD support" depends on ARM && ARCH_MXC diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile -index d6216a4..c6dfede 100644 +index 65b0b6a..49399f2 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile -@@ -71,6 +71,8 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o +@@ -68,6 +68,8 @@ obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o + obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o - obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o + +obj-$(CONFIG_USB_DWCOTG) += dwc_otg/ dwc_common_port/ @@ -12742,10 +20131,10 @@ index 0000000..6dd04b5 +} diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c new file mode 100644 -index 0000000..1668f10 +index 0000000..b802042 --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -0,0 +1,1429 @@ +@@ -0,0 +1,1434 @@ +#include +#include +#include @@ -13743,6 +21132,11 @@ index 0000000..1668f10 + tasklet_schedule(&task->t); +} + ++void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task) ++{ ++ tasklet_hi_schedule(&task->t); ++} ++ + +/* workqueues + - run in process context (can sleep) @@ -16298,7 +23692,7 @@ index 0000000..25c1cc0 +#endif /* _DWC_DH_H_ */ diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h new file mode 100644 -index 0000000..89cc325 +index 0000000..4ce560d --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_list.h @@ -0,0 +1,594 @@ @@ -16688,17 +24082,17 @@ index 0000000..89cc325 +#define DWC_TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define DWC_TAILQ_EMPTY(head) \ -+ (TAILQ_FIRST(head) == TAILQ_END(head)) ++ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head)) + +#define DWC_TAILQ_FOREACH(var, head, field) \ -+ for((var) = TAILQ_FIRST(head); \ -+ (var) != TAILQ_END(head); \ -+ (var) = TAILQ_NEXT(var, field)) ++ for ((var) = DWC_TAILQ_FIRST(head); \ ++ (var) != DWC_TAILQ_END(head); \ ++ (var) = DWC_TAILQ_NEXT(var, field)) + +#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ -+ for((var) = TAILQ_LAST(head, headname); \ -+ (var) != TAILQ_END(head); \ -+ (var) = TAILQ_PREV(var, headname, field)) ++ for ((var) = DWC_TAILQ_LAST(head, headname); \ ++ (var) != DWC_TAILQ_END(head); \ ++ (var) = DWC_TAILQ_PREV(var, headname, field)) + +/* + * Tail queue functions. @@ -18284,10 +25678,10 @@ index 0000000..4a8cdfe +#endif /* __DWC_NOTIFIER_H__ */ diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h new file mode 100644 -index 0000000..8117731 +index 0000000..a2bbe23 --- /dev/null +++ b/drivers/usb/host/dwc_common_port/dwc_os.h -@@ -0,0 +1,1274 @@ +@@ -0,0 +1,1276 @@ +/* ========================================================================= + * $File: //dwh/usb_iip/dev/software/dwc_common_port_2/dwc_os.h $ + * $Revision: #14 $ @@ -19272,6 +26666,8 @@ index 0000000..8117731 +extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); +#define dwc_task_schedule DWC_TASK_SCHEDULE + ++extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task); ++#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE + +/** @name Timer + * @@ -20516,10 +27912,10 @@ index 0000000..27bda82 +#endif /* _USB_H_ */ diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile new file mode 100644 -index 0000000..236c47c +index 0000000..e7bdd12 --- /dev/null +++ b/drivers/usb/host/dwc_otg/Makefile -@@ -0,0 +1,80 @@ +@@ -0,0 +1,82 @@ +# +# Makefile for DWC_otg Highspeed USB controller driver +# @@ -20558,6 +27954,8 @@ index 0000000..236c47c +dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o +dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o +dwc_otg-objs += dwc_otg_adp.o ++dwc_otg-objs += dwc_otg_fiq_fsm.o ++dwc_otg-objs += dwc_otg_fiq_stub.o +ifneq ($(CFI),) +dwc_otg-objs += dwc_otg_cfi.o +endif @@ -23507,7 +30905,7 @@ index 0000000..4110b25 +#endif //__DWC_OTG_ADP_H__ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_attr.c b/drivers/usb/host/dwc_otg/dwc_otg_attr.c new file mode 100644 -index 0000000..fab2961 +index 0000000..9da0c92 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c @@ -0,0 +1,1210 @@ @@ -24422,7 +31820,7 @@ index 0000000..fab2961 + return sprintf(buf, "Register Dump\n"); +} + -+DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); ++DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0); + +/** + * Dump global registers and either host or device registers (depending on the @@ -24433,12 +31831,12 @@ index 0000000..fab2961 +{ + dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev); + -+ dwc_otg_dump_spram(otg_dev->core_if); ++ //dwc_otg_dump_spram(otg_dev->core_if); + + return sprintf(buf, "SPRAM Dump\n"); +} + -+DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); ++DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0); + +/** + * Dump the current hcd state. @@ -24453,7 +31851,7 @@ index 0000000..fab2961 + return sprintf(buf, "HCD Dump\n"); +} + -+DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); ++DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0); + +/** + * Dump the average frame remaining at SOF. This can be used to @@ -24471,7 +31869,7 @@ index 0000000..fab2961 + return sprintf(buf, "HCD Dump Frame Remaining\n"); +} + -+DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); ++DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0); + +/** + * Displays the time required to read the GNPTXFSIZ register many times (the @@ -24499,7 +31897,7 @@ index 0000000..fab2961 + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); +} + -+DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); ++DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0); + +/** + * Displays the time required to write the GNPTXFSIZ register many times (the @@ -24527,7 +31925,7 @@ index 0000000..fab2961 + RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); +} + -+DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); ++DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0); + +#ifdef CONFIG_USB_DWC_OTG_LPM + @@ -27026,10 +34424,10 @@ index 0000000..55fd337 +#endif /* (__DWC_OTG_CFI_H__) */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil.c b/drivers/usb/host/dwc_otg/dwc_otg_cil.c new file mode 100644 -index 0000000..6a32c5c +index 0000000..e40060f --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -@@ -0,0 +1,7151 @@ +@@ -0,0 +1,7141 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ + * $Revision: #191 $ @@ -29276,9 +36674,7 @@ index 0000000..6a32c5c + */ +void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) +{ -+ uint32_t intr_enable; + hcintmsk_data_t hc_intr_mask; -+ gintmsk_data_t gintmsk = {.d32 = 0 }; + hcchar_data_t hcchar; + hcsplt_data_t hcsplt; + @@ -29380,14 +36776,6 @@ index 0000000..6a32c5c + } + DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32); + -+ /* Enable the top level host channel interrupt. */ -+ intr_enable = (1 << hc_num); -+ DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable); -+ -+ /* Make sure host channel interrupts are enabled. */ -+ gintmsk.b.hcintr = 1; -+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ + /* + * Program the HCCHARn register with the endpoint characteristics for + * the current transfer. @@ -35653,10 +43041,10 @@ index 0000000..79dbf83 +#endif diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c new file mode 100644 -index 0000000..59fc862 +index 0000000..96c76e3 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -0,0 +1,1563 @@ +@@ -0,0 +1,1594 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $ + * $Revision: #32 $ @@ -36977,7 +44365,7 @@ index 0000000..59fc862 +/** + * This function returns the Core Interrupt register. + */ -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd) +{ + gahbcfg_data_t gahbcfg = {.d32 = 0 }; + gintsts_data_t gintsts; @@ -36994,26 +44382,45 @@ index 0000000..59fc862 + gintmsk_common.b.lpmtranrcvd = 1; +#endif + gintmsk_common.b.restoredone = 1; -+ /** @todo: The port interrupt occurs while in device -+ * mode. Added code to CIL to clear the interrupt for now! -+ */ -+ gintmsk_common.b.portintr = 1; -+ ++ if(dwc_otg_is_device_mode(core_if)) ++ { ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ } + gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); + gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ if(fiq_enable) { ++ local_fiq_disable(); ++ /* Pull in the interrupts that the FIQ has masked */ ++ gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); ++ gintmsk.d32 |= gintmsk_common.d32; ++ /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ ++ reenable_gintmsk->d32 = gintmsk.d32; ++ local_fiq_enable(); ++ } ++ + gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); + +#ifdef DEBUG + /* if any common interrupts set */ + if (gintsts.d32 & gintmsk_common.d32) { -+ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n", + gintsts.d32, gintmsk.d32); + } +#endif -+ if (gahbcfg.b.glblintrmsk) ++ if (!fiq_enable){ ++ if (gahbcfg.b.glblintrmsk) ++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); ++ else ++ return 0; ++ } else { ++ /* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface. ++ * Can't trust the global interrupt mask bit in this case. ++ */ + return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -+ else -+ return 0; ++ } + +} + @@ -37045,6 +44452,7 @@ index 0000000..59fc862 +{ + int retval = 0; + gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk_reenable = { .d32 = 0 }; + gpwrdn_data_t gpwrdn = {.d32 = 0 }; + dwc_otg_device_t *otg_dev = dev; + dwc_otg_core_if_t *core_if = otg_dev->core_if; @@ -37066,7 +44474,10 @@ index 0000000..59fc862 + } + + if (core_if->hibernation_suspend <= 0) { -+ gintsts.d32 = dwc_otg_read_common_intr(core_if); ++ /* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end ++ * of this handler - god only knows why it's done like this ++ */ ++ gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd); + + if (gintsts.b.modemismatch) { + retval |= dwc_otg_handle_mode_mismatch_intr(core_if); @@ -37163,8 +44574,17 @@ index 0000000..59fc862 + gintsts.b.portintr = 1; + DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); + retval |= 1; ++ gintmsk_reenable.b.portintr = 1; + + } ++ /* Did we actually handle anything? if so, unmask the interrupt */ ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); ++// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); ++ if (retval && fiq_enable) { ++ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); ++ } ++ + } else { + DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); + @@ -37217,7 +44637,6 @@ index 0000000..59fc862 + } + if (core_if->lock) + DWC_SPINUNLOCK(core_if->lock); -+ + return retval; +} diff --git a/drivers/usb/host/dwc_otg/dwc_otg_core_if.h b/drivers/usb/host/dwc_otg/dwc_otg_core_if.h @@ -37933,10 +45352,10 @@ index 0000000..4138fd1 +#endif /* __DWC_CORE_IF_H__ */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h new file mode 100644 -index 0000000..8900318 +index 0000000..ccc24e0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -@@ -0,0 +1,116 @@ +@@ -0,0 +1,117 @@ +/* ========================================================================== + * + * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, @@ -37988,6 +45407,7 @@ index 0000000..8900318 + return old; +} + ++#define DBG_USER (0x1) +/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ +#define DBG_CIL (0x2) +/** When debug level has the DBG_CILV bit set, display CIL Verbose debug @@ -38055,10 +45475,10 @@ index 0000000..8900318 +#endif diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c new file mode 100644 -index 0000000..ac2c846 +index 0000000..dc7cd32 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -0,0 +1,1700 @@ +@@ -0,0 +1,1749 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $ + * $Revision: #92 $ @@ -38117,6 +45537,7 @@ index 0000000..ac2c846 +#include "dwc_otg_core_if.h" +#include "dwc_otg_pcd_if.h" +#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_fiq_fsm.h" + +#define DWC_DRIVER_VERSION "3.00a 10-AUG-2012" +#define DWC_DRIVER_DESC "HS OTG USB Controller driver" @@ -38125,6 +45546,7 @@ index 0000000..ac2c846 + +static const char dwc_driver_name[] = "dwc_otg"; + ++ +extern int pcd_init( +#ifdef LM_INTERFACE + struct lm_device *_dev @@ -38299,6 +45721,15 @@ index 0000000..ac2c846 + .adp_enable = -1, +}; + ++//Global variable to switch the fiq fix on or off ++bool fiq_enable = 1; ++// Global variable to enable the split transaction fix ++bool fiq_fsm_enable = true; ++//Bulk split-transaction NAK holdoff in microframes ++uint16_t nak_holdoff = 8; ++ ++unsigned short fiq_fsm_mask = 0x07; ++ +/** + * This function shows the Driver Version. + */ @@ -38840,17 +46271,32 @@ index 0000000..ac2c846 + _dev->resource->start, + _dev->resource->end - _dev->resource->start + 1); +#if 1 -+ if (!request_mem_region(_dev->resource->start, -+ _dev->resource->end - _dev->resource->start + 1, ++ if (!request_mem_region(_dev->resource[0].start, ++ _dev->resource[0].end - _dev->resource[0].start + 1, + "dwc_otg")) { + dev_dbg(&_dev->dev, "error reserving mapped memory\n"); + retval = -EFAULT; + goto fail; + } + -+ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start, -+ _dev->resource->end - -+ _dev->resource->start+1); ++ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, ++ _dev->resource[0].end - ++ _dev->resource[0].start+1); ++ if (fiq_enable) ++ { ++ if (!request_mem_region(_dev->resource[1].start, ++ _dev->resource[1].end - _dev->resource[1].start + 1, ++ "dwc_otg")) { ++ dev_dbg(&_dev->dev, "error reserving mapped memory\n"); ++ retval = -EFAULT; ++ goto fail; ++ } ++ ++ dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, ++ _dev->resource[1].end - ++ _dev->resource[1].start + 1); ++ } ++ +#else + { + struct map_desc desc = { @@ -38937,9 +46383,9 @@ index 0000000..ac2c846 + */ + +#if defined(PLATFORM_INTERFACE) -+ devirq = platform_get_irq(_dev, 0); ++ devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1); +#else -+ devirq = _dev->irq; ++ devirq = _dev->irq; +#endif + DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", + devirq); @@ -39105,6 +46551,12 @@ index 0000000..ac2c846 + int retval = 0; + int error; + struct device_driver *drv; ++ ++ if(fiq_fsm_enable && !fiq_enable) { ++ printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n"); ++ fiq_enable = 1; ++ } ++ + printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, + DWC_DRIVER_VERSION, +#ifdef LM_INTERFACE @@ -39124,6 +46576,9 @@ index 0000000..ac2c846 + printk(KERN_ERR "%s retval=%d\n", __func__, retval); + return retval; + } ++ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled"); ++ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled"); ++ printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled"); + + error = driver_create_file(drv, &driver_attr_version); +#ifdef DEBUG @@ -39404,6 +46859,20 @@ index 0000000..ac2c846 +module_param(microframe_schedule, bool, 0444); +MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); + ++module_param(fiq_enable, bool, 0444); ++MODULE_PARM_DESC(fiq_enable, "Enable the FIQ"); ++module_param(nak_holdoff, ushort, 0644); ++MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8"); ++module_param(fiq_fsm_enable, bool, 0444); ++MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask"); ++module_param(fiq_fsm_mask, ushort, 0444); ++MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n" ++ "Bit 0 : Non-periodic split transactions\n" ++ "Bit 1 : Periodic split transactions\n" ++ "Bit 2 : High-speed multi-transfer isochronous\n" ++ "All other bits should be set 0."); ++ ++ +/** @page "Module Parameters" + * + * The following parameters may be specified when starting the module. @@ -39851,12 +47320,1823 @@ index 0000000..6a8be63 +#endif + +#endif +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c +new file mode 100644 +index 0000000..276ad0c7 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c +@@ -0,0 +1,1346 @@ ++/* ++ * dwc_otg_fiq_fsm.c - The finite state machine FIQ ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This FIQ implements functionality that performs split transactions on ++ * the dwc_otg hardware without any outside intervention. A split transaction ++ * is "queued" by nominating a specific host channel to perform the entirety ++ * of a split transaction. This FIQ will then perform the microframe-precise ++ * scheduling required in each phase of the transaction until completion. ++ * ++ * The FIQ functionality is glued into the Synopsys driver via the entry point ++ * in the FSM enqueue function, and at the exit point in handling a HC interrupt ++ * for a FSM-enabled channel. ++ * ++ * NB: Large parts of this implementation have architecture-specific code. ++ * For porting this functionality to other ARM machines, the minimum is required: ++ * - An interrupt controller allowing the top-level dwc USB interrupt to be routed ++ * to the FIQ ++ * - A method of forcing a software generated interrupt from FIQ mode that then ++ * triggers an IRQ entry (with the dwc USB handler called by this IRQ number) ++ * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same ++ * processor core - there is no locking between the FIQ and IRQ (aside from ++ * local_fiq_disable) ++ * ++ */ ++ ++#include "dwc_otg_fiq_fsm.h" ++ ++ ++char buffer[1000*16]; ++int wptr; ++void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...) ++{ ++ enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR; ++ va_list args; ++ char text[17]; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) }; ++ ++ if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR) ++ { ++ snprintf(text, 9, " %4d:%1u ", hfnum.b.frnum/8, hfnum.b.frnum & 7); ++ va_start(args, fmt); ++ vsnprintf(text+8, 9, fmt, args); ++ va_end(args); ++ ++ memcpy(buffer + wptr, text, 16); ++ wptr = (wptr + 16) % sizeof(buffer); ++ } ++} ++ ++/** ++ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock ++ * Must be called with local interrupts and FIQ disabled. ++ */ ++#ifdef CONFIG_ARCH_BCM2709 ++inline void fiq_fsm_spin_lock(fiq_lock_t *lock) ++{ ++ unsigned long tmp; ++ uint32_t newval; ++ fiq_lock_t lockval; ++ smp_mb__before_spinlock(); ++ /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable ++ * will be sufficient. If we are on a different CPU, then the lock protects us. */ ++ prefetchw(&lock->slock); ++ asm volatile ( ++ "1: ldrex %0, [%3]\n" ++ " add %1, %0, %4\n" ++ " strex %2, %1, [%3]\n" ++ " teq %2, #0\n" ++ " bne 1b" ++ : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) ++ : "r" (&lock->slock), "I" (1 << 16) ++ : "cc"); ++ ++ while (lockval.tickets.next != lockval.tickets.owner) { ++ wfe(); ++ lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); ++ } ++ smp_mb(); ++} ++#else ++inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { } ++#endif ++ ++/** ++ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock ++ */ ++#ifdef CONFIG_ARCH_BCM2709 ++inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) ++{ ++ smp_mb(); ++ lock->tickets.owner++; ++ dsb_sev(); ++} ++#else ++inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { } ++#endif ++ ++/** ++ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction ++ * @channel: channel to re-enable ++ */ ++static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force) ++{ ++ hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) }; ++ ++ hcchar.b.chen = 0; ++ if (st->channel[n].hcchar_copy.b.eptype & 0x1) { ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ /* Hardware bug workaround: update the ssplit index */ ++ if (st->channel[n].hcsplt_copy.b.spltena) ++ st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; ++ ++ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++ } ++ ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); ++ hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ hcchar.b.chen = 1; ++ ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); ++ fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force); ++} ++ ++/** ++ * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage ++ * @st: Pointer to the channel's state ++ * @n : channel number ++ * ++ * Change host channel registers to perform a complete-split transaction. Being mindful of the ++ * endpoint direction, set control regs up correctly. ++ */ ++static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n) ++{ ++ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) }; ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ ++ hcsplt.b.compsplt = 1; ++ if (st->channel[n].hcchar_copy.b.epdir == 1) { ++ // If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket. ++ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; ++ } else { ++ // If OUT, the CSPLIT result contains handshake only. ++ hctsiz.b.xfersize = 0; ++ } ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++ mb(); ++} ++ ++static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n) ++{ ++ /* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */ ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ ++ if (st->channel[n].hcchar_copy.b.epdir == 0) { ++ return st->channel[n].hctsiz_copy.b.xfersize; ++ } else { ++ return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize; ++ } ++ ++} ++ ++ ++/** ++ * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT ++ * ++ * Of use only for IN periodic transfers. ++ */ ++static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n) ++{ ++ hcdma_data_t hcdma; ++ int i = st->channel[n].dma_info.index; ++ int len; ++ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; ++ ++ len = fiq_get_xfer_len(st, n); ++ fiq_print(FIQDBG_INT, st, "LEN: %03d", len); ++ st->channel[n].dma_info.slot_len[i] = len; ++ i++; ++ if (i > 6) ++ BUG(); ++ ++ hcdma.d32 = (dma_addr_t) &blob->channel[n].index[i].buf[0]; ++ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ st->channel[n].dma_info.index = i; ++ return 0; ++} ++ ++/** ++ * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ ++ */ ++static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n) ++{ ++ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; ++ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; ++ hctsiz.b.pktcnt = 1; ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++} ++ ++/** ++ * fiq_iso_out_advance() - update DMA address and split position bits ++ * for isochronous OUT transactions. ++ * ++ * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and ++ * Split-BEGIN states are not handled - this is done when the transaction was queued. ++ * ++ * This function must only be called from the FIQ_ISO_OUT_ACTIVE state. ++ */ ++static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n) ++{ ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ hcdma_data_t hcdma; ++ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; ++ int last = 0; ++ int i = st->channel[n].dma_info.index; ++ ++ fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i); ++ i++; ++ if (i == 4) ++ last = 1; ++ if (st->channel[n].dma_info.slot_len[i+1] == 255) ++ last = 1; ++ ++ /* New DMA address - address of bounce buffer referred to in index */ ++ hcdma.d32 = (uint32_t) &blob->channel[n].index[i].buf[0]; ++ //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n)); ++ //hcdma.d32 += st->channel[n].dma_info.slot_len[i]; ++ fiq_print(FIQDBG_INT, st, "LAST: %01d ", last); ++ fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]); ++ hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT); ++ hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ); ++ hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID; ++ /* Set up new packet length */ ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i]; ++ fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32); ++ ++ st->channel[n].dma_info.index++; ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); ++ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ return last; ++} ++ ++/** ++ * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT ++ * ++ * Despite the limitations of the DWC core, we can force a microframe pipeline of ++ * isochronous OUT start-split transactions while waiting for a corresponding other-type ++ * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it ++ * is very unlikely that filling the start-split FIFO will cause data loss. ++ * This allows much better interleaving of transactions in an order-independent way- ++ * there is no requirement to prioritise isochronous, just a state-space search has ++ * to be performed on each periodic start-split complete interrupt. ++ */ ++static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n) ++{ ++ int hub_addr = st->channel[n].hub_addr; ++ int port_addr = st->channel[n].port_addr; ++ int i, poked = 0; ++ for (i = 0; i < num_channels; i++) { ++ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ if (st->channel[i].hub_addr == hub_addr && ++ st->channel[i].port_addr == port_addr) { ++ switch (st->channel[i].fsm) { ++ case FIQ_PER_ISO_OUT_PENDING: ++ if (st->channel[i].nrpackets == 1) { ++ st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ fiq_fsm_restart_channel(st, i, 0); ++ poked = 1; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ if (poked) ++ break; ++ } ++ return poked; ++} ++ ++/** ++ * fiq_fsm_tt_in_use() - search for host channels using this TT ++ * @n: Channel to use as reference ++ * ++ */ ++int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n) ++{ ++ int hub_addr = st->channel[n].hub_addr; ++ int port_addr = st->channel[n].port_addr; ++ int i, in_use = 0; ++ for (i = 0; i < num_channels; i++) { ++ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ switch (st->channel[i].fsm) { ++ /* TT is reserved for channels that are in the middle of a periodic ++ * split transaction. ++ */ ++ case FIQ_PER_SSPLIT_STARTED: ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ //case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ case FIQ_PER_ISO_OUT_LAST: ++ if (st->channel[i].hub_addr == hub_addr && ++ st->channel[i].port_addr == port_addr) { ++ in_use = 1; ++ } ++ break; ++ default: ++ break; ++ } ++ if (in_use) ++ break; ++ } ++ return in_use; ++} ++ ++/** ++ * fiq_fsm_more_csplits() - determine whether additional CSPLITs need ++ * to be issued for this IN transaction. ++ * ++ * We cannot tell the inbound PID of a data packet due to hardware limitations. ++ * we need to make an educated guess as to whether we need to queue another CSPLIT ++ * or not. A no-brainer is when we have received enough data to fill the endpoint ++ * size, but for endpoints that give variable-length data then we have to resort ++ * to heuristics. ++ * ++ * We also return whether this is the last CSPLIT to be queued, again based on ++ * heuristics. This is to allow a 1-uframe overlap of periodic split transactions. ++ * Note: requires at least 1 CSPLIT to have been performed prior to being called. ++ */ ++ ++/* ++ * We need some way of guaranteeing if a returned periodic packet of size X ++ * has a DATA0 PID. ++ * The heuristic value of 144 bytes assumes that the received data has maximal ++ * bit-stuffing and the clock frequency of the transmitting device is at the lowest ++ * permissible limit. If the transfer length results in a final packet size ++ * 144 < p <= 188, then an erroneous CSPLIT will be issued. ++ * Also used to ensure that an endpoint will nominally only return a single ++ * complete-split worth of data. ++ */ ++#define DATA0_PID_HEURISTIC 144 ++ ++static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last) ++{ ++ ++ int i; ++ int total_len = 0; ++ int more_needed = 1; ++ struct fiq_channel_state *st = &state->channel[n]; ++ ++ for (i = 0; i < st->dma_info.index; i++) { ++ total_len += st->dma_info.slot_len[i]; ++ } ++ ++ *probably_last = 0; ++ ++ if (st->hcchar_copy.b.eptype == 0x3) { ++ /* ++ * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data ++ * then this is definitely the last CSPLIT. ++ */ ++ *probably_last = 1; ++ } else { ++ /* Isoc IN. This is a bit risky if we are the first transaction: ++ * we may have been held off slightly. */ ++ if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) { ++ more_needed = 0; ++ } ++ /* If in the next uframe we will receive enough data to fill the endpoint, ++ * then only issue 1 more csplit. ++ */ ++ if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC) ++ *probably_last = 1; ++ } ++ ++ if (total_len >= st->hctsiz_copy.b.xfersize || ++ i == 6 || total_len == 0) ++ /* Note: due to bit stuffing it is possible to have > 6 CSPLITs for ++ * a single endpoint. Accepting more would completely break our scheduling mechanism though ++ * - in these extreme cases we will pass through a truncated packet. ++ */ ++ more_needed = 0; ++ ++ return more_needed; ++} ++ ++/** ++ * fiq_fsm_too_late() - Test transaction for lateness ++ * ++ * If a SSPLIT for a large IN transaction is issued too late in a frame, ++ * the hub will disable the port to the device and respond with ERR handshakes. ++ * The hub status endpoint will not reflect this change. ++ * Returns 1 if we will issue a SSPLIT that will result in a device babble. ++ */ ++int notrace fiq_fsm_too_late(struct fiq_state *st, int n) ++{ ++ int uframe; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ uframe = hfnum.b.frnum & 0x7; ++ if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) { ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ ++ ++/** ++ * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline ++ * ++ * Search pending transactions in the start-split pending state and queue them. ++ * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4). ++ * Note: we specifically don't do isochronous OUT transactions first because better ++ * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT. ++ */ ++static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels) ++{ ++ int n; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; ++ if ((hfnum.b.frnum & 0x7) == 5) ++ return; ++ for (n = 0; n < num_channels; n++) { ++ if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) { ++ /* Check to see if any other transactions are using this TT */ ++ if(!fiq_fsm_tt_in_use(st, num_channels, n)) { ++ if (!fiq_fsm_too_late(st, n)) { ++ st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; ++ fiq_print(FIQDBG_INT, st, "NEXTPER "); ++ fiq_fsm_restart_channel(st, n, 0); ++ } else { ++ st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ } ++ break; ++ } ++ } ++ } ++ for (n = 0; n < num_channels; n++) { ++ if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) { ++ if (!fiq_fsm_tt_in_use(st, num_channels, n)) { ++ fiq_print(FIQDBG_INT, st, "NEXTISO "); ++ st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ fiq_fsm_restart_channel(st, n, 0); ++ break; ++ } ++ } ++ } ++} ++ ++/** ++ * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data ++ * @state: Pointer to fiq_state ++ * @n: Channel transaction is active on ++ * @hcint: Copy of host channel interrupt register ++ * ++ * Returns 0 if there are no more transactions for this HC to do, 1 ++ * otherwise. ++ */ ++static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint) ++{ ++ struct fiq_channel_state *st = &state->channel[n]; ++ int xfer_len = 0, nrpackets = 0; ++ hcdma_data_t hcdma; ++ fiq_print(FIQDBG_INT, state, "HSISO %02d", n); ++ ++ xfer_len = fiq_get_xfer_len(state, n); ++ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len; ++ ++ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32; ++ ++ st->hs_isoc_info.index++; ++ if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) { ++ return 0; ++ } ++ ++ /* grab the next DMA address offset from the array */ ++ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset; ++ FIQ_WRITE(state->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); ++ ++ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as ++ * the core needs to be told to send the correct number. Caution: for IN transfers, ++ * this is always set to the maximum size of the endpoint. */ ++ xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length; ++ /* Integer divide in a FIQ: fun. FIXME: make this not suck */ ++ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; ++ if (nrpackets == 0) ++ nrpackets = 1; ++ st->hcchar_copy.b.multicnt = nrpackets; ++ st->hctsiz_copy.b.pktcnt = nrpackets; ++ ++ /* Initial PID also needs to be set */ ++ if (st->hcchar_copy.b.epdir == 0) { ++ st->hctsiz_copy.b.xfersize = xfer_len; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_MDATA; ++ break; ++ } ++ ++ } else { ++ switch (st->hcchar_copy.b.multicnt) { ++ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA1; ++ break; ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA2; ++ break; ++ } ++ } ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32); ++ /* Channel is enabled on hcint handler exit */ ++ fiq_print(FIQDBG_INT, state, "HSISOOUT"); ++ return 1; ++} ++ ++ ++/** ++ * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler ++ * @state: Pointer to the state struct passed from banked FIQ mode registers. ++ * @num_channels: set according to the DWC hardware configuration ++ * ++ * The SOF handler in FSM mode has two functions ++ * 1. Hold off SOF from causing schedule advancement in IRQ context if there's ++ * nothing to do ++ * 2. Advance certain FSM states that require either a microframe delay, or a microframe ++ * of holdoff. ++ * ++ * The second part is architecture-specific to mach-bcm2835 - ++ * a sane interrupt controller would have a mask register for ARM interrupt sources ++ * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt ++ * number (USB) can be enabled. This means that certain parts of the USB specification ++ * that require "wait a little while, then issue another packet" cannot be fulfilled with ++ * the timing granularity required to achieve optimal throughout. The workaround is to use ++ * the SOF "timer" (125uS) to perform this task. ++ */ ++static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels) ++{ ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) }; ++ int n; ++ int kick_irq = 0; ++ ++ if ((hfnum.b.frnum & 0x7) == 1) { ++ /* We cannot issue csplits for transactions in the last frame past (n+1).1 ++ * Check to see if there are any transactions that are stale. ++ * Boot them out. ++ */ ++ for (n = 0; n < num_channels; n++) { ++ switch (state->channel[n].fsm) { ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_CSPLIT_LAST: ++ /* Check if we are no longer in the same full-speed frame. */ ++ if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) < ++ (hfnum.b.frnum & ~0x7)) ++ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ break; ++ default: ++ break; ++ } ++ } ++ } ++ ++ for (n = 0; n < num_channels; n++) { ++ switch (state->channel[n].fsm) { ++ ++ case FIQ_NP_SSPLIT_RETRY: ++ case FIQ_NP_IN_CSPLIT_RETRY: ++ case FIQ_NP_OUT_CSPLIT_RETRY: ++ fiq_fsm_restart_channel(state, n, 0); ++ break; ++ ++ case FIQ_HS_ISOC_SLEEPING: ++ state->channel[n].fsm = FIQ_HS_ISOC_TURBO; ++ fiq_fsm_restart_channel(state, n, 0); ++ break; ++ ++ case FIQ_PER_SSPLIT_QUEUED: ++ if ((hfnum.b.frnum & 0x7) == 5) ++ break; ++ if(!fiq_fsm_tt_in_use(state, num_channels, n)) { ++ if (!fiq_fsm_too_late(state, n)) { ++ fiq_print(FIQDBG_INT, st, "SOF GO %01d", n); ++ fiq_fsm_restart_channel(state, n, 0); ++ state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ /* Transaction cannot be started without risking a device babble error */ ++ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; ++ state->haintmsk_saved.b2.chint &= ~(1 << n); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); ++ kick_irq |= 1; ++ } ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_PENDING: ++ /* Ordinarily, this should be poked after the SSPLIT ++ * complete interrupt for a competing transfer on the same ++ * TT. Doesn't happen for aborted transactions though. ++ */ ++ if ((hfnum.b.frnum & 0x7) >= 5) ++ break; ++ if (!fiq_fsm_tt_in_use(state, num_channels, n)) { ++ /* Hardware bug. SOF can sometimes occur after the channel halt interrupt ++ * that caused this. ++ */ ++ fiq_fsm_restart_channel(state, n, 0); ++ fiq_print(FIQDBG_INT, state, "SOF ISOC"); ++ if (state->channel[n].nrpackets == 1) { ++ state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_WAIT: ++ /* we are guaranteed to be in this state if and only if the SSPLIT interrupt ++ * occurred when the bus transaction occurred. The SOF interrupt reversal bug ++ * will utterly bugger this up though. ++ */ ++ if (hfnum.b.frnum != state->channel[n].expected_uframe) { ++ fiq_print(FIQDBG_INT, state, "SOFCS %d ", n); ++ state->channel[n].fsm = FIQ_PER_CSPLIT_POLL; ++ fiq_fsm_restart_channel(state, n, 0); ++ fiq_fsm_start_next_periodic(state, num_channels); ++ ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ case FIQ_DEQUEUE_ISSUED: ++ /* Ugly: we have to force a HCD interrupt. ++ * Poke the mask for the channel in question. ++ * We will take a fake SOF because of this, but ++ * that's OK. ++ */ ++ state->haintmsk_saved.b2.chint &= ~(1 << n); ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); ++ kick_irq |= 1; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ if (state->kick_np_queues || ++ dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum)) ++ kick_irq |= 1; ++ ++ return !kick_irq; ++} ++ ++ ++/** ++ * fiq_fsm_do_hcintr() - FSM host channel interrupt handler ++ * @state: Pointer to the FIQ state struct ++ * @num_channels: Number of channels as per hardware config ++ * @n: channel for which HAINT(i) was raised ++ * ++ * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well. ++ */ ++static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcint_data_t hcint_probe; ++ hcchar_data_t hcchar; ++ int handled = 0; ++ int restart = 0; ++ int last_csplit = 0; ++ int start_next_periodic = 0; ++ struct fiq_channel_state *st = &state->channel[n]; ++ hfnum_data_t hfnum; ++ ++ hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT); ++ hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK); ++ hcint_probe.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) { ++ fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm); ++ fiq_print(FIQDBG_INT, state, "%08x", hcint.d32); ++ } ++ ++ switch (st->fsm) { ++ ++ case FIQ_PASSTHROUGH: ++ case FIQ_DEQUEUE_ISSUED: ++ /* doesn't belong to us, kick it upstairs */ ++ break; ++ ++ case FIQ_PASSTHROUGH_ERRORSTATE: ++ /* We are here to emulate the error recovery mechanism of the dwc HCD. ++ * Several interrupts are unmasked if a previous transaction failed - it's ++ * death for the FIQ to attempt to handle them as the channel isn't halted. ++ * Emulate what the HCD does in this situation: mask and continue. ++ * The FSM has no other state setup so this has to be handled out-of-band. ++ */ ++ fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); ++ if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { ++ fiq_print(FIQDBG_ERR, state, "RESET %02d", n); ++ /* In some random cases we can get a NAK interrupt coincident with a Xacterr ++ * interrupt, after the device has disappeared. ++ */ ++ if (!hcint.b.xacterr) ++ st->nr_errors = 0; ++ hcintmsk.b.nak = 0; ++ hcintmsk.b.ack = 0; ++ hcintmsk.b.datatglerr = 0; ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32); ++ return 1; ++ } ++ if (hcint_probe.b.chhltd) { ++ fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n); ++ fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32); ++ return 0; ++ } ++ break; ++ ++ /* Non-periodic state groups */ ++ case FIQ_NP_SSPLIT_STARTED: ++ case FIQ_NP_SSPLIT_RETRY: ++ /* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */ ++ if (hcint.b.ack) { ++ /* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction ++ * will start shortly. SOF needs to kick the transaction to prevent a NYET flood. ++ */ ++ if(st->hcchar_copy.b.epdir == 1) ++ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; ++ else ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ st->nr_errors = 0; ++ handled = 1; ++ fiq_fsm_setup_csplit(state, n); ++ } else if (hcint.b.nak) { ++ // No buffer space in TT. Retry on a uframe boundary. ++ st->fsm = FIQ_NP_SSPLIT_RETRY; ++ handled = 1; ++ } else if (hcint.b.xacterr) { ++ // The only other one we care about is xacterr. This implies HS bus error - retry. ++ st->nr_errors++; ++ st->fsm = FIQ_NP_SSPLIT_RETRY; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else { ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ handled = 0; ++ restart = 0; ++ } ++ break; ++ ++ case FIQ_NP_IN_CSPLIT_RETRY: ++ /* Received a CSPLIT done interrupt. ++ * Expected Data/NAK/STALL/NYET for IN. ++ */ ++ if (hcint.b.xfercomp) { ++ /* For IN, data is present. */ ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nak) { ++ /* no endpoint data. Punt it upstairs */ ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nyet) { ++ /* CSPLIT NYET - retry on a uframe boundary. */ ++ handled = 1; ++ st->nr_errors = 0; ++ } else if (hcint.b.datatglerr) { ++ /* data toggle errors do not set the xfercomp bit. */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else if (hcint.b.xacterr) { ++ /* HS error. Retry immediate */ ++ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else if (hcint.b.stall || hcint.b.bblerr) { ++ /* A STALL implies either a LS bus error or a genuine STALL. */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else { ++ /* Hardware bug. It's possible in some cases to ++ * get a channel halt with nothing else set when ++ * the response was a NYET. Treat as local 3-strikes retry. ++ */ ++ hcint_data_t hcint_test = hcint; ++ hcint_test.b.chhltd = 0; ++ if (!hcint_test.d32) { ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ } ++ } else { ++ /* Bail out if something unexpected happened */ ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } ++ } ++ break; ++ ++ case FIQ_NP_OUT_CSPLIT_RETRY: ++ /* Received a CSPLIT done interrupt. ++ * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/ ++ if (hcint.b.xfercomp) { ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nak) { ++ // The HCD will implement the holdoff on frame boundaries. ++ st->fsm = FIQ_NP_SPLIT_DONE; ++ } else if (hcint.b.nyet) { ++ // Hub still processing. ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ handled = 1; ++ st->nr_errors = 0; ++ //restart = 1; ++ } else if (hcint.b.xacterr) { ++ /* HS error. retry immediate */ ++ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ restart = 1; ++ } ++ } else if (hcint.b.stall) { ++ /* LS bus error or genuine stall */ ++ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; ++ } else { ++ /* ++ * Hardware bug. It's possible in some cases to get a ++ * channel halt with nothing else set when the response was a NYET. ++ * Treat as local 3-strikes retry. ++ */ ++ hcint_data_t hcint_test = hcint; ++ hcint_test.b.chhltd = 0; ++ if (!hcint_test.d32) { ++ st->nr_errors++; ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } else { ++ handled = 1; ++ } ++ } else { ++ // Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it. ++ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; ++ } ++ } ++ break; ++ ++ /* Periodic split states (except isoc out) */ ++ case FIQ_PER_SSPLIT_STARTED: ++ /* Expect an ACK or failure for SSPLIT */ ++ if (hcint.b.ack) { ++ /* ++ * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs. ++ * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1 ++ * point for microframe n-3, the packet will not appear on the bus until microframe n. ++ * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus ++ * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1 ++ * coincident with SOF for n+1. ++ * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place. ++ * These appear to be caused by timing/clock crossing bugs within the core itself. ++ * State machine workaround. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ fiq_fsm_setup_csplit(state, n); ++ /* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct ++ * time. If not, then we're in the next SOF. ++ */ ++ if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) { ++ fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n); ++ st->expected_uframe = hfnum.b.frnum; ++ st->fsm = FIQ_PER_CSPLIT_WAIT; ++ } else { ++ fiq_print(FIQDBG_INT, state, "CSPOL %01d", n); ++ /* For isochronous IN endpoints, ++ * we need to hold off if we are expecting a lot of data */ ++ if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) { ++ start_next_periodic = 1; ++ } ++ /* Danger will robinson: we are in a broken state. If our first interrupt after ++ * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable ++ * lag. Unmask the NYET interrupt. ++ */ ++ st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; ++ st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1; ++ restart = 1; ++ } ++ handled = 1; ++ } else if (hcint.b.xacterr) { ++ /* 3-strikes retry is enabled, we have hit our max nr_errors */ ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ start_next_periodic = 1; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ start_next_periodic = 1; ++ } ++ /* We can now queue the next isochronous OUT transaction, if one is pending. */ ++ if(fiq_fsm_tt_next_isoc(state, num_channels, n)) { ++ fiq_print(FIQDBG_INT, state, "NEXTISO "); ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_NYET1: ++ /* First CSPLIT attempt was a NYET. If we get a subsequent NYET, ++ * we are too late and the TT has dropped its CSPLIT fifo. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ start_next_periodic = 1; ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ st->fsm = FIQ_PER_CSPLIT_POLL; ++ st->nr_errors = 0; ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ handled = 1; ++ restart = 1; ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ /* Doh. Data lost. */ ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_BROKEN_NYET1: ++ /* ++ * we got here because our host channel is in the delayed-interrupt ++ * state and we cannot take a NYET interrupt any later than when it ++ * occurred. Disable then re-enable the channel if this happens to force ++ * CSPLITs to occur at the right time. ++ */ ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ fiq_print(FIQDBG_INT, state, "BROK: %01d ", n); ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ start_next_periodic = 1; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ st->fsm = FIQ_PER_CSPLIT_POLL; ++ handled = 1; ++ restart = 1; ++ start_next_periodic = 1; ++ /* Reload HCTSIZ for the next transfer */ ++ fiq_fsm_reload_hctsiz(state, n); ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ start_next_periodic = 1; ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ /* Local 3-strikes retry is handled by the core. This is a ERR response.*/ ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_PER_CSPLIT_POLL: ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); ++ start_next_periodic = 1; ++ if (hcint.b.nak) { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } else if (hcint.b.xfercomp) { ++ fiq_increment_dma_buf(state, num_channels, n); ++ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { ++ handled = 1; ++ restart = 1; ++ /* Reload HCTSIZ for the next transfer */ ++ fiq_fsm_reload_hctsiz(state, n); ++ if (!last_csplit) ++ start_next_periodic = 0; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } else if (hcint.b.nyet) { ++ /* Are we a NYET after the first data packet? */ ++ if (st->nrpackets == 0) { ++ st->fsm = FIQ_PER_CSPLIT_NYET1; ++ handled = 1; ++ restart = 1; ++ } else { ++ /* We got a NYET when polling CSPLITs. Can happen ++ * if our heuristic fails, or if someone disables us ++ * for any significant length of time. ++ */ ++ if (st->nr_errors >= 3) { ++ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_DONE; ++ } ++ } ++ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { ++ /* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/ ++ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; ++ } else { ++ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; ++ } ++ break; ++ ++ case FIQ_HS_ISOC_TURBO: ++ if (fiq_fsm_update_hs_isoc(state, n, hcint)) { ++ /* more transactions to come */ ++ handled = 1; ++ restart = 1; ++ fiq_print(FIQDBG_INT, state, "HSISO M "); ++ } else { ++ st->fsm = FIQ_HS_ISOC_DONE; ++ fiq_print(FIQDBG_INT, state, "HSISO F "); ++ } ++ break; ++ ++ case FIQ_HS_ISOC_ABORTED: ++ /* This abort is called by the driver rewriting the state mid-transaction ++ * which allows the dequeue mechanism to work more effectively. ++ */ ++ break; ++ ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ if (hcint.b.ack) { ++ if(fiq_iso_out_advance(state, num_channels, n)) { ++ /* last OUT transfer */ ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ /* ++ * Assuming the periodic FIFO in the dwc core ++ * actually does its job properly, we can queue ++ * the next ssplit now and in theory, the wire ++ * transactions will be in-order. ++ */ ++ // No it doesn't. It appears to process requests in host channel order. ++ //start_next_periodic = 1; ++ } ++ handled = 1; ++ restart = 1; ++ } else { ++ /* ++ * Isochronous transactions carry on regardless. Log the error ++ * and continue. ++ */ ++ //explode += 1; ++ st->nr_errors++; ++ if(fiq_iso_out_advance(state, num_channels, n)) { ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ //start_next_periodic = 1; ++ } ++ handled = 1; ++ restart = 1; ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_LAST: ++ if (hcint.b.ack) { ++ /* All done here */ ++ st->fsm = FIQ_PER_ISO_OUT_DONE; ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_DONE; ++ st->nr_errors++; ++ } ++ start_next_periodic = 1; ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ /* SOF kicked us because we overran. */ ++ start_next_periodic = 1; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (handled) { ++ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32); ++ } else { ++ /* Copy the regs into the state so the IRQ knows what to do */ ++ st->hcint_copy.d32 = hcint.d32; ++ } ++ ++ if (restart) { ++ /* Restart always implies handled. */ ++ if (restart == 2) { ++ /* For complete-split INs, the show must go on. ++ * Force a channel restart */ ++ fiq_fsm_restart_channel(state, n, 1); ++ } else { ++ fiq_fsm_restart_channel(state, n, 0); ++ } ++ } ++ if (start_next_periodic) { ++ fiq_fsm_start_next_periodic(state, num_channels); ++ } ++ if (st->fsm != FIQ_PASSTHROUGH) ++ fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm); ++ ++ return handled; ++} ++ ++ ++/** ++ * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ ++ * @state: pointer to state struct passed from the banked FIQ mode registers. ++ * @num_channels: set according to the DWC hardware configuration ++ * @dma: pointer to DMA bounce buffers for split transaction slots ++ * ++ * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode ++ * inside an EHCI or similar host controller regarding split transactions. The DWC core ++ * interrupts each and every time a split transaction packet is received or sent successfully. ++ * This results in either an interrupt storm when everything is working "properly", or ++ * the interrupt latency of the system in general breaks time-sensitive periodic split ++ * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ ++ * solves these problems. ++ * ++ * Return: void ++ */ ++void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) ++{ ++ gintsts_data_t gintsts, gintsts_handled; ++ gintmsk_data_t gintmsk; ++ //hfnum_data_t hfnum; ++ haint_data_t haint, haint_handled; ++ haintmsk_data_t haintmsk; ++ int kick_irq = 0; ++ ++ gintsts_handled.d32 = 0; ++ haint_handled.d32 = 0; ++ ++ fiq_fsm_spin_lock(&state->lock); ++ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); ++ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); ++ gintsts.d32 &= gintmsk.d32; ++ ++ if (gintsts.b.sofintr) { ++ /* For FSM mode, SOF is required to keep the state machine advance for ++ * certain stages of the periodic pipeline. It's death to mask this ++ * interrupt in that case. ++ */ ++ ++ if (!fiq_fsm_do_sof(state, num_channels)) { ++ /* Kick IRQ once. Queue advancement means that all pending transactions ++ * will get serviced when the IRQ finally executes. ++ */ ++ if (state->gintmsk_saved.b.sofintr == 1) ++ kick_irq |= 1; ++ state->gintmsk_saved.b.sofintr = 0; ++ } ++ gintsts_handled.b.sofintr = 1; ++ } ++ ++ if (gintsts.b.hcintr) { ++ int i; ++ haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT); ++ haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK); ++ haint.d32 &= haintmsk.d32; ++ haint_handled.d32 = 0; ++ for (i=0; ihaintmsk_saved.b2.chint &= ~(1 << i); ++ } else { ++ /* do_hcintr cleaned up after itself, but clear haint */ ++ haint_handled.b2.chint |= (1 << i); ++ } ++ } ++ } ++ ++ if (haint_handled.b2.chint) { ++ FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32); ++ } ++ ++ if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) { ++ /* ++ * This is necessary to avoid multiple retriggers of the MPHI in the case ++ * where interrupts are held off and HCINTs start to pile up. ++ * Only wake up the IRQ if a new interrupt came in, was not handled and was ++ * masked. ++ */ ++ haintmsk.d32 &= state->haintmsk_saved.d32; ++ FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32); ++ kick_irq |= 1; ++ } ++ /* Top-Level interrupt - always handled because it's level-sensitive */ ++ gintsts_handled.b.hcintr = 1; ++ } ++ ++ ++ /* Clear the bits in the saved register that were not handled but were triggered. */ ++ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); ++ ++ /* FIQ didn't handle something - mask has changed - write new mask */ ++ if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) { ++ gintmsk.d32 &= state->gintmsk_saved.d32; ++ gintmsk.b.sofintr = 1; ++ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); ++// fiq_print(FIQDBG_INT, state, "KICKGINT"); ++// fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32); ++// fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32); ++ kick_irq |= 1; ++ } ++ ++ if (gintsts_handled.d32) { ++ /* Only applies to edge-sensitive bits in GINTSTS */ ++ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); ++ } ++ ++ /* We got an interrupt, didn't handle it. */ ++ if (kick_irq) { ++ state->mphi_int_count++; ++ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); ++ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); ++ ++ } ++ state->fiq_done++; ++ mb(); ++ fiq_fsm_spin_unlock(&state->lock); ++} ++ ++ ++/** ++ * dwc_otg_fiq_nop() - FIQ "lite" ++ * @state: pointer to state struct passed from the banked FIQ mode registers. ++ * ++ * The "nop" handler does not intervene on any interrupts other than SOF. ++ * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals ++ * with non-periodic/periodic queues) needs to be kicked. ++ * ++ * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second. ++ * ++ * Return: void ++ */ ++void notrace dwc_otg_fiq_nop(struct fiq_state *state) ++{ ++ gintsts_data_t gintsts, gintsts_handled; ++ gintmsk_data_t gintmsk; ++ hfnum_data_t hfnum; ++ ++ fiq_fsm_spin_lock(&state->lock); ++ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); ++ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); ++ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); ++ gintsts.d32 &= gintmsk.d32; ++ gintsts_handled.d32 = 0; ++ ++ if (gintsts.b.sofintr) { ++ if (!state->kick_np_queues && ++ dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) { ++ /* SOF handled, no work to do, just ACK interrupt */ ++ gintsts_handled.b.sofintr = 1; ++ } else { ++ /* Kick IRQ */ ++ state->gintmsk_saved.b.sofintr = 0; ++ } ++ } ++ ++ /* Reset handled interrupts */ ++ if(gintsts_handled.d32) { ++ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); ++ } ++ ++ /* Clear the bits in the saved register that were not handled but were triggered. */ ++ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); ++ ++ /* We got an interrupt, didn't handle it and want to mask it */ ++ if (~(state->gintmsk_saved.d32)) { ++ state->mphi_int_count++; ++ gintmsk.d32 &= state->gintmsk_saved.d32; ++ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); ++ /* Force a clear before another dummy send */ ++ FIQ_WRITE(state->mphi_regs.intstat, (1<<29)); ++ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); ++ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); ++ ++ } ++ state->fiq_done++; ++ mb(); ++ fiq_fsm_spin_unlock(&state->lock); ++} +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h +new file mode 100644 +index 0000000..8455324 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h +@@ -0,0 +1,367 @@ ++/* ++ * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This FIQ implements functionality that performs split transactions on ++ * the dwc_otg hardware without any outside intervention. A split transaction ++ * is "queued" by nominating a specific host channel to perform the entirety ++ * of a split transaction. This FIQ will then perform the microframe-precise ++ * scheduling required in each phase of the transaction until completion. ++ * ++ * The FIQ functionality has been surgically implanted into the Synopsys ++ * vendor-provided driver. ++ * ++ */ ++ ++#ifndef DWC_OTG_FIQ_FSM_H_ ++#define DWC_OTG_FIQ_FSM_H_ ++ ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_hcd.h" ++#include ++#include ++#include ++#include ++ ++#if 0 ++#define FLAME_ON(x) \ ++do { \ ++ int gpioreg; \ ++ \ ++ gpioreg = readl(__io_address(0x20200000+0x8)); \ ++ gpioreg &= ~(7 << (x-20)*3); \ ++ gpioreg |= 0x1 << (x-20)*3; \ ++ writel(gpioreg, __io_address(0x20200000+0x8)); \ ++ \ ++ writel(1< 1, SOF wakes up the isochronous FSM */ ++ FIQ_HS_ISOC_SLEEPING = 24, ++ FIQ_HS_ISOC_DONE = 25, ++ FIQ_HS_ISOC_ABORTED = 26, ++ FIQ_DEQUEUE_ISSUED = 30, ++ FIQ_TEST = 32, ++}; ++ ++struct fiq_stack { ++ int magic1; ++ uint8_t stack[2048]; ++ int magic2; ++}; ++ ++ ++/** ++ * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel) ++ * @index: Number of slots reported used for IN transactions / number of slots ++ * transmitted for an OUT transaction ++ * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused) ++ * ++ * Split transaction transfers can have variable length depending on other bus ++ * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore ++ * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers ++ * can happen per-frame. ++ */ ++struct fiq_dma_info { ++ u8 index; ++ u8 slot_len[6]; ++}; ++ ++struct __attribute__((packed)) fiq_split_dma_slot { ++ u8 buf[188]; ++}; ++ ++struct fiq_dma_channel { ++ struct __attribute__((packed)) fiq_split_dma_slot index[6]; ++}; ++ ++struct fiq_dma_blob { ++ struct __attribute__((packed)) fiq_dma_channel channel[0]; ++}; ++ ++/** ++ * struct fiq_hs_isoc_info - USB2.0 isochronous data ++ * @iso_frame: Pointer to the array of OTG URB iso_frame_descs. ++ * @nrframes: Total length of iso_frame_desc array ++ * @index: Current index (FIQ-maintained) ++ * ++ */ ++struct fiq_hs_isoc_info { ++ struct dwc_otg_hcd_iso_packet_desc *iso_desc; ++ unsigned int nrframes; ++ unsigned int index; ++}; ++ ++/** ++ * struct fiq_channel_state - FIQ state machine storage ++ * @fsm: Current state of the channel as understood by the FIQ ++ * @nr_errors: Number of transaction errors on this split-transaction ++ * @hub_addr: SSPLIT/CSPLIT destination hub ++ * @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub ++ * @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For ++ * split-IN, number of CSPLIT data packets that were received. ++ * @hcchar_copy: ++ * @hcsplt_copy: ++ * @hcintmsk_copy: ++ * @hctsiz_copy: Copies of the host channel registers. ++ * For use as scratch, or for returning state. ++ * ++ * The fiq_channel_state is state storage between interrupts for a host channel. The ++ * FSM state is stored here. Members of this structure must only be set up by the ++ * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ ++ * has updated the state to either a COMPLETE state group or ABORT state group. ++ */ ++ ++struct fiq_channel_state { ++ enum fiq_fsm_state fsm; ++ unsigned int nr_errors; ++ unsigned int hub_addr; ++ unsigned int port_addr; ++ /* Hardware bug workaround: sometimes channel halt interrupts are ++ * delayed until the next SOF. Keep track of when we expected to get interrupted. */ ++ unsigned int expected_uframe; ++ /* in/out for communicating number of dma buffers used, or number of ISOC to do */ ++ unsigned int nrpackets; ++ struct fiq_dma_info dma_info; ++ struct fiq_hs_isoc_info hs_isoc_info; ++ /* Copies of HC registers - in/out communication from/to IRQ handler ++ * and for ease of channel setup. A bit of mungeing is performed - for ++ * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint. ++ */ ++ hcchar_data_t hcchar_copy; ++ hcsplt_data_t hcsplt_copy; ++ hcint_data_t hcint_copy; ++ hcintmsk_data_t hcintmsk_copy; ++ hctsiz_data_t hctsiz_copy; ++ hcdma_data_t hcdma_copy; ++}; ++ ++/** ++ * struct fiq_state - top-level FIQ state machine storage ++ * @mphi_regs: virtual address of the MPHI peripheral register file ++ * @dwc_regs_base: virtual address of the base of the DWC core register file ++ * @dma_base: physical address for the base of the DMA bounce buffers ++ * @dummy_send: Scratch area for sending a fake message to the MPHI peripheral ++ * @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled. ++ * Used for determining which interrupts fired to set off the IRQ handler. ++ * @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally. ++ * @np_count: Non-periodic transactions in the active queue ++ * @np_sent: Count of non-periodic transactions that have completed ++ * @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism, ++ * this is the next frame on which a SOF interrupt is required. Used to hold off ++ * passing SOF through to the driver until necessary. ++ * @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host ++ * channels configured into the core logic. ++ * ++ * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub. ++ * It contains top-level state information. ++ */ ++struct fiq_state { ++ fiq_lock_t lock; ++ mphi_regs_t mphi_regs; ++ void *dwc_regs_base; ++ dma_addr_t dma_base; ++ struct fiq_dma_blob *fiq_dmab; ++ void *dummy_send; ++ gintmsk_data_t gintmsk_saved; ++ haintmsk_data_t haintmsk_saved; ++ int mphi_int_count; ++ unsigned int fiq_done; ++ unsigned int kick_np_queues; ++ unsigned int next_sched_frame; ++#ifdef FIQ_DEBUG ++ char * buffer; ++ unsigned int bufsiz; ++#endif ++ struct fiq_channel_state channel[0]; ++}; ++ ++extern void fiq_fsm_spin_lock(fiq_lock_t *lock); ++ ++extern void fiq_fsm_spin_unlock(fiq_lock_t *lock); ++ ++extern int fiq_fsm_too_late(struct fiq_state *st, int n); ++ ++extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); ++ ++extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels); ++ ++extern void dwc_otg_fiq_nop(struct fiq_state *state); ++ ++#endif /* DWC_OTG_FIQ_FSM_H_ */ +diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S +new file mode 100644 +index 0000000..ffa8d21 +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S +@@ -0,0 +1,80 @@ ++/* ++ * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ ++ * ++ * Copyright (c) 2013 Raspberry Pi Foundation ++ * ++ * Author: Jonathan Bell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * * Neither the name of Raspberry Pi nor the ++ * names of its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY ++ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ++ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ++ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++ ++#include ++#include ++ ++ ++.text ++ ++.global _dwc_otg_fiq_stub_end; ++ ++/** ++ * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow ++ * a C-style function call with arguments from the FIQ banked registers. ++ * r0 = &hcd->fiq_state ++ * r1 = &hcd->num_channels ++ * r2 = &hcd->dma_buffers ++ * Tramples: r0, r1, r2, r4, fp, ip ++ */ ++ ++ENTRY(_dwc_otg_fiq_stub) ++ /* Stash unbanked regs - SP will have been set up for us */ ++ mov ip, sp; ++ stmdb sp!, {r0-r12, lr}; ++#ifdef FIQ_DEBUG ++ // Cycle profiling - read cycle counter at start ++ mrc p15, 0, r5, c15, c12, 1; ++#endif ++ /* r11 = fp, don't trample it */ ++ mov r4, fp; ++ /* set EABI frame size */ ++ sub fp, ip, #512; ++ ++ /* for fiq NOP mode - just need state */ ++ mov r0, r8; ++ /* r9 = num_channels */ ++ mov r1, r9; ++ /* r10 = struct *dma_bufs */ ++// mov r2, r10; ++ ++ /* r4 = &fiq_c_function */ ++ blx r4; ++#ifdef FIQ_DEBUG ++ mrc p15, 0, r4, c15, c12, 1; ++ subs r5, r5, r4; ++ // r5 is now the cycle count time for executing the FIQ. Store it somewhere? ++#endif ++ ldmia sp!, {r0-r12, lr}; ++ subs pc, lr, #4; ++_dwc_otg_fiq_stub_end: ++END(_dwc_otg_fiq_stub) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c new file mode 100644 -index 0000000..1e89549 +index 0000000..6c53d2c --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -0,0 +1,3479 @@ +@@ -0,0 +1,4252 @@ + +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $ @@ -39899,10 +49179,15 @@ index 0000000..1e89549 + * header file. + */ + ++#include ++#include ++ +#include "dwc_otg_hcd.h" +#include "dwc_otg_regs.h" ++#include "dwc_otg_fiq_fsm.h" + +extern bool microframe_schedule; ++extern uint16_t fiq_fsm_mask, nak_holdoff; + +//#define DEBUG_HOST_CHANNELS +#ifdef DEBUG_HOST_CHANNELS @@ -39912,6 +49197,7 @@ index 0000000..1e89549 +static int last_sel_trans_num_avail_hc_at_end = 0; +#endif /* DEBUG_HOST_CHANNELS */ + ++ +dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) +{ + return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); @@ -40021,31 +49307,43 @@ index 0000000..1e89549 + +/** + * Processes all the URBs in a single list of QHs. Completes them with -+ * -ETIMEDOUT and frees the QTD. ++ * -ESHUTDOWN and frees the QTD. + */ +static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) +{ -+ dwc_list_link_t *qh_item; ++ dwc_list_link_t *qh_item, *qh_tmp; + dwc_otg_qh_t *qh; + dwc_otg_qtd_t *qtd, *qtd_tmp; + -+ DWC_LIST_FOREACH(qh_item, qh_list) { ++ DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) { + qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); + DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, + &qh->qtd_list, qtd_list_entry) { + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); + if (qtd->urb != NULL) { + hcd->fops->complete(hcd, qtd->urb->priv, -+ qtd->urb, -DWC_E_TIMEOUT); ++ qtd->urb, -DWC_E_SHUTDOWN); + dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); + } + + } ++ if(qh->channel) { ++ /* Using hcchar.chen == 1 is not a reliable test. ++ * It is possible that the channel has already halted ++ * but not yet been through the IRQ handler. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ if(microframe_schedule) ++ hcd->available_host_channels++; ++ qh->channel = NULL; ++ } ++ dwc_otg_hcd_qh_remove(hcd, qh); + } +} + +/** -+ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic + * and periodic schedules. The QTD associated with each URB is removed from + * the schedule and freed. This function may be called when a disconnect is + * detected or when the HCD is being stopped. @@ -40131,7 +49429,8 @@ index 0000000..1e89549 + */ + dwc_otg_hcd->flags.b.port_connect_status_change = 1; + dwc_otg_hcd->flags.b.port_connect_status = 0; -+ ++ if(fiq_enable) ++ local_fiq_disable(); + /* + * Shutdown any transfers in process by clearing the Tx FIFO Empty + * interrupt mask and status bits and disabling subsequent host @@ -40227,8 +49526,17 @@ index 0000000..1e89549 + channel->qh = NULL; + } + } ++ if(fiq_fsm_enable) { ++ for(i=0; i < 128; i++) { ++ dwc_otg_hcd->hub_port[i] = 0; ++ } ++ } ++ + } + ++ if(fiq_enable) ++ local_fiq_enable(); ++ + if (dwc_otg_hcd->fops->disconnect) { + dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); + } @@ -40266,6 +49574,7 @@ index 0000000..1e89549 +} +#endif + ++ +/** + * HCD Callback function for Remote Wakeup. + * @@ -40316,10 +49625,12 @@ index 0000000..1e89549 + dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle, + int atomic_alloc) +{ -+ dwc_irqflags_t flags; + int retval = 0; ++ uint8_t needs_scheduling = 0; ++ dwc_otg_transaction_type_e tr_type; + dwc_otg_qtd_t *qtd; + gintmsk_data_t intr_mask = {.d32 = 0 }; ++ hprt0_data_t hprt0 = { .d32 = 0 }; + +#ifdef DEBUG /* integrity checks (Broadcom) */ + if (NULL == hcd->core_if) { @@ -40334,6 +49645,16 @@ index 0000000..1e89549 + return -DWC_E_NO_DEVICE; + } + ++ /* Some core configurations cannot support LS traffic on a FS root port */ ++ if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) && ++ (hcd->core_if->hwcfg2.b.fs_phy_type == 1) && ++ (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) { ++ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) { ++ return -DWC_E_NO_DEVICE; ++ } ++ } ++ + qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc); + if (qtd == NULL) { + DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); @@ -40349,32 +49670,27 @@ index 0000000..1e89549 + return -DWC_E_NO_MEMORY; + } +#endif -+ retval = -+ dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); ++ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); ++ if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1; ++ if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) ++ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ ++ needs_scheduling = 0; ++ ++ retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); + // creates a new queue in ep_handle if it doesn't exist already + if (retval < 0) { + DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " + "Error status %d\n", retval); + dwc_otg_hcd_qtd_free(qtd); -+ } else { -+ qtd->qh = *ep_handle; ++ return retval; + } -+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -+ if (!intr_mask.b.sofintr && retval == 0) { -+ dwc_otg_transaction_type_e tr_type; -+ if ((qtd->qh->ep_type == UE_BULK) -+ && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { -+ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -+ return 0; -+ } -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if(needs_scheduling) { + tr_type = dwc_otg_hcd_select_transactions(hcd); + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); + } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); + } -+ + return retval; +} + @@ -40383,6 +49699,8 @@ index 0000000..1e89549 +{ + dwc_otg_qh_t *qh; + dwc_otg_qtd_t *urb_qtd; ++ BUG_ON(!hcd); ++ BUG_ON(!dwc_otg_urb); + +#ifdef DEBUG /* integrity checks (Broadcom) */ + @@ -40399,14 +49717,17 @@ index 0000000..1e89549 + return -DWC_E_INVALID; + } + urb_qtd = dwc_otg_urb->qtd; ++ BUG_ON(!urb_qtd); + if (urb_qtd->qh == NULL) { + DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); + return -DWC_E_INVALID; + } +#else + urb_qtd = dwc_otg_urb->qtd; ++ BUG_ON(!urb_qtd); +#endif + qh = urb_qtd->qh; ++ BUG_ON(!qh); + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { + if (urb_qtd->in_process) { + dump_channel_info(hcd, qh); @@ -40421,6 +49742,7 @@ index 0000000..1e89549 + if (urb_qtd->in_process && qh->channel) { + /* The QTD is in process (it has been assigned to a channel). */ + if (hcd->flags.b.port_connect_status) { ++ int n = qh->channel->hc_num; + /* + * If still connected (i.e. in host mode), halt the + * channel so it can be used for other transfers. If @@ -40428,8 +49750,16 @@ index 0000000..1e89549 + * written to halt the channel since the core is in + * device mode. + */ -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ /* In FIQ FSM mode, we need to shut down carefully. ++ * The FIQ may attempt to restart a disabled channel */ ++ if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) { ++ qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE; ++ qh->channel->halt_pending = 1; ++ hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED; ++ } else { ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } + } + } + @@ -40546,6 +49876,32 @@ index 0000000..1e89549 + dwc_otg_hcd->flags.b.port_reset_change = 1; +} + ++static void completion_tasklet_func(void *ptr) ++{ ++ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr; ++ struct urb *urb; ++ urb_tq_entry_t *item; ++ dwc_irqflags_t flags; ++ ++ /* This could just be spin_lock_irq */ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) { ++ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list); ++ urb = item->urb; ++ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item, ++ urb_tq_entries); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ DWC_FREE(item); ++ ++ usb_hcd_giveback_urb(hcd->priv, urb, urb->status); ++ ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ return; ++} ++ +static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) +{ + dwc_list_link_t *item; @@ -40633,6 +49989,34 @@ index 0000000..1e89549 + cil_hcd_start(core_if); +} + ++void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num) ++{ ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ struct fiq_dma_blob *blob = hcd->fiq_dmab; ++ int i; ++ ++ st->fsm = FIQ_PASSTHROUGH; ++ st->hcchar_copy.d32 = 0; ++ st->hcsplt_copy.d32 = 0; ++ st->hcint_copy.d32 = 0; ++ st->hcintmsk_copy.d32 = 0; ++ st->hctsiz_copy.d32 = 0; ++ st->hcdma_copy.d32 = 0; ++ st->nr_errors = 0; ++ st->hub_addr = 0; ++ st->port_addr = 0; ++ st->expected_uframe = 0; ++ st->nrpackets = 0; ++ st->dma_info.index = 0; ++ for (i = 0; i < 6; i++) ++ st->dma_info.slot_len[i] = 255; ++ st->hs_isoc_info.index = 0; ++ st->hs_isoc_info.iso_desc = NULL; ++ st->hs_isoc_info.nrframes = 0; ++ ++ DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128); ++} ++ +/** + * Frees secondary storage associated with the dwc_otg_hcd structure contained + * in the struct usb_hcd field. @@ -40678,12 +50062,15 @@ index 0000000..1e89549 + } else if (dwc_otg_hcd->status_buf != NULL) { + DWC_FREE(dwc_otg_hcd->status_buf); + } ++ DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock); + DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); + /* Set core_if's lock pointer to NULL */ + dwc_otg_hcd->core_if->lock = NULL; + + DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); + DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); ++ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); ++ DWC_FREE(dwc_otg_hcd->fiq_state); + +#ifdef DWC_DEV_SRPCAP + if (dwc_otg_hcd->core_if->power_down == 2 && @@ -40733,7 +50120,7 @@ index 0000000..1e89549 + DWC_LIST_INIT(&hcd->periodic_sched_ready); + DWC_LIST_INIT(&hcd->periodic_sched_assigned); + DWC_LIST_INIT(&hcd->periodic_sched_queued); -+ ++ DWC_TAILQ_INIT(&hcd->completed_urb_list); + /* + * Create a host channel descriptor for each host channel implemented + * in the controller. Initialize the channel descriptor array. @@ -40761,6 +50148,60 @@ index 0000000..1e89549 + channel); + } + ++ if (fiq_enable) { ++ hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)); ++ if (!hcd->fiq_state) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels))); ++ ++ for (i = 0; i < num_channels; i++) { ++ hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH; ++ } ++ hcd->fiq_state->dummy_send = DWC_ALLOC_ATOMIC(16); ++ ++ hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack)); ++ if (!hcd->fiq_stack) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ hcd->fiq_stack->magic1 = 0xDEADBEEF; ++ hcd->fiq_stack->magic2 = 0xD00DFEED; ++ hcd->fiq_state->gintmsk_saved.d32 = ~0; ++ hcd->fiq_state->haintmsk_saved.b2.chint = ~0; ++ ++ /* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools ++ * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels) ++ * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some ++ * moderately readable array casts. ++ */ ++ hcd->fiq_dmab = DWC_DMA_ALLOC((sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base); ++ DWC_WARN("FIQ DMA bounce buffers: virt = 0x%08x dma = 0x%08x len=%d", ++ (unsigned int)hcd->fiq_dmab, (unsigned int)hcd->fiq_state->dma_base, ++ sizeof(struct fiq_dma_channel) * num_channels); ++ ++ DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024); ++ ++ /* pointer for debug in fiq_print */ ++ hcd->fiq_state->fiq_dmab = hcd->fiq_dmab; ++ if (fiq_fsm_enable) { ++ int i; ++ for (i=0; i < hcd->core_if->core_params->host_channels; i++) { ++ dwc_otg_cleanup_fiq_channel(hcd, i); ++ } ++ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s%s", ++ (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", ++ (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", ++ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "", ++ (fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : ""); ++ } ++ } ++ + /* Initialize the Connection timeout timer. */ + hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", + dwc_otg_hcd_connect_timeout, 0); @@ -40771,6 +50212,9 @@ index 0000000..1e89549 + + /* Initialize reset tasklet. */ + hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); ++ ++ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet", ++ completion_tasklet_func, hcd); +#ifdef DWC_DEV_SRPCAP + if (hcd->core_if->power_down == 2) { + /* Initialize Power on timer for Host power up in case hibernation */ @@ -40803,6 +50247,12 @@ index 0000000..1e89549 + hcd->frame_list = NULL; + hcd->frame_list_dma = 0; + hcd->periodic_qh_count = 0; ++ ++ DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port)); ++#ifdef FIQ_DEBUG ++ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc)); ++#endif ++ +out: + return retval; +} @@ -40873,6 +50323,9 @@ index 0000000..1e89549 + dwc_otg_qtd_t *qtd; + dwc_otg_hcd_urb_t *urb; + void* ptr = NULL; ++ uint32_t intr_enable; ++ unsigned long flags; ++ gintmsk_data_t gintmsk = { .d32 = 0, }; + + qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); + @@ -40948,7 +50401,13 @@ index 0000000..1e89549 + uint32_t hub_addr, port_addr; + hc->do_split = 1; + hc->xact_pos = qtd->isoc_split_pos; -+ hc->complete_split = qtd->complete_split; ++ /* We don't need to do complete splits anymore */ ++// if(fiq_fsm_enable) ++ if (0) ++ hc->complete_split = qtd->complete_split = 0; ++ else ++ hc->complete_split = qtd->complete_split; ++ + hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); + hc->hub_addr = (uint8_t) hub_addr; + hc->port_addr = (uint8_t) port_addr; @@ -41092,9 +50551,537 @@ index 0000000..1e89549 + hc->desc_list_addr = qh->desc_list_dma; + + dwc_otg_hc_init(hcd->core_if, hc); ++ ++ local_irq_save(flags); ++ ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ } ++ ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc->hc_num); ++ DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ if (fiq_enable) { ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } ++ ++ local_irq_restore(flags); + hc->qh = qh; +} + ++ ++/** ++ * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ ++ * @qh: pointer to the endpoint's queue head ++ * ++ * Transaction start/end control flow is grafted onto the existing dwc_otg ++ * mechanisms, to avoid spaghettifying the functions more than they already are. ++ * This function's eligibility check is altered by debug parameter. ++ * ++ * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction. ++ */ ++ ++int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh) ++{ ++ if (qh->do_split) { ++ switch (qh->ep_type) { ++ case UE_CONTROL: ++ case UE_BULK: ++ if (fiq_fsm_mask & (1 << 0)) ++ return 1; ++ break; ++ case UE_INTERRUPT: ++ case UE_ISOCHRONOUS: ++ if (fiq_fsm_mask & (1 << 1)) ++ return 1; ++ break; ++ default: ++ break; ++ } ++ } else if (qh->ep_type == UE_ISOCHRONOUS) { ++ if (fiq_fsm_mask & (1 << 2)) { ++ /* HS ISOCH support. We test for compatibility: ++ * - DWORD aligned buffers ++ * - Must be at least 2 transfers (otherwise pointless to use the FIQ) ++ * If yes, then the fsm enqueue function will handle the state machine setup. ++ */ ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ struct dwc_otg_hcd_iso_packet_desc (*iso_descs)[0] = &urb->iso_descs; ++ int nr_iso_frames = urb->packet_count; ++ int i; ++ uint32_t ptr; ++ ++ if (nr_iso_frames < 2) ++ return 0; ++ for (i = 0; i < nr_iso_frames; i++) { ++ ptr = urb->dma + iso_descs[i]->offset; ++ if (ptr & 0x3) { ++ printk_ratelimited("%s: Non-Dword aligned isochronous frame offset." ++ " Cannot queue FIQ-accelerated transfer to device %d endpoint %d\n", ++ __FUNCTION__, qh->channel->dev_addr, qh->channel->ep_num); ++ return 0; ++ } ++ } ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++/** ++ * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers ++ * @hcd: Pointer to the dwc_otg_hcd struct ++ * @qh: Pointer to the endpoint's queue head ++ * ++ * Periodic split transactions are transmitted modulo 188 bytes. ++ * This necessitates slicing data up into buckets for isochronous out ++ * and fixing up the DMA address for all IN transfers. ++ * ++ * Returns 1 if the DMA bounce buffers have been used, 0 if the default ++ * HC buffer has been used. ++ */ ++int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh) ++ { ++ int frame_length, i = 0; ++ uint8_t *ptr = NULL; ++ dwc_hc_t *hc = qh->channel; ++ struct fiq_dma_blob *blob; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ for (i = 0; i < 6; i++) { ++ st->dma_info.slot_len[i] = 255; ++ } ++ st->dma_info.index = 0; ++ i = 0; ++ if (hc->ep_is_in) { ++ /* ++ * Set dma_regs to bounce buffer. FIQ will update the ++ * state depending on transaction progress. ++ */ ++ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; ++ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; ++ /* Calculate the max number of CSPLITS such that the FIQ can time out ++ * a transaction if it fails. ++ */ ++ frame_length = st->hcchar_copy.b.mps; ++ do { ++ i++; ++ frame_length -= 188; ++ } while (frame_length >= 0); ++ st->nrpackets = i; ++ return 1; ++ } else { ++ if (qh->ep_type == UE_ISOCHRONOUS) { ++ ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ frame_length = frame_desc->length; ++ ++ /* Virtual address for bounce buffers */ ++ blob = hcd->fiq_dmab; ++ ++ ptr = qtd->urb->buf + frame_desc->offset; ++ if (frame_length == 0) { ++ /* ++ * for isochronous transactions, we must still transmit a packet ++ * even if the length is zero. ++ */ ++ st->dma_info.slot_len[0] = 0; ++ st->nrpackets = 1; ++ } else { ++ do { ++ if (frame_length <= 188) { ++ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length); ++ st->dma_info.slot_len[i] = frame_length; ++ ptr += frame_length; ++ } else { ++ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188); ++ st->dma_info.slot_len[i] = 188; ++ ptr += 188; ++ } ++ i++; ++ frame_length -= 188; ++ } while (frame_length > 0); ++ st->nrpackets = i; ++ } ++ ptr = qtd->urb->buf + frame_desc->offset; ++ /* Point the HC at the DMA address of the bounce buffers */ ++ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; ++ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; ++ ++ /* fixup xfersize to the actual packet size */ ++ st->hctsiz_copy.b.pid = 0; ++ st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0]; ++ return 1; ++ } else { ++ /* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */ ++ return 0; ++ } ++ } ++} ++ ++/* ++ * Pushing a periodic request into the queue near the EOF1 point ++ * in a microframe causes erroneous behaviour (frmovrun) interrupt. ++ * Usually, the request goes out on the bus causing a transfer but ++ * the core does not transfer the data to memory. ++ * This guard interval (in number of 60MHz clocks) is required which ++ * must cater for CPU latency between reading the value and enabling ++ * the channel. ++ */ ++#define PERIODIC_FRREM_BACKOFF 1000 ++ ++int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ dwc_hc_t *hc = qh->channel; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ int frame; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; ++ int xfer_len, nrpackets; ++ hcdma_data_t hcdma; ++ hfnum_data_t hfnum; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) ++ return 0; ++ ++ st->nr_errors = 0; ++ ++ st->hcchar_copy.d32 = 0; ++ st->hcchar_copy.b.mps = hc->max_packet; ++ st->hcchar_copy.b.epdir = hc->ep_is_in; ++ st->hcchar_copy.b.devaddr = hc->dev_addr; ++ st->hcchar_copy.b.epnum = hc->ep_num; ++ st->hcchar_copy.b.eptype = hc->ep_type; ++ ++ st->hcintmsk_copy.b.chhltd = 1; ++ ++ frame = dwc_otg_hcd_get_frame_number(hcd); ++ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; ++ ++ st->hcchar_copy.b.lspddev = 0; ++ /* Enable the channel later as a final register write. */ ++ ++ st->hcsplt_copy.d32 = 0; ++ ++ st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs; ++ st->hs_isoc_info.nrframes = qtd->urb->packet_count; ++ /* grab the next DMA address offset from the array */ ++ st->hcdma_copy.d32 = qtd->urb->dma; ++ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset; ++ ++ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as ++ * the core needs to be told to send the correct number. Caution: for IN transfers, ++ * this is always set to the maximum size of the endpoint. */ ++ xfer_len = st->hs_isoc_info.iso_desc[0].length; ++ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; ++ if (nrpackets == 0) ++ nrpackets = 1; ++ st->hcchar_copy.b.multicnt = nrpackets; ++ st->hctsiz_copy.b.pktcnt = nrpackets; ++ ++ /* Initial PID also needs to be set */ ++ if (st->hcchar_copy.b.epdir == 0) { ++ st->hctsiz_copy.b.xfersize = xfer_len; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_MDATA; ++ break; ++ } ++ ++ } else { ++ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; ++ switch (st->hcchar_copy.b.multicnt) { ++ case 1: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA0; ++ break; ++ case 2: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA1; ++ break; ++ case 3: ++ st->hctsiz_copy.b.pid = DWC_PID_DATA2; ++ break; ++ } ++ } ++ ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); ++ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); ++ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { ++ /* Prevent queueing near EOF1. Bad things happen if a periodic ++ * split transaction is queued very close to EOF. ++ */ ++ st->fsm = FIQ_HS_ISOC_SLEEPING; ++ } else { ++ st->fsm = FIQ_HS_ISOC_TURBO; ++ st->hcchar_copy.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ } ++ mb(); ++ st->hcchar_copy.b.chen = 0; ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ return 0; ++} ++ ++ ++/** ++ * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state ++ * @hcd: Pointer to the dwc_otg_hcd struct ++ * @qh: Pointer to the endpoint's queue head ++ * ++ * This overrides the dwc_otg driver's normal method of queueing a transaction. ++ * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup ++ * for the nominated host channel. ++ * ++ * For periodic transfers, it also peeks at the FIQ state to see if an immediate ++ * start is possible. If not, then the FIQ is left to start the transfer. ++ */ ++int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ int start_immediate = 1, i; ++ hfnum_data_t hfnum; ++ dwc_hc_t *hc = qh->channel; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ /* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */ ++ int hub_addr, port_addr, frame, uframe; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; ++ ++ if (st->fsm != FIQ_PASSTHROUGH) ++ return 0; ++ st->nr_errors = 0; ++ ++ st->hcchar_copy.d32 = 0; ++ st->hcchar_copy.b.mps = hc->max_packet; ++ st->hcchar_copy.b.epdir = hc->ep_is_in; ++ st->hcchar_copy.b.devaddr = hc->dev_addr; ++ st->hcchar_copy.b.epnum = hc->ep_num; ++ st->hcchar_copy.b.eptype = hc->ep_type; ++ if (hc->ep_type & 0x1) { ++ if (hc->ep_is_in) ++ st->hcchar_copy.b.multicnt = 3; ++ else ++ /* Docs say set this to 1, but driver sets to 0! */ ++ st->hcchar_copy.b.multicnt = 0; ++ } else { ++ st->hcchar_copy.b.multicnt = 1; ++ st->hcchar_copy.b.oddfrm = 0; ++ } ++ st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0; ++ /* Enable the channel later as a final register write. */ ++ ++ st->hcsplt_copy.d32 = 0; ++ if(qh->do_split) { ++ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); ++ st->hcsplt_copy.b.compsplt = 0; ++ st->hcsplt_copy.b.spltena = 1; ++ // XACTPOS is for isoc-out only but needs initialising anyway. ++ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL; ++ if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) { ++ /* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL. ++ * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ ++ * will update as necessary. ++ */ ++ if (hc->xfer_len > 188) { ++ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN; ++ } ++ } ++ st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr; ++ st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr; ++ st->hub_addr = hub_addr; ++ st->port_addr = port_addr; ++ } ++ ++ st->hctsiz_copy.d32 = 0; ++ st->hctsiz_copy.b.dopng = 0; ++ st->hctsiz_copy.b.pid = hc->data_pid_start; ++ ++ if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { ++ hc->xfer_len = hc->max_packet; ++ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { ++ hc->xfer_len = 188; ++ } ++ st->hctsiz_copy.b.xfersize = hc->xfer_len; ++ ++ st->hctsiz_copy.b.pktcnt = 1; ++ ++ if (hc->ep_type & 0x1) { ++ /* ++ * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers, ++ * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array. ++ * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt ++ * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set ++ * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ ++ * must not touch internal driver state. ++ */ ++ if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) { ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ } else { ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ /* The FIQ depends upon no other interrupts being enabled except channel halt. ++ * Fixup channel interrupt mask. */ ++ st->hcintmsk_copy.d32 = 0; ++ st->hcintmsk_copy.b.chhltd = 1; ++ st->hcintmsk_copy.b.ahberr = 1; ++ ++ /* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions ++ * as Control puts the transfer into the non-periodic request queue and the ++ * non-periodic handler in the hub. Makes things lots easier. ++ */ ++ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) { ++ st->hcchar_copy.b.multicnt = 0; ++ st->hcchar_copy.b.oddfrm = 0; ++ st->hcchar_copy.b.eptype = UE_CONTROL; ++ if (hc->align_buff) { ++ st->hcdma_copy.d32 = hc->align_buff; ++ } else { ++ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); ++ } ++ } ++ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); ++ ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ ++ if (hc->ep_type & 0x1) { ++ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ frame = (hfnum.b.frnum & ~0x7) >> 3; ++ uframe = hfnum.b.frnum & 0x7; ++ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { ++ /* Prevent queueing near EOF1. Bad things happen if a periodic ++ * split transaction is queued very close to EOF. ++ */ ++ start_immediate = 0; ++ } else if (uframe == 5) { ++ start_immediate = 0; ++ } else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) { ++ start_immediate = 0; ++ } else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) { ++ start_immediate = 0; ++ } else { ++ /* Search through all host channels to determine if a transaction ++ * is currently in progress */ ++ for (i = 0; i < hcd->core_if->core_params->host_channels; i++) { ++ if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH) ++ continue; ++ switch (hcd->fiq_state->channel[i].fsm) { ++ /* TT is reserved for channels that are in the middle of a periodic ++ * split transaction. ++ */ ++ case FIQ_PER_SSPLIT_STARTED: ++ case FIQ_PER_CSPLIT_WAIT: ++ case FIQ_PER_CSPLIT_NYET1: ++ case FIQ_PER_CSPLIT_POLL: ++ case FIQ_PER_ISO_OUT_ACTIVE: ++ case FIQ_PER_ISO_OUT_LAST: ++ if (hcd->fiq_state->channel[i].hub_addr == hub_addr && ++ hcd->fiq_state->channel[i].port_addr == port_addr) { ++ start_immediate = 0; ++ } ++ break; ++ default: ++ break; ++ } ++ if (!start_immediate) ++ break; ++ } ++ } ++ } ++ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) ++ start_immediate = 1; ++ ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); ++ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); ++ switch (hc->ep_type) { ++ case UE_CONTROL: ++ case UE_BULK: ++ st->fsm = FIQ_NP_SSPLIT_STARTED; ++ break; ++ case UE_ISOCHRONOUS: ++ if (hc->ep_is_in) { ++ if (start_immediate) { ++ st->fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ } ++ } else { ++ if (start_immediate) { ++ /* Single-isoc OUT packets don't require FIQ involvement */ ++ if (st->nrpackets == 1) { ++ st->fsm = FIQ_PER_ISO_OUT_LAST; ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_ACTIVE; ++ } ++ } else { ++ st->fsm = FIQ_PER_ISO_OUT_PENDING; ++ } ++ } ++ break; ++ case UE_INTERRUPT: ++ if (fiq_fsm_mask & 0x8) { ++ st->fsm = FIQ_NP_SSPLIT_STARTED; ++ } else if (start_immediate) { ++ st->fsm = FIQ_PER_SSPLIT_STARTED; ++ } else { ++ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ } ++ default: ++ break; ++ } ++ if (start_immediate) { ++ /* Set the oddfrm bit as close as possible to actual queueing */ ++ frame = dwc_otg_hcd_get_frame_number(hcd); ++ st->expected_uframe = (frame + 1) & 0x3FFF; ++ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; ++ st->hcchar_copy.b.chen = 1; ++ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); ++ } ++ mb(); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ return 0; ++} ++ ++ +/** + * This function selects transactions from the HCD transfer schedule and + * assigns them to available host channels. It is called from HCD interrupt @@ -41110,13 +51097,9 @@ index 0000000..1e89549 + dwc_otg_qh_t *qh; + int num_channels; + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; + dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; + -+#ifdef DEBUG_SOF -+ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); -+#endif -+ +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_per_scheduled = 0; + last_sel_trans_num_nonper_scheduled = 0; @@ -41128,6 +51111,9 @@ index 0000000..1e89549 + + while (qh_ptr != &hcd->periodic_sched_ready && + !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ + if (microframe_schedule) { + // Make sure we leave one channel for non periodic transactions. + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); @@ -41153,8 +51139,6 @@ index 0000000..1e89549 + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, + &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -+ -+ ret_val = DWC_OTG_TRANSACTION_PERIODIC; + } + + /* @@ -41169,6 +51153,28 @@ index 0000000..1e89549 + num_channels - hcd->periodic_channels) && + !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { + ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ /* ++ * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission ++ * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed ++ * cheeky devices that just hold off using NAKs ++ */ ++ if (nak_holdoff && qh->do_split) { ++ if (qh->nak_frame != 0xffff) { ++ uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8); ++ uint16_t frame = dwc_otg_hcd_get_frame_number(hcd); ++ if (dwc_frame_num_le(frame, next_frame)) { ++ if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) { ++ hcd->fiq_state->next_sched_frame = next_frame; ++ } ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ continue; ++ } else { ++ qh->nak_frame = 0xFFFF; ++ } ++ } ++ } ++ + if (microframe_schedule) { + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + if (hcd->available_host_channels < 1) { @@ -41181,7 +51187,6 @@ index 0000000..1e89549 + last_sel_trans_num_nonper_scheduled++; +#endif /* DEBUG_HOST_CHANNELS */ + } -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); + + assign_and_init_hc(hcd, qh); + @@ -41195,21 +51200,41 @@ index 0000000..1e89549 + &qh->qh_list_entry); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + -+ if (ret_val == DWC_OTG_TRANSACTION_NONE) { -+ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; -+ } else { -+ ret_val = DWC_OTG_TRANSACTION_ALL; -+ } + + if (!microframe_schedule) + hcd->non_periodic_channels++; + } ++ /* we moved a non-periodic QH to the active schedule. If the inactive queue is empty, ++ * stop the FIQ from kicking us. We could potentially still have elements here if we ++ * ran out of host channels. ++ */ ++ if (fiq_enable) { ++ if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) { ++ hcd->fiq_state->kick_np_queues = 0; ++ } else { ++ /* For each entry remaining in the NP inactive queue, ++ * if this a NAK'd retransmit then don't set the kick flag. ++ */ ++ if(nak_holdoff) { ++ DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) { ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ if (qh->nak_frame == 0xFFFF) { ++ hcd->fiq_state->kick_np_queues = 1; ++ } ++ } ++ } ++ } ++ } ++ if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) ++ ret_val |= DWC_OTG_TRANSACTION_PERIODIC; ++ ++ if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) ++ ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC; ++ + +#ifdef DEBUG_HOST_CHANNELS + last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels; +#endif /* DEBUG_HOST_CHANNELS */ -+ -+ DWC_SPINLOCK_FREE(channel_lock); + return ret_val; +} + @@ -41244,6 +51269,12 @@ index 0000000..1e89549 + hc->qh->ping_state = 0; + } + } else if (!hc->xfer_started) { ++ if (fiq_fsm_enable && hc->error_state) { ++ hcd->fiq_state->channel[hc->hc_num].nr_errors = ++ DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count; ++ hcd->fiq_state->channel[hc->hc_num].fsm = ++ FIQ_PASSTHROUGH_ERRORSTATE; ++ } + dwc_otg_hc_start_transfer(hcd->core_if, hc); + hc->qh->ping_state = 0; + } @@ -41296,7 +51327,7 @@ index 0000000..1e89549 + hptxsts_data_t tx_status; + dwc_list_link_t *qh_ptr; + dwc_otg_qh_t *qh; -+ int status; ++ int status = 0; + int no_queue_space = 0; + int no_fifo_space = 0; + @@ -41323,20 +51354,36 @@ index 0000000..1e89549 + + qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); + -+ /* -+ * Set a flag if we're queuing high-bandwidth in slave mode. -+ * The flag prevents any halts to get into the request queue in -+ * the middle of multiple high-bandwidth packets getting queued. -+ */ -+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -+ hcd->core_if->queuing_high_bandwidth = 1; ++ // Do not send a split start transaction any later than frame .6 ++ // Note, we have to schedule a periodic in .5 to make it go in .6 ++ if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) ++ { ++ qh_ptr = qh_ptr->next; ++ hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; ++ continue; + } -+ status = -+ queue_transaction(hcd, qh->channel, -+ tx_status.b.ptxfspcavail); -+ if (status < 0) { -+ no_fifo_space = 1; -+ break; ++ ++ if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { ++ if (qh->do_split) ++ fiq_fsm_queue_split_transaction(hcd, qh); ++ else ++ fiq_fsm_queue_isoc_transaction(hcd, qh); ++ } else { ++ ++ /* ++ * Set a flag if we're queueing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { ++ hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ status = queue_transaction(hcd, qh->channel, ++ tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } + } + + /* @@ -41452,17 +51499,20 @@ index 0000000..1e89549 + + qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, + qh_list_entry); -+ status = -+ queue_transaction(hcd, qh->channel, -+ tx_status.b.nptxfspcavail); + -+ if (status > 0) { -+ more_to_do = 1; -+ } else if (status < 0) { -+ no_fifo_space = 1; -+ break; ++ if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { ++ fiq_fsm_queue_split_transaction(hcd, qh); ++ } else { ++ status = queue_transaction(hcd, qh->channel, ++ tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } + } -+ + /* Advance to next QH, skipping start-of-list entry. */ + hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; + if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { @@ -41544,9 +51594,16 @@ index 0000000..1e89549 + */ + gintmsk_data_t gintmsk = {.d32 = 0 }; + gintmsk.b.nptxfempty = 1; -+ DWC_MODIFY_REG32(&hcd->core_if-> -+ core_global_regs->gintmsk, gintmsk.d32, -+ 0); ++ ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ } + } + } +} @@ -42977,17 +53034,13 @@ index 0000000..1e89549 + else + dwc_otg_urb = DWC_ALLOC(size); + -+ if (NULL != dwc_otg_urb) -+ dwc_otg_urb->packet_count = iso_desc_count; ++ if (dwc_otg_urb) ++ dwc_otg_urb->packet_count = iso_desc_count; + else { -+ dwc_otg_urb->packet_count = 0; -+ if (size != 0) { -+ DWC_ERROR("**** DWC OTG HCD URB alloc - " -+ "%salloc of %db failed\n", -+ atomic_alloc?"atomic ":"", size); -+ } -+ } -+ ++ DWC_ERROR("**** DWC OTG HCD URB alloc - " ++ "%salloc of %db failed\n", ++ atomic_alloc?"atomic ":"", size); ++ } + return dwc_otg_urb; +} + @@ -43338,10 +53391,10 @@ index 0000000..1e89549 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h new file mode 100644 -index 0000000..bb4f67a +index 0000000..43dbed9 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -0,0 +1,824 @@ +@@ -0,0 +1,862 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $ + * $Revision: #58 $ @@ -43384,6 +53437,8 @@ index 0000000..bb4f67a +#include "dwc_otg_core_if.h" +#include "dwc_list.h" +#include "dwc_otg_cil.h" ++#include "dwc_otg_fiq_fsm.h" ++ + +/** + * @file @@ -43512,10 +53567,10 @@ index 0000000..bb4f67a + +/** Transaction types. */ +typedef enum dwc_otg_transaction_type { -+ DWC_OTG_TRANSACTION_NONE, -+ DWC_OTG_TRANSACTION_PERIODIC, -+ DWC_OTG_TRANSACTION_NON_PERIODIC, -+ DWC_OTG_TRANSACTION_ALL ++ DWC_OTG_TRANSACTION_NONE = 0, ++ DWC_OTG_TRANSACTION_PERIODIC = 1, ++ DWC_OTG_TRANSACTION_NON_PERIODIC = 2, ++ DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC +} dwc_otg_transaction_type_e; + +struct dwc_otg_qh; @@ -43665,6 +53720,11 @@ index 0000000..bb4f67a + */ + uint16_t sched_frame; + ++ /* ++ ** Frame a NAK was received on this queue head, used to minimise NAK retransmission ++ */ ++ uint16_t nak_frame; ++ + /** (micro)frame at which last start split was initialized. */ + uint16_t start_split_frame; + @@ -43709,10 +53769,19 @@ index 0000000..bb4f67a + + uint16_t speed; + uint16_t frame_usecs[8]; ++ ++ uint32_t skip_count; +} dwc_otg_qh_t; + +DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); + ++typedef struct urb_tq_entry { ++ struct urb *urb; ++ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries; ++} urb_tq_entry_t; ++ ++DWC_TAILQ_HEAD(urb_list, urb_tq_entry); ++ +/** + * This structure holds the state of the HCD, including the non-periodic and + * periodic schedules. @@ -43890,9 +53959,12 @@ index 0000000..bb4f67a + /* Tasket to do a reset */ + dwc_tasklet_t *reset_tasklet; + ++ dwc_tasklet_t *completion_tasklet; ++ struct urb_list completed_urb_list; ++ + /* */ + dwc_spinlock_t *lock; -+ ++ dwc_spinlock_t *channel_lock; + /** + * Private data that could be used by OS wrapper. + */ @@ -43903,9 +53975,21 @@ index 0000000..bb4f67a + /** Frame List */ + uint32_t *frame_list; + ++ /** Hub - Port assignment */ ++ int hub_port[128]; ++#ifdef FIQ_DEBUG ++ int hub_port_alloc[2048]; ++#endif ++ + /** Frame List DMA address */ + dma_addr_t frame_list_dma; + ++ struct fiq_stack *fiq_stack; ++ struct fiq_state *fiq_state; ++ ++ /** Virtual address for split transaction DMA bounce buffers */ ++ struct fiq_dma_blob *fiq_dmab; ++ +#ifdef DEBUG + uint32_t frrem_samples; + uint64_t frrem_accum; @@ -43933,6 +54017,13 @@ index 0000000..bb4f67a +extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, + dwc_otg_transaction_type_e tr_type); + ++int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); ++void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); ++ ++extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); ++extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh); ++extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num); ++ +/** @} */ + +/** @name Interrupt Handler Functions */ @@ -44168,10 +54259,10 @@ index 0000000..bb4f67a +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c new file mode 100644 -index 0000000..274967b +index 0000000..ee920c4 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -@@ -0,0 +1,1133 @@ +@@ -0,0 +1,1132 @@ +/*========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $ + * $Revision: #10 $ @@ -44450,7 +54541,7 @@ index 0000000..274967b +static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) +{ + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; + + dwc_hc_t *hc = qh->channel; + if (dwc_qh_is_non_per(qh)) { @@ -44480,7 +54571,6 @@ index 0000000..274967b + dwc_memset(qh->desc_list, 0x00, + sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); + } -+ DWC_SPINLOCK_FREE(channel_lock); +} + +/** @@ -45307,10 +55397,10 @@ index 0000000..274967b +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h new file mode 100644 -index 0000000..4823167 +index 0000000..fb57db0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -@@ -0,0 +1,412 @@ +@@ -0,0 +1,417 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $ + * $Revision: #12 $ @@ -45426,6 +55516,11 @@ index 0000000..4823167 + */ +extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); + ++/** This function is used to handle the fast interrupt ++ * ++ */ ++extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void); ++ +/** + * Returns private data set by + * dwc_otg_hcd_set_priv_data function. @@ -45725,10 +55820,10 @@ index 0000000..4823167 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c new file mode 100644 -index 0000000..b41e164 +index 0000000..8a31562 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -0,0 +1,2106 @@ +@@ -0,0 +1,2713 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $ + * $Revision: #89 $ @@ -45766,44 +55861,99 @@ index 0000000..b41e164 +#include "dwc_otg_hcd.h" +#include "dwc_otg_regs.h" + ++#include ++#include ++#include ++ ++ +extern bool microframe_schedule; + +/** @file + * This file contains the implementation of the HCD Interrupt handlers. + */ + ++int fiq_done, int_done; ++ ++#ifdef FIQ_DEBUG ++char buffer[1000*16]; ++int wptr; ++void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) ++{ ++ FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB; ++ va_list args; ++ char text[17]; ++ hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; ++ ++ if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) ++ { ++ local_fiq_disable(); ++ snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); ++ va_start(args, fmt); ++ vsnprintf(text+8, 9, fmt, args); ++ va_end(args); ++ ++ memcpy(buffer + wptr, text, 16); ++ wptr = (wptr + 16) % sizeof(buffer); ++ local_fiq_enable(); ++ } ++} ++#endif ++ +/** This function handles interrupts for the HCD. */ +int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) +{ + int retval = 0; -+ ++ static int last_time; + dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; + gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ hfnum_data_t hfnum; ++ haintmsk_data_t haintmsk; ++ +#ifdef DEBUG + dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; + -+ //GRAYG: debugging -+ if (NULL == global_regs) { -+ DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p " -+ "core_if=%p\n", -+ dwc_otg_hcd, global_regs); -+ return retval; -+ } +#endif + ++ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); ++ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); ++ + /* Exit from ISR if core is hibernated */ + if (core_if->hibernation_suspend == 1) { -+ return retval; ++ goto exit_handler_routine; + } + DWC_SPINLOCK(dwc_otg_hcd->lock); + /* Check if HOST Mode */ + if (dwc_otg_is_host_mode(core_if)) { -+ gintsts.d32 = dwc_otg_read_core_intr(core_if); -+ if (!gintsts.d32) { -+ DWC_SPINUNLOCK(dwc_otg_hcd->lock); -+ return 0; ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ /* Pull in from the FIQ's disabled mask */ ++ gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); ++ dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; + } ++ ++ if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) { ++ gintsts.b.hcintr = 1; ++ } ++ ++ /* Danger will robinson: fake a SOF if necessary */ ++ if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) { ++ gintsts.b.sofintr = 1; ++ } ++ gintsts.d32 &= gintmsk.d32; ++ ++ if (fiq_enable) { ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } ++ ++ if (!gintsts.d32) { ++ goto exit_handler_routine; ++ } ++ +#ifdef DEBUG ++ // We should be OK doing this because the common interrupts should already have been serviced + /* Don't print debug message in the interrupt handler on SOF */ +#ifndef DEBUG_SOF + if (gintsts.d32 != DWC_SOF_INTR_MASK) @@ -45819,10 +55969,11 @@ index 0000000..b41e164 + "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n", + gintsts.d32, core_if); +#endif -+ ++ hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); + if (gintsts.b.sofintr) { + retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); + } ++ + if (gintsts.b.rxstsqlvl) { + retval |= + dwc_otg_hcd_handle_rx_status_q_level_intr @@ -45837,7 +55988,18 @@ index 0000000..b41e164 + /** @todo Implement i2cintr handler. */ + } + if (gintsts.b.portintr) { ++ ++ gintmsk_data_t gintmsk = { .b.portintr = 1}; + retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ } + } + if (gintsts.b.hcintr) { + retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); @@ -45869,11 +56031,56 @@ index 0000000..b41e164 +#endif + + } ++ ++exit_handler_routine: ++ if (fiq_enable) { ++ gintmsk_data_t gintmsk_new; ++ haintmsk_data_t haintmsk_new; ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; ++ if(fiq_fsm_enable) ++ haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; ++ else ++ haintmsk_new.d32 = 0x0000FFFF; ++ ++ /* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */ ++ if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) { ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16)); ++ if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) { ++ fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR"); ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16))); ++ while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17))) ++ ; ++ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31)); ++ dwc_otg_hcd->fiq_state->mphi_int_count = 0; ++ } ++ int_done++; ++ } ++ haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); ++ /* Re-enable interrupts that the FIQ masked (first time round) */ ++ FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ ++ if ((jiffies / HZ) > last_time) { ++ //dwc_otg_qh_t *qh; ++ //dwc_list_link_t *cur; ++ /* Once a second output the fiq and irq numbers, useful for debug */ ++ last_time = jiffies / HZ; ++ // DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d", ++ // dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels, ++ // dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done); ++ //printk(KERN_WARNING "Periodic queues:\n"); ++ } ++ } ++ + DWC_SPINUNLOCK(dwc_otg_hcd->lock); + return retval; +} + +#ifdef DWC_TRACK_MISSED_SOFS ++ +#warning Compiling code to track missed SOFs +#define FRAME_NUM_ARRAY_SIZE 1000 +/** @@ -45916,10 +56123,12 @@ index 0000000..b41e164 +int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) +{ + hfnum_data_t hfnum; ++ gintsts_data_t gintsts = { .d32 = 0 }; + dwc_list_link_t *qh_entry; + dwc_otg_qh_t *qh; + dwc_otg_transaction_type_e tr_type; -+ gintsts_data_t gintsts = {.d32 = 0 }; ++ int did_something = 0; ++ int32_t next_sched_frame = -1; + + hfnum.d32 = + DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); @@ -45943,23 +56152,38 @@ index 0000000..b41e164 + qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); + qh_entry = qh_entry->next; + if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { ++ + /* + * Move QH to the ready list to be executed next + * (micro)frame. + */ + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, + &qh->qh_list_entry); ++ ++ did_something = 1; ++ } ++ else ++ { ++ if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame)) ++ { ++ next_sched_frame = qh->sched_frame; ++ } + } + } ++ if (fiq_enable) ++ hcd->fiq_state->next_sched_frame = next_sched_frame; ++ + tr_type = dwc_otg_hcd_select_transactions(hcd); + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ did_something = 1; + } + -+ /* Clear interrupt */ -+ gintsts.b.sofintr = 1; -+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -+ ++ /* Clear interrupt - but do not trample on the FIQ sof */ ++ if (!fiq_fsm_enable) { ++ gintsts.b.sofintr = 1; ++ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ } + return 1; +} + @@ -46235,12 +56459,25 @@ index 0000000..b41e164 +{ + int i; + int retval = 0; -+ haint_data_t haint; ++ haint_data_t haint = { .d32 = 0 } ; + + /* Clear appropriate bits in HCINTn to clear the interrupt bit in + * GINTSTS */ + -+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ if (!fiq_fsm_enable) ++ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ ++ // Overwrite with saved interrupts from fiq handler ++ if(fiq_fsm_enable) ++ { ++ /* check the mask? */ ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); ++ haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); ++ dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; ++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } + + for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { + if (haint.b2.chint & (1 << i)) { @@ -46282,7 +56519,8 @@ index 0000000..b41e164 + *short_read = (hctsiz.b.xfersize != 0); + } + } else if (hc->qh->do_split) { -+ length = qtd->ssplit_out_xfer_count; ++ //length = split_out_xfersize[hc->hc_num]; ++ length = qtd->ssplit_out_xfer_count; + } else { + length = hc->xfer_len; + } @@ -46326,7 +56564,6 @@ index 0000000..b41e164 + DWC_OTG_HC_XFER_COMPLETE, + &short_read); + -+ + /* non DWORD-aligned buffer case handling. */ + if (hc->align_buff && xfer_length && hc->ep_is_in) { + dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, @@ -46528,11 +56765,22 @@ index 0000000..b41e164 + dwc_otg_transaction_type_e tr_type; + int free_qtd; + dwc_irqflags_t flags; -+ dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); ++ dwc_spinlock_t *channel_lock = hcd->channel_lock; ++ ++ int hog_port = 0; + + DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", + __func__, hc->hc_num, halt_status, hc->xfer_len); + ++ if(fiq_fsm_enable && hc->do_split) { ++ if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { ++ if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || ++ hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { ++ hog_port = 0; ++ } ++ } ++ } ++ + switch (halt_status) { + case DWC_OTG_HC_XFER_URB_COMPLETE: + free_qtd = 1; @@ -46585,6 +56833,8 @@ index 0000000..b41e164 + * function clears the channel interrupt enables and conditions, so + * there's no need to clear the Channel Halted interrupt separately. + */ ++ if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH) ++ dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num); + dwc_otg_hc_cleanup(hcd->core_if, hc); + DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); + @@ -46607,6 +56857,7 @@ index 0000000..b41e164 + + DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); + hcd->available_host_channels++; ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels); + DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); + } + @@ -46615,7 +56866,6 @@ index 0000000..b41e164 + if (tr_type != DWC_OTG_TRANSACTION_NONE) { + dwc_otg_hcd_queue_transactions(hcd, tr_type); + } -+ DWC_SPINLOCK_FREE(channel_lock); +} + +/** @@ -46653,7 +56903,15 @@ index 0000000..b41e164 + * be processed. + */ + gintmsk.b.nptxfempty = 1; -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } + } else { + /* + * Move the QH from the periodic queued schedule to @@ -46670,7 +56928,15 @@ index 0000000..b41e164 + * processed. + */ + gintmsk.b.ptxfempty = 1; -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } + } + } +} @@ -47026,6 +57292,17 @@ index 0000000..b41e164 + "NAK Received--\n", hc->hc_num); + + /* ++ * When we get bulk NAKs then remember this so we holdoff on this qh until ++ * the beginning of the next frame ++ */ ++ switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_BULK: ++ case UE_CONTROL: ++ if (nak_holdoff && qtd->qh->do_split) ++ hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); ++ } ++ ++ /* + * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and + * interrupt. Re-start the SSPLIT transfer. + */ @@ -47047,7 +57324,11 @@ index 0000000..b41e164 + * transfers in DMA mode for the sole purpose of + * resetting the error count after a transaction error + * occurs. The core will continue transferring data. ++ * Disable other interrupts unmasked for the same ++ * reason. + */ ++ disable_hc_int(hc_regs, datatglerr); ++ disable_hc_int(hc_regs, ack); + qtd->error_count = 0; + goto handle_nak_done; + } @@ -47159,6 +57440,15 @@ index 0000000..b41e164 + halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); + } + } else { ++ /* ++ * An unmasked ACK on a non-split DMA transaction is ++ * for the sole purpose of resetting error counts. Disable other ++ * interrupts unmasked for the same reason. ++ */ ++ if(hcd->core_if->dma_enable) { ++ disable_hc_int(hc_regs, datatglerr); ++ disable_hc_int(hc_regs, nak); ++ } + qtd->error_count = 0; + + if (hc->qh->ping_state) { @@ -47221,8 +57511,10 @@ index 0000000..b41e164 + hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { + int frnum = dwc_otg_hcd_get_frame_number(hcd); + ++ // With the FIQ running we only ever see the failed NYET + if (dwc_full_frame_num(frnum) != -+ dwc_full_frame_num(hc->qh->sched_frame)) { ++ dwc_full_frame_num(hc->qh->sched_frame) || ++ fiq_fsm_enable) { + /* + * No longer in the same full speed frame. + * Treat this as a transaction error. @@ -47509,13 +57801,28 @@ index 0000000..b41e164 + dwc_otg_qtd_t * qtd) +{ + DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " -+ "Data Toggle Error--\n", hc->hc_num); ++ "Data Toggle Error on %s transfer--\n", ++ hc->hc_num, (hc->ep_is_in ? "IN" : "OUT")); + -+ if (hc->ep_is_in) { ++ /* Data toggles on split transactions cause the hc to halt. ++ * restart transfer */ ++ if(hc->qh->do_split) ++ { ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ } else if (hc->ep_is_in) { ++ /* An unmasked data toggle error on a non-split DMA transaction is ++ * for the sole purpose of resetting error counts. Disable other ++ * interrupts unmasked for the same reason. ++ */ ++ if(hcd->core_if->dma_enable) { ++ disable_hc_int(hc_regs, ack); ++ disable_hc_int(hc_regs, nak); ++ } + qtd->error_count = 0; -+ } else { -+ DWC_ERROR("Data Toggle Error on OUT transfer," -+ "channel %d\n", hc->hc_num); + } + + disable_hc_int(hc_regs, datatglerr); @@ -47595,10 +57902,9 @@ index 0000000..b41e164 + dwc_otg_hc_regs_t * hc_regs, + dwc_otg_qtd_t * qtd) +{ ++ int out_nak_enh = 0; + hcint_data_t hcint; + hcintmsk_data_t hcintmsk; -+ int out_nak_enh = 0; -+ + /* For core with OUT NAK enhancement, the flow for high- + * speed CONTROL/BULK OUT is handled a little differently. + */ @@ -47628,6 +57934,7 @@ index 0000000..b41e164 + } + + /* Read the HCINTn register to determine the cause for the halt. */ ++ + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); + @@ -47668,6 +57975,8 @@ index 0000000..b41e164 + handle_hc_babble_intr(hcd, hc, hc_regs, qtd); + } else if (hcint.b.frmovrun) { + handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.datatglerr) { ++ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); + } else if (!out_nak_enh) { + if (hcint.b.nyet) { + /* @@ -47717,12 +58026,24 @@ index 0000000..b41e164 + DWC_READ_REG32(&hcd-> + core_if->core_global_regs-> + gintsts)); ++ /* Failthrough: use 3-strikes rule */ ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); + } + + } + } else { + DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", + hcint.d32); ++ /* Failthrough: use 3-strikes rule */ ++ qtd->error_count++; ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); + } +} + @@ -47759,6 +58080,361 @@ index 0000000..b41e164 + return 1; +} + ++ ++/** ++ * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on ++ * FIQ transfer completion ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * 1. Un-mangle the status as recorded in each iso_frame_desc status ++ * 2. Copy it from the dwc_otg_urb into the real URB ++ */ ++void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) ++{ ++ struct dwc_otg_hcd_urb *dwc_urb = qtd->urb; ++ int nr_frames = dwc_urb->packet_count; ++ int i; ++ hcint_data_t frame_hcint; ++ ++ for (i = 0; i < nr_frames; i++) { ++ frame_hcint.d32 = dwc_urb->iso_descs[i].status; ++ if (frame_hcint.b.xfercomp) { ++ dwc_urb->iso_descs[i].status = 0; ++ dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length; ++ } else if (frame_hcint.b.frmovrun) { ++ if (qh->ep_is_in) ++ dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES; ++ else ++ dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else if (frame_hcint.b.xacterr) { ++ dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else if (frame_hcint.b.bblerr) { ++ dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW; ++ dwc_urb->error_count++; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ } else { ++ /* Something went wrong */ ++ dwc_urb->iso_descs[i].status = -1; ++ dwc_urb->iso_descs[i].actual_length = 0; ++ dwc_urb->error_count++; ++ } ++ } ++ //printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n", ++ // __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count); ++ hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0); ++ release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++} ++ ++/** ++ * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state. ++ * Returns total length of data or -1 if the buffers were not used. ++ * ++ */ ++int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) ++{ ++ dwc_hc_t *hc = qh->channel; ++ struct fiq_dma_blob *blob = hcd->fiq_dmab; ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ uint8_t *ptr = NULL; ++ int index = 0, len = 0; ++ int i = 0; ++ if (hc->ep_is_in) { ++ /* Copy data out of the DMA bounce buffers to the URB's buffer. ++ * The align_buf is ignored as this is ignored on FSM enqueue. */ ++ ptr = qtd->urb->buf; ++ if (qh->ep_type == UE_ISOCHRONOUS) { ++ /* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */ ++ index = qtd->isoc_frame_index; ++ ptr += qtd->urb->iso_descs[index].offset; ++ } else { ++ /* Need to increment by actual_length for interrupt IN */ ++ ptr += qtd->urb->actual_length; ++ } ++ ++ for (i = 0; i < st->dma_info.index; i++) { ++ len += st->dma_info.slot_len[i]; ++ dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]); ++ ptr += st->dma_info.slot_len[i]; ++ } ++ return len; ++ } else { ++ /* OUT endpoints - nothing to do. */ ++ return -1; ++ } ++ ++} ++/** ++ * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt ++ * from a channel handled in the FIQ ++ * @hcd: Pointer to dwc_otg_hcd struct ++ * @num: Host channel number ++ * ++ * If a host channel interrupt was received by the IRQ and this was a channel ++ * used by the FIQ, the execution flow for transfer completion is substantially ++ * different from the normal (messy) path. This function and its friends handles ++ * channel cleanup and transaction completion from a FIQ transaction. ++ */ ++void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) ++{ ++ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; ++ dwc_hc_t *hc = hcd->hc_ptr_array[num]; ++ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); ++ dwc_otg_qh_t *qh = hc->qh; ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; ++ hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; ++ int hostchannels = 0; ++ fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); ++ ++ hostchannels = hcd->available_host_channels; ++ switch (st->fsm) { ++ case FIQ_TEST: ++ break; ++ ++ case FIQ_DEQUEUE_ISSUED: ++ /* hc_halt was called. QTD no longer exists. */ ++ /* TODO: for a nonperiodic split transaction, need to issue a ++ * CLEAR_TT_BUFFER hub command if we were in the start-split phase. ++ */ ++ release_channel(hcd, hc, NULL, hc->halt_status); ++ break; ++ ++ case FIQ_NP_SPLIT_DONE: ++ /* Nonperiodic transaction complete. */ ++ if (!hc->ep_is_in) { ++ qtd->ssplit_out_xfer_count = hc->xfer_len; ++ } ++ if (hcint.b.xfercomp) { ++ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.nak) { ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } ++ break; ++ ++ case FIQ_NP_SPLIT_HS_ABORTED: ++ /* A HS abort is a 3-strikes on the HS bus at any point in the transaction. ++ * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that ++ * because there's no guarantee which order a non-periodic split happened in. ++ * We could end up clearing a perfectly good transaction out of the buffer. ++ */ ++ if (hcint.b.xacterr) { ++ qtd->error_count += st->nr_errors; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ local_fiq_disable(); ++ BUG(); ++ } ++ break; ++ ++ case FIQ_NP_SPLIT_LS_ABORTED: ++ /* A few cases can cause this - either an unknown state on a SSPLIT or ++ * STALL/data toggle error response on a CSPLIT */ ++ if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.datatglerr) { ++ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ local_fiq_disable(); ++ BUG(); ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_DONE: ++ /* Isoc IN or Interrupt IN/OUT */ ++ ++ /* Flow control here is different from the normal execution by the driver. ++ * We need to completely ignore most of the driver's method of handling ++ * split transactions and do it ourselves. ++ */ ++ if (hc->ep_type == UE_INTERRUPT) { ++ if (hcint.b.nak) { ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } else if (hc->ep_is_in) { ++ int len; ++ len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num); ++ //printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length); ++ qtd->urb->actual_length += len; ++ if (qtd->urb->actual_length >= qtd->urb->length) { ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ /* Interrupt transfer not complete yet - is it a short read? */ ++ if (len < hc->max_packet) { ++ /* Interrupt transaction complete */ ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ /* Further transactions required */ ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ } else { ++ /* Interrupt OUT complete. */ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ qtd->urb->actual_length += hc->xfer_len; ++ if (qtd->urb->actual_length >= qtd->urb->length) { ++ qtd->urb->status = 0; ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ } else { ++ /* ISOC IN complete. */ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ int len = 0; ++ /* Record errors, update qtd. */ ++ if (st->nr_errors) { ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } else { ++ frame_desc->status = 0; ++ /* Unswizzle dma */ ++ len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num); ++ frame_desc->actual_length = len; ++ } ++ qtd->isoc_frame_index++; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ break; ++ ++ case FIQ_PER_ISO_OUT_DONE: { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ if (st->nr_errors) { ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } else { ++ frame_desc->status = 0; ++ frame_desc->actual_length = frame_desc->length; ++ } ++ qtd->isoc_frame_index++; ++ qtd->isoc_split_offset = 0; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_NYET_ABORTED: ++ /* Doh. lost the data. */ ++ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " ++ "- FIQ reported NYET. Data may have been lost.\n", ++ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); ++ if (hc->ep_type == UE_ISOCHRONOUS) { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ frame_desc->actual_length = 0; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ qtd->isoc_frame_index++; ++ qtd->isoc_split_offset = 0; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ case FIQ_HS_ISOC_DONE: ++ /* The FIQ has performed a whole pile of isochronous transactions. ++ * The status is recorded as the interrupt state should the transaction ++ * fail. ++ */ ++ dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num); ++ break; ++ ++ case FIQ_PER_SPLIT_LS_ABORTED: ++ if (hcint.b.xacterr) { ++ /* Hub has responded with an ERR packet. Device ++ * has been unplugged or the port has been disabled. ++ * TODO: need to issue a reset to the hub port. */ ++ qtd->error_count += 3; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed " ++ "- FIQ reported FSM=%d. Data may have been lost.\n", ++ st->fsm, hc->dev_addr, hc->ep_num); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ case FIQ_PER_SPLIT_HS_ABORTED: ++ /* Either the SSPLIT phase suffered transaction errors or something ++ * unexpected happened. ++ */ ++ qtd->error_count += 3; ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ break; ++ ++ case FIQ_PER_SPLIT_TIMEOUT: ++ /* Couldn't complete in the nominated frame */ ++ printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " ++ "- FIQ timed out. Data may have been lost.\n", ++ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); ++ if (hc->ep_type == UE_ISOCHRONOUS) { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ /* Record errors, update qtd. */ ++ frame_desc->actual_length = 0; ++ if (hc->ep_is_in) { ++ frame_desc->status = -DWC_E_NO_STREAM_RES; ++ } else { ++ frame_desc->status = -DWC_E_COMMUNICATION; ++ } ++ qtd->isoc_frame_index++; ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); ++ } ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ break; ++ ++ default: ++ DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x", ++ hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num); ++ qtd->error_count++; ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ return; ++} ++ +/** Handles interrupt for a specific Host Channel */ +int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) +{ @@ -47773,15 +58449,41 @@ index 0000000..b41e164 + + hc = dwc_otg_hcd->hc_ptr_array[num]; + hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; ++ if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ /* We are responding to a channel disable. Driver ++ * state is cleared - our qtd has gone away. ++ */ ++ release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); ++ return 1; ++ } + qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); + ++ /* ++ * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ. ++ * Execution path is fundamentally different for the channels after a FIQ has completed ++ * a split transaction. ++ */ ++ if (fiq_fsm_enable) { ++ switch (dwc_otg_hcd->fiq_state->channel[num].fsm) { ++ case FIQ_PASSTHROUGH: ++ break; ++ case FIQ_PASSTHROUGH_ERRORSTATE: ++ /* Hook into the error count */ ++ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); ++ if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) { ++ qtd->error_count = 0; ++ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); ++ } ++ break; ++ default: ++ dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num); ++ return 1; ++ } ++ } ++ + hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); + hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ DWC_DEBUGPL(DBG_HCDV, -+ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", -+ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); + hcint.d32 = hcint.d32 & hcintmsk.d32; -+ + if (!dwc_otg_hcd->core_if->dma_enable) { + if (hcint.b.chhltd && hcint.d32 != 0x2) { + hcint.b.chhltd = 0; @@ -47811,7 +58513,8 @@ index 0000000..b41e164 + retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); + } + if (hcint.b.ack) { -+ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ if(!hcint.b.chhltd) ++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); + } + if (hcint.b.nyet) { + retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); @@ -47833,14 +58536,14 @@ index 0000000..b41e164 + + return retval; +} -+ +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c new file mode 100644 -index 0000000..e4787f5 +index 0000000..1d28459 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -0,0 +1,893 @@ +@@ -0,0 +1,994 @@ ++ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ + * $Revision: #20 $ @@ -47893,12 +58596,14 @@ index 0000000..e4787f5 +#include +#include +#include ++#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) +#include <../drivers/usb/core/hcd.h> +#else +#include +#endif ++#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) +#define USB_URB_EP_LINKING 1 @@ -47910,6 +58615,9 @@ index 0000000..e4787f5 +#include "dwc_otg_dbg.h" +#include "dwc_otg_driver.h" +#include "dwc_otg_hcd.h" ++ ++extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end; ++ +/** + * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is + * qualified with its direction (possible 32 endpoints per device). @@ -47919,6 +58627,8 @@ index 0000000..e4787f5 + +static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; + ++extern bool fiq_enable; ++ +/** @name Linux HC Driver API Functions */ +/** @{ */ +/* manage i/o requests, device state */ @@ -48102,13 +58812,15 @@ index 0000000..e4787f5 + +/** + * Sets the final status of an URB and returns it to the device driver. Any -+ * required cleanup of the URB is performed. ++ * required cleanup of the URB is performed. The HCD lock should be held on ++ * entry. + */ +static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, + dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) +{ + struct urb *urb = (struct urb *)urb_handle; -+ ++ urb_tq_entry_t *new_entry; ++ int rc = 0; + if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { + DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", + __func__, urb, usb_pipedevice(urb->pipe), @@ -48122,7 +58834,7 @@ index 0000000..e4787f5 + } + } + } -+ ++ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t)); + urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); + /* Convert status value. */ + switch (status) { @@ -48144,6 +58856,9 @@ index 0000000..e4787f5 + case -DWC_E_OVERFLOW: + status = -EOVERFLOW; + break; ++ case -DWC_E_SHUTDOWN: ++ status = -ESHUTDOWN; ++ break; + default: + if (status) { + DWC_PRINTF("Uknown urb status %d\n", status); @@ -48183,20 +58898,34 @@ index 0000000..e4787f5 + urb); + } + } -+ + DWC_FREE(dwc_otg_urb); -+ ++ if (!new_entry) { ++ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); ++ urb->status = -EPROTO; ++ /* don't schedule the tasklet - ++ * directly return the packet here with error. */ +#if USB_URB_EP_LINKING -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); +#endif -+ DWC_SPINUNLOCK(hcd->lock); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); +#else -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); +#endif -+ DWC_SPINLOCK(hcd->lock); -+ ++ } else { ++ new_entry->urb = urb; ++#if USB_URB_EP_LINKING ++ rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); ++ if(0 == rc) { ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); ++ } ++#endif ++ if(0 == rc) { ++ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, ++ urb_tq_entries); ++ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet); ++ } ++ } + return 0; +} + @@ -48209,6 +58938,62 @@ index 0000000..e4787f5 + .get_b_hnp_enable = _get_b_hnp_enable, +}; + ++static struct fiq_handler fh = { ++ .name = "usb_fiq", ++}; ++ ++static void hcd_init_fiq(void *cookie) ++{ ++ dwc_otg_device_t *otg_dev = cookie; ++ dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; ++ struct pt_regs regs; ++ ++ if (claim_fiq(&fh)) { ++ DWC_ERROR("Can't claim FIQ"); ++ BUG(); ++ } ++ DWC_WARN("FIQ on core %d at 0x%08x", ++ smp_processor_id(), ++ (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); ++ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); ++ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); ++ memset(®s,0,sizeof(regs)); ++ ++ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; ++ if (fiq_fsm_enable) { ++ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; ++ //regs.ARM_r10 = dwc_otg_hcd->dma; ++ regs.ARM_fp = (long) dwc_otg_fiq_fsm; ++ } else { ++ regs.ARM_fp = (long) dwc_otg_fiq_nop; ++ } ++ ++ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); ++ ++// __show_regs(®s); ++ set_fiq_regs(®s); ++ ++ //Set the mphi periph to the required registers ++ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; ++ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; ++ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; ++ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; ++ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; ++ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; ++ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); ++ //Enable mphi peripheral ++ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); ++#ifdef DEBUG ++ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) ++ DWC_WARN("MPHI periph has been enabled"); ++ else ++ DWC_WARN("MPHI periph has NOT been enabled"); ++#endif ++ // Enable FIQ interrupt from USB peripheral ++ enable_fiq(INTERRUPT_VC_USB); ++ local_fiq_enable(); ++} ++ +/** + * Initializes the HCD. This function allocates memory for and initializes the + * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the @@ -48258,6 +59043,7 @@ index 0000000..e4787f5 + + hcd->regs = otg_dev->os_dep.base; + ++ + /* Initialize the DWC OTG HCD. */ + dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); + if (!dwc_otg_hcd) { @@ -48271,6 +59057,15 @@ index 0000000..e4787f5 + goto error2; + } + ++ if (fiq_enable) { ++ if (num_online_cpus() > 1) { ++ /* bcm2709: can run the FIQ on a separate core to IRQs */ ++ smp_call_function_single(1, hcd_init_fiq, otg_dev, 1); ++ } else { ++ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); ++ } ++ } ++ + otg_dev->hcd->otg_dev = otg_dev; + hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) @@ -48286,9 +59081,9 @@ index 0000000..e4787f5 + * IRQ line, and calls hcd_start method. + */ +#ifdef PLATFORM_INTERFACE -+ retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED | IRQF_DISABLED); ++ retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED | IRQF_DISABLED); +#else -+ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); ++ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); +#endif + if (retval < 0) { + goto error2; @@ -48385,9 +59180,13 @@ index 0000000..e4787f5 +/** Returns the current frame number. */ +static int get_frame_number(struct usb_hcd *hcd) +{ ++ hprt0_data_t hprt0; + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); -+ -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); ++ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3; ++ else ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); +} + +#ifdef DEBUG @@ -48450,9 +59249,7 @@ index 0000000..e4787f5 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) + struct usb_host_endpoint *ep = urb->ep; +#endif -+#if USB_URB_EP_LINKING + dwc_irqflags_t irqflags; -+#endif + void **ref_ep_hcpriv = &ep->hcpriv; + dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); + dwc_otg_hcd_urb_t *dwc_otg_urb; @@ -48504,9 +59301,8 @@ index 0000000..e4787f5 + if(dwc_otg_urb == NULL) + return -ENOMEM; + -+ urb->hcpriv = dwc_otg_urb; -+ if (!dwc_otg_urb && urb->number_of_packets) -+ return -ENOMEM; ++ if (!dwc_otg_urb && urb->number_of_packets) ++ return -ENOMEM; + + dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), ep_type, @@ -48546,37 +59342,42 @@ index 0000000..e4787f5 + iso_frame_desc[i].length); + } + ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); ++ urb->hcpriv = dwc_otg_urb; +#if USB_URB_EP_LINKING -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); + retval = usb_hcd_link_urb_to_ep(hcd, urb); -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); + if (0 == retval) +#endif -+ { -+ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -+ /*(dwc_otg_qh_t **)*/ -+ ref_ep_hcpriv, -+ mem_flags == GFP_ATOMIC ? 1 : 0); -+ if (0 == retval) { -+ if (alloc_bandwidth) { -+ allocate_bus_bandwidth(hcd, -+ dwc_otg_hcd_get_ep_bandwidth( -+ dwc_otg_hcd, *ref_ep_hcpriv), -+ urb); -+ } -+ } else { ++ { ++ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, ++ /*(dwc_otg_qh_t **)*/ ++ ref_ep_hcpriv, 1); ++ if (0 == retval) { ++ if (alloc_bandwidth) { ++ allocate_bus_bandwidth(hcd, ++ dwc_otg_hcd_get_ep_bandwidth( ++ dwc_otg_hcd, *ref_ep_hcpriv), ++ urb); ++ } ++ } else { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); +#if USB_URB_EP_LINKING -+ dwc_irqflags_t irqflags; -+ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -+ usb_hcd_unlink_urb_from_ep(hcd, urb); -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); ++ usb_hcd_unlink_urb_from_ep(hcd, urb); +#endif -+ if (retval == -DWC_E_NO_DEVICE) { -+ retval = -ENODEV; -+ } -+ } -+ } ++ DWC_FREE(dwc_otg_urb); ++ urb->hcpriv = NULL; ++ if (retval == -DWC_E_NO_DEVICE) ++ retval = -ENODEV; ++ } ++ } ++#if USB_URB_EP_LINKING ++ else ++ { ++ DWC_FREE(dwc_otg_urb); ++ urb->hcpriv = NULL; ++ } ++#endif ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); + return retval; +} + @@ -48620,6 +59421,8 @@ index 0000000..e4787f5 + usb_hcd_unlink_urb_from_ep(hcd, urb); +#endif + DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++ ++ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) + usb_hcd_giveback_urb(hcd, urb); +#else @@ -48736,10 +59539,10 @@ index 0000000..e4787f5 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c new file mode 100644 -index 0000000..0a1cbb7 +index 0000000..acd0dd7 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -0,0 +1,923 @@ +@@ -0,0 +1,957 @@ +/* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $ + * $Revision: #44 $ @@ -48924,6 +59727,7 @@ index 0000000..0a1cbb7 + if (microframe_schedule) + qh->speed = dev_speed; + ++ qh->nak_frame = 0xffff; + + if (((dev_speed == USB_SPEED_LOW) || + (dev_speed == USB_SPEED_FULL)) && @@ -48933,6 +59737,7 @@ index 0000000..0a1cbb7 + dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, + hub_port); + qh->do_split = 1; ++ qh->skip_count = 0; + } + + if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { @@ -49315,6 +60120,8 @@ index 0000000..0a1cbb7 + return status; +} + ++ ++ +/** + * Schedules an interrupt or isochronous transfer in the periodic schedule. + * @@ -49373,8 +60180,13 @@ index 0000000..0a1cbb7 + DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); + } + else { -+ /* Always start in the inactive schedule. */ -+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); ++ if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame))) ++ { ++ hcd->fiq_state->next_sched_frame = qh->sched_frame; ++ ++ } ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); + } + + if (!microframe_schedule) { @@ -49388,6 +60200,7 @@ index 0000000..0a1cbb7 + return status; +} + ++ +/** + * This function adds a QH to either the non periodic or periodic schedule if + * it is not already in the schedule. If the QH is already in the schedule, no @@ -49410,12 +60223,20 @@ index 0000000..0a1cbb7 + /* Always start in the inactive schedule. */ + DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, + &qh->qh_list_entry); ++ //hcd->fiq_state->kick_np_queues = 1; + } else { + status = schedule_periodic(hcd, qh); + if ( !hcd->periodic_qh_count ) { + intr_mask.b.sofintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -+ intr_mask.d32, intr_mask.d32); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ } + } + hcd->periodic_qh_count++; + } @@ -49469,13 +60290,22 @@ index 0000000..0a1cbb7 + hcd->non_periodic_qh_ptr->next; + } + DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) ++ // hcd->fiq_state->kick_np_queues = 1; + } else { + deschedule_periodic(hcd, qh); + hcd->periodic_qh_count--; -+ if( !hcd->periodic_qh_count ) { ++ if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { + intr_mask.b.sofintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -+ intr_mask.d32, 0); ++ if (fiq_enable) { ++ local_fiq_disable(); ++ fiq_fsm_spin_lock(&hcd->fiq_state->lock); ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); ++ local_fiq_enable(); ++ } else { ++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ } + } + } +} @@ -49501,6 +60331,7 @@ index 0000000..0a1cbb7 + if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { + /* Add back to inactive non-periodic schedule. */ + dwc_otg_hcd_qh_add(hcd, qh); ++ //hcd->fiq_state->kick_np_queues = 1; + } + } else { + uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); @@ -49510,6 +60341,7 @@ index 0000000..0a1cbb7 + if (sched_next_periodic_split) { + + qh->sched_frame = frame_number; ++ + if (dwc_frame_num_le(frame_number, + dwc_frame_num_inc + (qh->start_split_frame, @@ -49558,6 +60390,11 @@ index 0000000..0a1cbb7 + DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, + &qh->qh_list_entry); + } else { ++ if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame)) ++ { ++ hcd->fiq_state->next_sched_frame = qh->sched_frame; ++ } ++ + DWC_LIST_MOVE_HEAD + (&hcd->periodic_sched_inactive, + &qh->qh_list_entry); @@ -49622,6 +60459,7 @@ index 0000000..0a1cbb7 + * QH to place the QTD into. If it does not find a QH, then it will create a + * new QH. If the QH to which the QTD is added is not currently scheduled, it + * is placed into the proper schedule based on its EP type. ++ * HCD lock must be held and interrupts must be disabled on entry + * + * @param[in] qtd The QTD to add + * @param[in] hcd The DWC HCD structure @@ -49634,8 +60472,6 @@ index 0000000..0a1cbb7 + dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc) +{ + int retval = 0; -+ dwc_irqflags_t flags; -+ + dwc_otg_hcd_urb_t *urb = qtd->urb; + + /* @@ -49645,18 +60481,19 @@ index 0000000..0a1cbb7 + if (*qh == NULL) { + *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); + if (*qh == NULL) { -+ retval = -1; ++ retval = -DWC_E_NO_MEMORY; + goto done; ++ } else { ++ if (fiq_enable) ++ hcd->fiq_state->kick_np_queues = 1; + } + } -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); + retval = dwc_otg_hcd_qh_add(hcd, *qh); + if (retval == 0) { + DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, + qtd_list_entry); ++ qtd->qh = *qh; + } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ +done: + + return retval; @@ -49665,10 +60502,10 @@ index 0000000..0a1cbb7 +#endif /* DWC_DEVICE_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h new file mode 100644 -index 0000000..e46d9bb +index 0000000..6b2c7d0 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -@@ -0,0 +1,185 @@ +@@ -0,0 +1,188 @@ +#ifndef _DWC_OS_DEP_H_ +#define _DWC_OS_DEP_H_ + @@ -49768,6 +60605,9 @@ index 0000000..e46d9bb + /** Register offset for Diagnostic API */ + uint32_t reg_offset; + ++ /** Base address for MPHI peripheral */ ++ void *mphi_base; ++ +#ifdef LM_INTERFACE + struct lm_device *lmdev; +#elif defined(PCI_INTERFACE) @@ -53212,7 +64052,7 @@ index 0000000..c8d2e0e +#endif /* DWC_HOST_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c new file mode 100644 -index 0000000..1b1f83c +index 0000000..c8590b5 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c @@ -0,0 +1,5147 @@ @@ -57494,7 +68334,7 @@ index 0000000..1b1f83c + && (pcd->ep0state == EP0_OUT_DATA_PHASE)) + status.d32 = core_if->dev_if->out_desc_addr->status.d32; + if (pcd->ep0state == EP0_OUT_STATUS_PHASE) -+ status.d32 = status.d32 = core_if->dev_if-> ++ status.d32 = core_if->dev_if-> + out_desc_addr->status.d32; + + if (status.b.sr) { @@ -58365,10 +69205,10 @@ index 0000000..1b1f83c +#endif /* DWC_HOST_ONLY */ diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c new file mode 100644 -index 0000000..5d310df +index 0000000..4b32941 --- /dev/null +++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -@@ -0,0 +1,1358 @@ +@@ -0,0 +1,1360 @@ + /* ========================================================================== + * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $ + * $Revision: #21 $ @@ -58430,6 +69270,8 @@ index 0000000..5d310df +#include "dwc_otg_driver.h" +#include "dwc_otg_dbg.h" + ++extern bool fiq_enable; ++ +static struct gadget_wrapper { + dwc_otg_pcd_t *pcd; + @@ -59593,13 +70435,13 @@ index 0000000..5d310df + */ +#ifdef PLATFORM_INTERFACE + DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", -+ platform_get_irq(_dev, 0)); -+ retval = request_irq(platform_get_irq(_dev, 0), dwc_otg_pcd_irq, ++ platform_get_irq(_dev, fiq_enable ? 0 : 1)); ++ retval = request_irq(platform_get_irq(_dev, fiq_enable ? 0 : 1), dwc_otg_pcd_irq, + IRQF_SHARED, gadget_wrapper->gadget.name, + otg_dev->pcd); + if (retval != 0) { + DWC_ERROR("request of irq%d failed\n", -+ platform_get_irq(_dev, 0)); ++ platform_get_irq(_dev, fiq_enable ? 0 : 1)); + free_wrapper(gadget_wrapper); + return -EBUSY; + } @@ -62987,10 +73829,10 @@ index 0000000..cdc9963 +test_main(); +0; -From 21af740ba39591ca0b84bf217e5e17748c912ac8 Mon Sep 17 00:00:00 2001 +From d88e00c9d4c9ea9ff9c3d65ac79afe580c083d14 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:54:32 +0100 -Subject: [PATCH 005/172] bcm2708 watchdog driver +Subject: [PATCH 005/104] bcm2708 watchdog driver Signed-off-by: popcornmix --- @@ -63001,7 +73843,7 @@ Signed-off-by: popcornmix create mode 100644 drivers/watchdog/bcm2708_wdog.c diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig -index 08f41ad..3bc50f2 100644 +index 16f2023..ca7d6463 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -452,6 +452,12 @@ config RETU_WATCHDOG @@ -63010,7 +73852,7 @@ index 08f41ad..3bc50f2 100644 +config BCM2708_WDT + tristate "BCM2708 Watchdog" -+ depends on ARCH_BCM2708 ++ depends on ARCH_BCM2708 || ARCH_BCM2709 + help + Enables BCM2708 watchdog support. + @@ -63018,7 +73860,7 @@ index 08f41ad..3bc50f2 100644 tristate "MOXART watchdog" depends on ARCH_MOXART diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile -index c569ec8..10e0665 100644 +index 5c19294..aac60bd 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o @@ -63418,318 +74260,10 @@ index 0000000..8a27d68 +MODULE_ALIAS_MISCDEV(TEMP_MINOR); +MODULE_LICENSE("GPL"); -From 44deeada252c455ee3325a19e375016d60826802 Mon Sep 17 00:00:00 2001 -From: Harm Hanemaaijer -Date: Thu, 20 Jun 2013 20:21:39 +0200 -Subject: [PATCH 008/172] Speed up console framebuffer imageblit function - -Especially on platforms with a slower CPU but a relatively high -framebuffer fill bandwidth, like current ARM devices, the existing -console monochrome imageblit function used to draw console text is -suboptimal for common pixel depths such as 16bpp and 32bpp. The existing -code is quite general and can deal with several pixel depths. By creating -special case functions for 16bpp and 32bpp, by far the most common pixel -formats used on modern systems, a significant speed-up is attained -which can be readily felt on ARM-based devices like the Raspberry Pi -and the Allwinner platform, but should help any platform using the -fb layer. - -The special case functions allow constant folding, eliminating a number -of instructions including divide operations, and allow the use of an -unrolled loop, eliminating instructions with a variable shift size, -reducing source memory access instructions, and eliminating excessive -branching. These unrolled loops also allow much better code optimization -by the C compiler. The code that selects which optimized variant is used -is also simplified, eliminating integer divide instructions. - -The speed-up, measured by timing 'cat file.txt' in the console, varies -between 40% and 70%, when testing on the Raspberry Pi and Allwinner -ARM-based platforms, depending on font size and the pixel depth, with -the greater benefit for 32bpp. - -Signed-off-by: Harm Hanemaaijer ---- - drivers/video/fbdev/core/cfbimgblt.c | 152 +++++++++++++++++++++++++++++++++-- - 1 file changed, 147 insertions(+), 5 deletions(-) - -diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c -index a2bb276..436494f 100644 ---- a/drivers/video/fbdev/core/cfbimgblt.c -+++ b/drivers/video/fbdev/core/cfbimgblt.c -@@ -28,6 +28,11 @@ - * - * Also need to add code to deal with cards endians that are different than - * the native cpu endians. I also need to deal with MSB position in the word. -+ * Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013: -+ * - Provide optimized versions of fast_imageblit for 16 and 32bpp that are -+ * significantly faster than the previous implementation. -+ * - Simplify the fast/slow_imageblit selection code, avoiding integer -+ * divides. - */ - #include - #include -@@ -262,6 +267,133 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * - } - } - -+/* -+ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded -+ * into the code, main loop unrolled. -+ */ -+ -+static inline void fast_imageblit16(const struct fb_image *image, -+ struct fb_info *p, u8 __iomem * dst1, -+ u32 fgcolor, u32 bgcolor) -+{ -+ u32 fgx = fgcolor, bgx = bgcolor; -+ u32 spitch = (image->width + 7) / 8; -+ u32 end_mask, eorx; -+ const char *s = image->data, *src; -+ u32 __iomem *dst; -+ const u32 *tab = NULL; -+ int i, j, k; -+ -+ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; -+ -+ fgx <<= 16; -+ bgx <<= 16; -+ fgx |= fgcolor; -+ bgx |= bgcolor; -+ -+ eorx = fgx ^ bgx; -+ k = image->width / 2; -+ -+ for (i = image->height; i--;) { -+ dst = (u32 __iomem *) dst1; -+ src = s; -+ -+ j = k; -+ while (j >= 4) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 6) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 4) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 2) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[bits & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ src++; -+ j -= 4; -+ } -+ if (j != 0) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 6) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ if (j >= 2) { -+ end_mask = tab[(bits >> 4) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ if (j == 3) { -+ end_mask = tab[(bits >> 2) & 3]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst); -+ } -+ } -+ } -+ dst1 += p->fix.line_length; -+ s += spitch; -+ } -+} -+ -+/* -+ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded -+ * into the code, main loop unrolled. -+ */ -+ -+static inline void fast_imageblit32(const struct fb_image *image, -+ struct fb_info *p, u8 __iomem * dst1, -+ u32 fgcolor, u32 bgcolor) -+{ -+ u32 fgx = fgcolor, bgx = bgcolor; -+ u32 spitch = (image->width + 7) / 8; -+ u32 end_mask, eorx; -+ const char *s = image->data, *src; -+ u32 __iomem *dst; -+ const u32 *tab = NULL; -+ int i, j, k; -+ -+ tab = cfb_tab32; -+ -+ eorx = fgx ^ bgx; -+ k = image->width; -+ -+ for (i = image->height; i--;) { -+ dst = (u32 __iomem *) dst1; -+ src = s; -+ -+ j = k; -+ while (j >= 8) { -+ u8 bits = *src; -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 6) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 5) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 4) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 3) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 2) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[(bits >> 1) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ end_mask = tab[bits & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ src++; -+ j -= 8; -+ } -+ if (j != 0) { -+ u32 bits = (u32) * src; -+ while (j > 1) { -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); -+ bits <<= 1; -+ j--; -+ } -+ end_mask = tab[(bits >> 7) & 1]; -+ FB_WRITEL((end_mask & eorx) ^ bgx, dst); -+ } -+ dst1 += p->fix.line_length; -+ s += spitch; -+ } -+} -+ - void cfb_imageblit(struct fb_info *p, const struct fb_image *image) - { - u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; -@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) - bgcolor = image->bg_color; - } - -- if (32 % bpp == 0 && !start_index && !pitch_index && -- ((width & (32/bpp-1)) == 0) && -- bpp >= 8 && bpp <= 32) -- fast_imageblit(image, p, dst1, fgcolor, bgcolor); -- else -+ if (!start_index && !pitch_index) { -+ if (bpp == 32) -+ fast_imageblit32(image, p, dst1, fgcolor, -+ bgcolor); -+ else if (bpp == 16 && (width & 1) == 0) -+ fast_imageblit16(image, p, dst1, fgcolor, -+ bgcolor); -+ else if (bpp == 8 && (width & 3) == 0) -+ fast_imageblit(image, p, dst1, fgcolor, -+ bgcolor); -+ else -+ slow_imageblit(image, p, dst1, fgcolor, -+ bgcolor, -+ start_index, pitch_index); -+ } else - slow_imageblit(image, p, dst1, fgcolor, bgcolor, - start_index, pitch_index); - } else - -From 33bcff1a531c7d7810dfd4f81546ddc3bffadeba Mon Sep 17 00:00:00 2001 -From: Siarhei Siamashka -Date: Mon, 17 Jun 2013 13:32:11 +0300 -Subject: [PATCH 009/172] fbdev: add FBIOCOPYAREA ioctl - -Based on the patch authored by Ali Gholami Rudi at - https://lkml.org/lkml/2009/7/13/153 - -Provide an ioctl for userspace applications, but only if this operation -is hardware accelerated (otherwide it does not make any sense). - -Signed-off-by: Siarhei Siamashka ---- - drivers/video/fbdev/core/fbmem.c | 30 ++++++++++++++++++++++++++++++ - include/uapi/linux/fb.h | 5 +++++ - 2 files changed, 35 insertions(+) - -diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c -index 0705d88..771992a 100644 ---- a/drivers/video/fbdev/core/fbmem.c -+++ b/drivers/video/fbdev/core/fbmem.c -@@ -1084,6 +1084,25 @@ fb_blank(struct fb_info *info, int blank) - } - EXPORT_SYMBOL(fb_blank); - -+static int fb_copyarea_user(struct fb_info *info, -+ struct fb_copyarea *copy) -+{ -+ int ret = 0; -+ if (!lock_fb_info(info)) -+ return -ENODEV; -+ if (copy->dx + copy->width > info->var.xres || -+ copy->sx + copy->width > info->var.xres || -+ copy->dy + copy->height > info->var.yres || -+ copy->sy + copy->height > info->var.yres) { -+ ret = -EINVAL; -+ goto out; -+ } -+ info->fbops->fb_copyarea(info, copy); -+out: -+ unlock_fb_info(info); -+ return ret; -+} -+ - static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) - { -@@ -1094,6 +1113,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - struct fb_event event; -+ struct fb_copyarea copy; - void __user *argp = (void __user *)arg; - long ret = 0; - -@@ -1211,6 +1231,15 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unlock_fb_info(info); - console_unlock(); - break; -+ case FBIOCOPYAREA: -+ if (info->flags & FBINFO_HWACCEL_COPYAREA) { -+ /* only provide this ioctl if it is accelerated */ -+ if (copy_from_user(©, argp, sizeof(copy))) -+ return -EFAULT; -+ ret = fb_copyarea_user(info, ©); -+ break; -+ } -+ /* fall through */ - default: - if (!lock_fb_info(info)) - return -ENODEV; -@@ -1365,6 +1394,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, - case FBIOPAN_DISPLAY: - case FBIOGET_CON2FBMAP: - case FBIOPUT_CON2FBMAP: -+ case FBIOCOPYAREA: - arg = (unsigned long) compat_ptr(arg); - case FBIOBLANK: - ret = do_fb_ioctl(info, cmd, arg); -diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h -index fb795c3..fa72af0 100644 ---- a/include/uapi/linux/fb.h -+++ b/include/uapi/linux/fb.h -@@ -34,6 +34,11 @@ - #define FBIOPUT_MODEINFO 0x4617 - #define FBIOGET_DISPINFO 0x4618 - #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) -+/* -+ * HACK: use 'z' in order not to clash with any other ioctl numbers which might -+ * be concurrently added to the mainline kernel -+ */ -+#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea) - - #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ - #define FB_TYPE_PLANES 1 /* Non interleaved planes */ - -From 86c75685b2acfe9012480bb2301a818d3d0beea8 Mon Sep 17 00:00:00 2001 +From 5fd2663844abf7f14ef6ac8abbb26e117eb9e24f Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:55:09 +0100 -Subject: [PATCH 010/172] bcm2708 framebuffer driver +Subject: [PATCH 006/104] bcm2708 framebuffer driver Signed-off-by: popcornmix @@ -63773,6 +74307,8 @@ an interrupt instead for larger transfers. For small transfers, busy-waiting is still likely to be faster. Signed-off-by: Luke Diamand + +bcm2708: Make ioctl logging quieter --- arch/arm/mach-bcm2708/dma.c | 8 + arch/arm/mach-bcm2708/include/mach/dma.h | 2 + @@ -63821,7 +74357,7 @@ index f2568d4..a4aac4c 100644 /* When listing features we can ask for when allocating DMA channels give diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig -index 4916c97..95fcb7f 100644 +index b3dd417..b3768b4 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -224,6 +224,20 @@ config FB_TILEBLITTING @@ -63859,7 +74395,7 @@ index 1979aff..57181ad 100644 obj-$(CONFIG_FB_CLPS711X) += clps711x-fb.o diff --git a/drivers/video/fbdev/bcm2708_fb.c b/drivers/video/fbdev/bcm2708_fb.c new file mode 100644 -index 0000000..dcc4de2 +index 0000000..f632750 --- /dev/null +++ b/drivers/video/fbdev/bcm2708_fb.c @@ -0,0 +1,818 @@ @@ -64289,10 +74825,10 @@ index 0000000..dcc4de2 + + bcm_mailbox_property(&p, p[0]); + -+ pr_info("bcm2708_fb_ioctl %x,%lx returns=%d p[1]=0x%x\n", cmd, arg, p[5], p[1]); -+ + if ( p[1] == VCMSG_REQUEST_SUCCESSFUL ) + result = 0; ++ else ++ pr_err("bcm2708_fb_ioctl %x,%lx returns=%d p[1]=0x%x\n", cmd, arg, p[5], p[1]); + } + return result; +} @@ -67172,13139 +77708,2894 @@ index 3c14e43..7626beb6a 100644 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 -From 3f8261010097c731e1128600efb3107dc83aa24f Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 2 Jul 2013 23:42:01 +0100 -Subject: [PATCH 011/172] bcm2708 vchiq driver +From d4c6296fcab17d13cd97d83c8f53bc884d7081d6 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:22:53 +0100 +Subject: [PATCH 007/104] dmaengine: Add support for BCM2708 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -Signed-off-by: popcornmix +Add support for DMA controller of BCM2708 as used in the Raspberry Pi. +Currently it only supports cyclic DMA. -vchiq: create_pagelist copes with vmalloc memory +Signed-off-by: Florian Meier -Signed-off-by: Daniel Stone +dmaengine: expand functionality by supporting scatter/gather transfers sdhci-bcm2708 and dma.c: fix for LITE channels -vchiq: fix the shim message release +DMA: fix cyclic LITE length overflow bug -Signed-off-by: Daniel Stone +dmaengine: bcm2708: Remove chancnt affectations -vchiq: export additional symbols +Mirror bcm2835-dma.c commit 9eba5536a7434c69d8c185d4bd1c70734d92287d: +chancnt is already filled by dma_async_device_register, which uses the channel +list to know how much channels there is. -Signed-off-by: Daniel Stone +Since it's already filled, we can safely remove it from the drivers' probe +function. -VCHIQ: Make service closure fully synchronous (drv) +Signed-off-by: Noralf Trønnes -This is one half of a two-part patch, the other half of which is to -the vchiq_lib user library. With these patches, calls to -vchiq_close_service and vchiq_remove_service won't return until any -associated callbacks have been delivered to the callback thread. +dmaengine: bcm2708: overwrite dreq only if it is not set -VCHIQ: Add per-service tracing +dreq is set when the DMA channel is fetched from Device Tree. +slave_id is set using dmaengine_slave_config(). +Only overwrite dreq with slave_id if it is not set. -The new service option VCHIQ_SERVICE_OPTION_TRACE is a boolean that -toggles tracing for the specified service. +dreq/slave_id in the cyclic DMA case is not touched, because I don't +have hardware to test with. -This commit also introduces vchi_service_set_option and the associated -option VCHI_SERVICE_OPTION_TRACE. +Signed-off-by: Noralf Trønnes -vchiq: Make the synchronous-CLOSE logic more tolerant +dmaengine: bcm2708: do device registration in the board file -vchiq: Move logging control into debugfs +Don't register the device in the driver. Do it in the board file. -vchiq: Take care of a corner case tickled by VCSM +Signed-off-by: Noralf Trønnes -Closing a connection that isn't fully open requires care, since one -side does not know the other side's port number. Code was present to -handle the case where a CLOSE is sent immediately after an OPEN, i.e. -before the OPENACK has been received, but this was incorrectly being -used when an OPEN from a client using port 0 was rejected. +dmaengine: bcm2708: don't restrict DT support to ARCH_BCM2835 -(In the observed failure, the host was attempting to use the VCSM -service, which isn't present in the 'cutdown' firmware. The failure -was intermittent because sometimes the keepalive service would -grab port 0.) +Both ARCH_BCM2835 and ARCH_BCM270x are built with OF now. +Add Device Tree support to the non ARCH_BCM2835 case. +Use the same driver name regardless of architecture. -This case can be distinguished because the client's remoteport will -still be VCHIQ_PORT_FREE, and the srvstate will be OPENING. Either -condition is sufficient to differentiate it from the special case -described above. +Signed-off-by: Noralf Trønnes --- - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/vc04_services/Kconfig | 9 + - drivers/misc/vc04_services/Makefile | 17 + - .../interface/vchi/connections/connection.h | 328 ++ - .../interface/vchi/message_drivers/message.h | 204 ++ - drivers/misc/vc04_services/interface/vchi/vchi.h | 378 ++ - .../misc/vc04_services/interface/vchi/vchi_cfg.h | 224 ++ - .../interface/vchi/vchi_cfg_internal.h | 71 + - .../vc04_services/interface/vchi/vchi_common.h | 174 + - .../misc/vc04_services/interface/vchi/vchi_mh.h | 42 + - .../misc/vc04_services/interface/vchiq_arm/vchiq.h | 40 + - .../vc04_services/interface/vchiq_arm/vchiq_2835.h | 42 + - .../interface/vchiq_arm/vchiq_2835_arm.c | 561 +++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2883 +++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 223 ++ - .../interface/vchiq_arm/vchiq_build_info.h | 37 + - .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 66 + - .../interface/vchiq_arm/vchiq_connected.c | 119 + - .../interface/vchiq_arm/vchiq_connected.h | 50 + - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 3861 ++++++++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_core.h | 711 ++++ - .../interface/vchiq_arm/vchiq_debugfs.c | 383 ++ - .../interface/vchiq_arm/vchiq_debugfs.h | 52 + - .../interface/vchiq_arm/vchiq_genversion | 87 + - .../vc04_services/interface/vchiq_arm/vchiq_if.h | 189 + - .../interface/vchiq_arm/vchiq_ioctl.h | 131 + - .../interface/vchiq_arm/vchiq_kern_lib.c | 456 +++ - .../interface/vchiq_arm/vchiq_memdrv.h | 71 + - .../interface/vchiq_arm/vchiq_pagelist.h | 58 + - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 853 +++++ - .../vc04_services/interface/vchiq_arm/vchiq_util.c | 151 + - .../vc04_services/interface/vchiq_arm/vchiq_util.h | 81 + - .../interface/vchiq_arm/vchiq_version.c | 59 + - 34 files changed, 12613 insertions(+) - create mode 100644 drivers/misc/vc04_services/Kconfig - create mode 100644 drivers/misc/vc04_services/Makefile - create mode 100644 drivers/misc/vc04_services/interface/vchi/connections/connection.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/message_drivers/message.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_common.h - create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c + arch/arm/mach-bcm2708/bcm2708.c | 6 + + arch/arm/mach-bcm2708/dma.c | 2 + + arch/arm/mach-bcm2708/include/mach/dma.h | 6 +- + arch/arm/mach-bcm2709/bcm2709.c | 6 + + drivers/dma/Kconfig | 6 + + drivers/dma/Makefile | 1 + + drivers/dma/bcm2708-dmaengine.c | 989 +++++++++++++++++++++++++++++++ + 7 files changed, 1015 insertions(+), 1 deletion(-) + create mode 100644 drivers/dma/bcm2708-dmaengine.c -diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index 006242c..4e6f46c3 100644 ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -524,6 +524,7 @@ source "drivers/misc/carma/Kconfig" - source "drivers/misc/altera-stapl/Kconfig" - source "drivers/misc/mei/Kconfig" - source "drivers/misc/vmw_vmci/Kconfig" -+source "drivers/misc/vc04_services/Kconfig" - source "drivers/misc/mic/Kconfig" - source "drivers/misc/genwqe/Kconfig" - source "drivers/misc/echo/Kconfig" -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index 7d5c4cd..c085ede 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -51,6 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ - obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ - obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o - obj-$(CONFIG_SRAM) += sram.o -+obj-y += vc04_services/ - obj-y += mic/ - obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ -diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig -new file mode 100644 -index 0000000..2663933 ---- /dev/null -+++ b/drivers/misc/vc04_services/Kconfig -@@ -0,0 +1,9 @@ -+config BCM2708_VCHIQ -+ tristate "Videocore VCHIQ" -+ depends on MACH_BCM2708 -+ default y -+ help -+ Kernel to VideoCore communication interface for the -+ BCM2708 family of products. -+ Defaults to Y when the Broadcom Videocore services -+ are included in the build, N otherwise. -diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile -new file mode 100644 -index 0000000..0c82520 ---- /dev/null -+++ b/drivers/misc/vc04_services/Makefile -@@ -0,0 +1,17 @@ -+ifeq ($(CONFIG_MACH_BCM2708),y) -+ -+obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o -+ -+vchiq-objs := \ -+ interface/vchiq_arm/vchiq_core.o \ -+ interface/vchiq_arm/vchiq_arm.o \ -+ interface/vchiq_arm/vchiq_kern_lib.o \ -+ interface/vchiq_arm/vchiq_2835_arm.o \ -+ interface/vchiq_arm/vchiq_debugfs.o \ -+ interface/vchiq_arm/vchiq_shim.o \ -+ interface/vchiq_arm/vchiq_util.o \ -+ interface/vchiq_arm/vchiq_connected.o \ -+ -+ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -+ -+endif -diff --git a/drivers/misc/vc04_services/interface/vchi/connections/connection.h b/drivers/misc/vc04_services/interface/vchi/connections/connection.h -new file mode 100644 -index 0000000..fef6ac3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/connections/connection.h -@@ -0,0 +1,328 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef CONNECTION_H_ -+#define CONNECTION_H_ -+ -+#include -+#include -+#include -+ -+#include "interface/vchi/vchi_cfg_internal.h" -+#include "interface/vchi/vchi_common.h" -+#include "interface/vchi/message_drivers/message.h" -+ -+/****************************************************************************** -+ Global defs -+ *****************************************************************************/ -+ -+// Opaque handle for a connection / service pair -+typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; -+ -+// opaque handle to the connection state information -+typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; -+ -+typedef struct vchi_connection_t VCHI_CONNECTION_T; -+ -+ -+/****************************************************************************** -+ API -+ *****************************************************************************/ -+ -+// Routine to init a connection with a particular low level driver -+typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, -+ const VCHI_MESSAGE_DRIVER_T * driver ); -+ -+// Routine to control CRC enabling at a connection level -+typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, -+ VCHI_CRC_CONTROL_T control ); -+ -+// Routine to create a service -+typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, -+ int32_t service_id, -+ uint32_t rx_fifo_size, -+ uint32_t tx_fifo_size, -+ int server, -+ VCHI_CALLBACK_T callback, -+ void *callback_param, -+ int32_t want_crc, -+ int32_t want_unaligned_bulk_rx, -+ int32_t want_unaligned_bulk_tx, -+ VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); -+ -+// Routine to close a service -+typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); -+ -+// Routine to queue a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// scatter-gather (vector) message queueing -+typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// Routine to dequeue a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to peek at a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to hold a message -+typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags, -+ void **message_handle ); -+ -+// Routine to initialise a received message iterator -+typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ VCHI_MSG_ITER_T *iter, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to release a held message -+typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *message_handle ); -+ -+// Routine to get info on a held message -+typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *message_handle, -+ void **data, -+ int32_t *msg_size, -+ uint32_t *tx_timestamp, -+ uint32_t *rx_timestamp ); -+ -+// Routine to check whether the iterator has a next message -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ const VCHI_MSG_ITER_T *iter ); -+ -+// Routine to advance the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter, -+ void **data, -+ uint32_t *msg_size ); -+ -+// Routine to remove the last message returned by the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter ); -+ -+// Routine to hold the last message returned by the iterator -+typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, -+ VCHI_MSG_ITER_T *iter, -+ void **msg_handle ); -+ -+// Routine to transmit bulk data -+typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle ); -+ -+// Routine to receive data -+typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle ); -+ -+// Routine to report if a server is available -+typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t peer_flags ); -+ -+// Routine to report the number of RX slots available -+typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); -+ -+// Routine to report the RX slot size -+typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); -+ -+// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO -+typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, -+ int32_t service, -+ uint32_t length, -+ MESSAGE_TX_CHANNEL_T channel, -+ uint32_t channel_params, -+ uint32_t data_length, -+ uint32_t data_offset); -+ -+// Callback to inform a service that a Xon or Xoff message has been received -+typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t xoff); -+ -+// Callback to inform a service that a server available reply message has been received -+typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, uint32_t flags); -+ -+// Callback to indicate that bulk auxiliary messages have arrived -+typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); -+ -+// Callback to indicate that bulk auxiliary messages have arrived -+typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); -+ -+// Callback with all the connection info you require -+typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); -+ -+// Callback to inform of a disconnect -+typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); -+ -+// Callback to inform of a power control request -+typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, int32_t enable); -+ -+// allocate memory suitably aligned for this connection -+typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); -+ -+// free memory allocated by buffer_allocate -+typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); -+ -+ -+/****************************************************************************** -+ System driver struct -+ *****************************************************************************/ -+ -+struct opaque_vchi_connection_api_t -+{ -+ // Routine to init the connection -+ VCHI_CONNECTION_INIT_T init; -+ -+ // Connection-level CRC control -+ VCHI_CONNECTION_CRC_CONTROL_T crc_control; -+ -+ // Routine to connect to or create service -+ VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; -+ -+ // Routine to disconnect from a service -+ VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; -+ -+ // Routine to queue a message -+ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; -+ -+ // scatter-gather (vector) message queue -+ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; -+ -+ // Routine to dequeue a message -+ VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; -+ -+ // Routine to peek at a message -+ VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; -+ -+ // Routine to hold a message -+ VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; -+ -+ // Routine to initialise a received message iterator -+ VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; -+ -+ // Routine to release a message -+ VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; -+ -+ // Routine to get information on a held message -+ VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; -+ -+ // Routine to check for next message on iterator -+ VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; -+ -+ // Routine to get next message on iterator -+ VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; -+ -+ // Routine to remove the last message returned by iterator -+ VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; -+ -+ // Routine to hold the last message returned by iterator -+ VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; -+ -+ // Routine to transmit bulk data -+ VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; -+ -+ // Routine to receive data -+ VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; -+ -+ // Routine to report the available servers -+ VCHI_CONNECTION_SERVER_PRESENT server_present; -+ -+ // Routine to report the number of RX slots available -+ VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; -+ -+ // Routine to report the RX slot size -+ VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; -+ -+ // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO -+ VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; -+ -+ // Callback to inform a service that a Xon or Xoff message has been received -+ VCHI_CONNECTION_FLOW_CONTROL flow_control; -+ -+ // Callback to inform a service that a server available reply message has been received -+ VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; -+ -+ // Callback to indicate that bulk auxiliary messages have arrived -+ VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; -+ -+ // Callback to indicate that a bulk auxiliary message has been transmitted -+ VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; -+ -+ // Callback to provide information about the connection -+ VCHI_CONNECTION_INFO connection_info; -+ -+ // Callback to notify that peer has requested disconnect -+ VCHI_CONNECTION_DISCONNECT disconnect; -+ -+ // Callback to notify that peer has requested power change -+ VCHI_CONNECTION_POWER_CONTROL power_control; -+ -+ // allocate memory suitably aligned for this connection -+ VCHI_BUFFER_ALLOCATE buffer_allocate; -+ -+ // free memory allocated by buffer_allocate -+ VCHI_BUFFER_FREE buffer_free; -+ +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index d9db081..5d8657b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -250,6 +250,11 @@ static struct platform_device bcm2708_dmaman_device = { + .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), + }; + ++static struct platform_device bcm2708_dmaengine_device = { ++ .name = "bcm2708-dmaengine", ++ .id = -1, +}; + -+struct vchi_connection_t { -+ const VCHI_CONNECTION_API_T *api; -+ VCHI_CONNECTION_STATE_T *state; -+#ifdef VCHI_COARSE_LOCKING -+ struct semaphore sem; -+#endif + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -508,6 +513,7 @@ void __init bcm2708_init(void) + clkdev_add(&lookups[i]); + + bcm_register_device(&bcm2708_dmaman_device); ++ bcm_register_device(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device(&bcm2708_gpio_device); +diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c +index 1da2413..a5e58d1 100644 +--- a/arch/arm/mach-bcm2708/dma.c ++++ b/arch/arm/mach-bcm2708/dma.c +@@ -156,6 +156,8 @@ static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, + dmaman->chan_available = chans_available; + dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ + dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ ++ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ + } + + static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, +diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h +index a4aac4c..d03e7b5 100644 +--- a/arch/arm/mach-bcm2708/include/mach/dma.h ++++ b/arch/arm/mach-bcm2708/include/mach/dma.h +@@ -77,9 +77,13 @@ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); + those with higher priority smaller ordinal numbers */ + #define BCM_DMA_FEATURE_FAST_ORD 0 + #define BCM_DMA_FEATURE_BULK_ORD 1 ++#define BCM_DMA_FEATURE_NORMAL_ORD 2 ++#define BCM_DMA_FEATURE_LITE_ORD 3 + #define BCM_DMA_FEATURE_FAST (1< -+#include -+#include -+ -+#include "interface/vchi/vchi_cfg_internal.h" -+#include "interface/vchi/vchi_common.h" -+ -+ -+typedef enum message_event_type { -+ MESSAGE_EVENT_NONE, -+ MESSAGE_EVENT_NOP, -+ MESSAGE_EVENT_MESSAGE, -+ MESSAGE_EVENT_SLOT_COMPLETE, -+ MESSAGE_EVENT_RX_BULK_PAUSED, -+ MESSAGE_EVENT_RX_BULK_COMPLETE, -+ MESSAGE_EVENT_TX_COMPLETE, -+ MESSAGE_EVENT_MSG_DISCARDED -+} MESSAGE_EVENT_TYPE_T; -+ -+typedef enum vchi_msg_flags -+{ -+ VCHI_MSG_FLAGS_NONE = 0x0, -+ VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 -+} VCHI_MSG_FLAGS_T; -+ -+typedef enum message_tx_channel -+{ -+ MESSAGE_TX_CHANNEL_MESSAGE = 0, -+ MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards -+} MESSAGE_TX_CHANNEL_T; -+ -+// Macros used for cycling through bulk channels -+#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) -+#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) -+ -+typedef enum message_rx_channel -+{ -+ MESSAGE_RX_CHANNEL_MESSAGE = 0, -+ MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards -+} MESSAGE_RX_CHANNEL_T; -+ -+// Message receive slot information -+typedef struct rx_msg_slot_info { -+ -+ struct rx_msg_slot_info *next; -+ //struct slot_info *prev; -+#if !defined VCHI_COARSE_LOCKING -+ struct semaphore sem; -+#endif -+ -+ uint8_t *addr; // base address of slot -+ uint32_t len; // length of slot in bytes -+ -+ uint32_t write_ptr; // hardware causes this to advance -+ uint32_t read_ptr; // this module does the reading -+ int active; // is this slot in the hardware dma fifo? -+ uint32_t msgs_parsed; // count how many messages are in this slot -+ uint32_t msgs_released; // how many messages have been released -+ void *state; // connection state information -+ uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services -+} RX_MSG_SLOTINFO_T; -+ -+// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. -+// In particular, it mustn't use addr and len - they're the client buffer, but the message -+// driver will be tasked with sending the aligned core section. -+typedef struct rx_bulk_slotinfo_t { -+ struct rx_bulk_slotinfo_t *next; -+ -+ struct semaphore *blocking; -+ -+ // needed by DMA -+ void *addr; -+ uint32_t len; -+ -+ // needed for the callback -+ void *service; -+ void *handle; -+ VCHI_FLAGS_T flags; -+} RX_BULK_SLOTINFO_T; -+ -+ -+/* ---------------------------------------------------------------------- -+ * each connection driver will have a pool of the following struct. -+ * -+ * the pool will be managed by vchi_qman_* -+ * this means there will be multiple queues (single linked lists) -+ * a given struct message_info will be on exactly one of these queues -+ * at any one time -+ * -------------------------------------------------------------------- */ -+typedef struct rx_message_info { -+ -+ struct message_info *next; -+ //struct message_info *prev; -+ -+ uint8_t *addr; -+ uint32_t len; -+ RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message -+ uint32_t tx_timestamp; -+ uint32_t rx_timestamp; -+ -+} RX_MESSAGE_INFO_T; -+ -+typedef struct { -+ MESSAGE_EVENT_TYPE_T type; -+ -+ struct { -+ // for messages -+ void *addr; // address of message -+ uint16_t slot_delta; // whether this message indicated slot delta -+ uint32_t len; // length of message -+ RX_MSG_SLOTINFO_T *slot; // slot this message is in -+ int32_t service; // service id this message is destined for -+ uint32_t tx_timestamp; // timestamp from the header -+ uint32_t rx_timestamp; // timestamp when we parsed it -+ } message; -+ -+ // FIXME: cleanup slot reporting... -+ RX_MSG_SLOTINFO_T *rx_msg; -+ RX_BULK_SLOTINFO_T *rx_bulk; -+ void *tx_handle; -+ MESSAGE_TX_CHANNEL_T tx_channel; -+ -+} MESSAGE_EVENT_T; -+ -+ -+// callbacks -+typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); -+ -+typedef struct { -+ VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; -+} VCHI_MESSAGE_DRIVER_OPEN_T; -+ -+ -+// handle to this instance of message driver (as returned by ->open) -+typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; -+ -+struct opaque_vchi_message_driver_t { -+ VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); -+ int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, int32_t enable ); -+ int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message -+ int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) -+ int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) -+ void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver -+ int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); -+ int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, int32_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void -+ *address, uint32_t length_avail, uint32_t max_total_length, int32_t pad_to_fill, int32_t allow_partial ); -+ -+ int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); -+ int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); -+ void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); -+ void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); -+ int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); -+ int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); -+ -+ int32_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); -+ int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); -+ void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); -+ void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); -+}; -+ -+ -+#endif // _VCHI_MESSAGE_H_ -+ -+/****************************** End of file ***********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi.h b/drivers/misc/vc04_services/interface/vchi/vchi.h -new file mode 100644 -index 0000000..1b17e98 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi.h -@@ -0,0 +1,378 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_H_ -+#define VCHI_H_ -+ -+#include "interface/vchi/vchi_cfg.h" -+#include "interface/vchi/vchi_common.h" -+#include "interface/vchi/connections/connection.h" -+#include "vchi_mh.h" -+ -+ -+/****************************************************************************** -+ Global defs -+ *****************************************************************************/ -+ -+#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) -+#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) -+#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) -+ -+#ifdef USE_VCHIQ_ARM -+#define VCHI_BULK_ALIGNED(x) 1 -+#else -+#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) -+#endif -+ -+struct vchi_version { -+ uint32_t version; -+ uint32_t version_min; -+}; -+#define VCHI_VERSION(v_) { v_, v_ } -+#define VCHI_VERSION_EX(v_, m_) { v_, m_ } -+ -+typedef enum -+{ -+ VCHI_VEC_POINTER, -+ VCHI_VEC_HANDLE, -+ VCHI_VEC_LIST -+} VCHI_MSG_VECTOR_TYPE_T; -+ -+typedef struct vchi_msg_vector_ex { -+ -+ VCHI_MSG_VECTOR_TYPE_T type; -+ union -+ { -+ // a memory handle -+ struct -+ { -+ VCHI_MEM_HANDLE_T handle; -+ uint32_t offset; -+ int32_t vec_len; -+ } handle; -+ -+ // an ordinary data pointer -+ struct -+ { -+ const void *vec_base; -+ int32_t vec_len; -+ } ptr; -+ -+ // a nested vector list -+ struct -+ { -+ struct vchi_msg_vector_ex *vec; -+ uint32_t vec_len; -+ } list; -+ } u; -+} VCHI_MSG_VECTOR_EX_T; -+ -+ -+// Construct an entry in a msg vector for a pointer (p) of length (l) -+#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } -+ -+// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) -+#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } -+ -+// Macros to manipulate 'FOURCC' values -+#define MAKE_FOURCC(x) ((int32_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) -+#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF -+ -+ -+// Opaque service information -+struct opaque_vchi_service_t; -+ -+// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, -+// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. -+typedef struct -+{ -+ struct opaque_vchi_service_t *service; -+ void *message; -+} VCHI_HELD_MSG_T; -+ -+ -+ -+// structure used to provide the information needed to open a server or a client -+typedef struct { -+ struct vchi_version version; -+ int32_t service_id; -+ VCHI_CONNECTION_T *connection; -+ uint32_t rx_fifo_size; -+ uint32_t tx_fifo_size; -+ VCHI_CALLBACK_T callback; -+ void *callback_param; -+ /* client intends to receive bulk transfers of -+ odd lengths or into unaligned buffers */ -+ int32_t want_unaligned_bulk_rx; -+ /* client intends to transmit bulk transfers of -+ odd lengths or out of unaligned buffers */ -+ int32_t want_unaligned_bulk_tx; -+ /* client wants to check CRCs on (bulk) xfers. -+ Only needs to be set at 1 end - will do both directions. */ -+ int32_t want_crc; -+} SERVICE_CREATION_T; -+ -+// Opaque handle for a VCHI instance -+typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; -+ -+// Opaque handle for a server or client -+typedef struct opaque_vchi_service_handle_t *VCHI_SERVICE_HANDLE_T; -+ -+// Service registration & startup -+typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); -+ -+typedef struct service_info_tag { -+ const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ -+ VCHI_SERVICE_INIT init; /* Service initialisation function */ -+ void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ -+} SERVICE_INFO_T; -+ -+/****************************************************************************** -+ Global funcs - implementation is specific to which side you are on (local / remote) -+ *****************************************************************************/ -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, -+ const VCHI_MESSAGE_DRIVER_T * low_level); -+ -+ -+// Routine used to initialise the vchi on both local + remote connections -+extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); -+ -+extern int32_t vchi_exit( void ); -+ -+extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, -+ const uint32_t num_connections, -+ VCHI_INSTANCE_T instance_handle ); -+ -+//When this is called, ensure that all services have no data pending. -+//Bulk transfers can remain 'queued' -+extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); -+ -+// Global control over bulk CRC checking -+extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, -+ VCHI_CRC_CONTROL_T control ); -+ -+// helper functions -+extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); -+extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); -+extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); -+ -+ -+/****************************************************************************** -+ Global service API -+ *****************************************************************************/ -+// Routine to create a named service -+extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle ); -+ -+// Routine to destory a service -+extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to open a named service -+extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle); -+ -+extern int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, -+ short *peer_version ); -+ -+// Routine to close a named service -+extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to increment ref count on a named service -+extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to decrement ref count on a named service -+extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to set a control option for a named service -+extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_SERVICE_OPTION_T option, -+ int value); -+ -+// Routine to send a message across a service -+extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// scatter-gather (vector) and send message -+int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_EX_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// legacy scatter-gather (vector) and send message, only handles pointers -+int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle ); -+ -+// Routine to receive a msg from a service -+// Dequeue is equivalent to hold, copy into client buffer, release -+extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to look at a message in place. -+// The message is not dequeued, so a subsequent call to peek or dequeue -+// will return the same message. -+extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags ); -+ -+// Routine to remove a message after it has been read in place with peek -+// The first message on the queue is dequeued. -+extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); -+ -+// Routine to look at a message in place. -+// The message is dequeued, so the caller is left holding it; the descriptor is -+// filled in and must be released when the user has finished with the message. -+extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, -+ void **data, // } may be NULL, as info can be -+ uint32_t *msg_size, // } obtained from HELD_MSG_T -+ VCHI_FLAGS_T flags, -+ VCHI_HELD_MSG_T *message_descriptor ); -+ -+// Initialise an iterator to look through messages in place -+extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_ITER_T *iter, -+ VCHI_FLAGS_T flags ); -+ -+/****************************************************************************** -+ Global service support API - operations on held messages and message iterators -+ *****************************************************************************/ -+ -+// Routine to get the address of a held message -+extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the size of a held message -+extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the transmit timestamp as written into the header by the peer -+extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to get the reception timestamp, written as we parsed the header -+extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); -+ -+// Routine to release a held message after it has been processed -+extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); -+ -+// Indicates whether the iterator has a next message. -+extern int32_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); -+ -+// Return the pointer and length for the next message and advance the iterator. -+extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, -+ void **data, -+ uint32_t *msg_size ); -+ -+// Remove the last message returned by vchi_msg_iter_next. -+// Can only be called once after each call to vchi_msg_iter_next. -+extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); -+ -+// Hold the last message returned by vchi_msg_iter_next. -+// Can only be called once after each call to vchi_msg_iter_next. -+extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, -+ VCHI_HELD_MSG_T *message ); -+ -+// Return information for the next message, and hold it, advancing the iterator. -+extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, -+ void **data, // } may be NULL -+ uint32_t *msg_size, // } -+ VCHI_HELD_MSG_T *message ); -+ -+ -+/****************************************************************************** -+ Global bulk API -+ *****************************************************************************/ -+ -+// Routine to prepare interface for a transfer from the other side -+extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+ -+ -+// Prepare interface for a transfer from the other side into relocatable memory. -+int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T h_dst, -+ uint32_t offset, -+ uint32_t data_size, -+ const VCHI_FLAGS_T flags, -+ void * const bulk_handle ); -+ -+// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) -+extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+ -+ -+/****************************************************************************** -+ Configuration plumbing -+ *****************************************************************************/ -+ -+// function prototypes for the different mid layers (the state info gives the different physical connections) -+extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); -+//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); -+//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); -+ -+// declare all message drivers here -+const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T h_src, -+ uint32_t offset, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *transfer_handle ); -+#endif /* VCHI_H_ */ -+ -+/****************************** End of file **********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h -new file mode 100644 -index 0000000..26bc2d3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h -@@ -0,0 +1,224 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_CFG_H_ -+#define VCHI_CFG_H_ -+ -+/**************************************************************************************** -+ * Defines in this first section are part of the VCHI API and may be examined by VCHI -+ * services. -+ ***************************************************************************************/ -+ -+/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ -+/* Really determined by the message driver, and should be available from a run-time call. */ -+#ifndef VCHI_BULK_ALIGN -+# if __VCCOREVER__ >= 0x04000000 -+# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans -+# else -+# define VCHI_BULK_ALIGN 16 -+# endif -+#endif -+ -+/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ -+/* May be less than or greater than VCHI_BULK_ALIGN */ -+/* Really determined by the message driver, and should be available from a run-time call. */ -+#ifndef VCHI_BULK_GRANULARITY -+# if __VCCOREVER__ >= 0x04000000 -+# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans -+# else -+# define VCHI_BULK_GRANULARITY 16 -+# endif -+#endif -+ -+/* The largest possible message to be queued with vchi_msg_queue. */ -+#ifndef VCHI_MAX_MSG_SIZE -+# if defined VCHI_LOCAL_HOST_PORT -+# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? -+# else -+# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! -+# endif -+#endif -+ -+/****************************************************************************************** -+ * Defines below are system configuration options, and should not be used by VCHI services. -+ *****************************************************************************************/ -+ -+/* How many connections can we support? A localhost implementation uses 2 connections, -+ * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW -+ * driver. */ -+#ifndef VCHI_MAX_NUM_CONNECTIONS -+# define VCHI_MAX_NUM_CONNECTIONS 3 -+#endif -+ -+/* How many services can we open per connection? Extending this doesn't cost processing time, just a small -+ * amount of static memory. */ -+#ifndef VCHI_MAX_SERVICES_PER_CONNECTION -+# define VCHI_MAX_SERVICES_PER_CONNECTION 36 -+#endif -+ -+/* Adjust if using a message driver that supports more logical TX channels */ -+#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION -+# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels -+#endif -+ -+/* Adjust if using a message driver that supports more logical RX channels */ -+#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION -+# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI -+#endif -+ -+/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective -+ * receive queue space, less message headers. */ -+#ifndef VCHI_NUM_READ_SLOTS -+# if defined(VCHI_LOCAL_HOST_PORT) -+# define VCHI_NUM_READ_SLOTS 4 -+# else -+# define VCHI_NUM_READ_SLOTS 48 -+# endif -+#endif -+ -+/* Do we utilise overrun facility for receive message slots? Can aid peer transmit -+ * performance. Only define on VideoCore end, talking to host. -+ */ -+//#define VCHI_MSG_RX_OVERRUN -+ -+/* How many transmit slots do we use. Generally don't need many, as the hardware driver -+ * underneath VCHI will usually have its own buffering. */ -+#ifndef VCHI_NUM_WRITE_SLOTS -+# define VCHI_NUM_WRITE_SLOTS 4 -+#endif -+ -+/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, -+ * then it's taking up too much buffer space, and the peer service will be told to stop -+ * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS -+ * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency -+ * is too high. */ -+#ifndef VCHI_XOFF_THRESHOLD -+# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) -+#endif -+ -+/* After we've sent an XOFF, the peer will be told to resume transmission once the local -+ * service has dequeued/released enough messages that it's now occupying -+ * VCHI_XON_THRESHOLD slots or fewer. */ -+#ifndef VCHI_XON_THRESHOLD -+# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) -+#endif -+ -+/* A size below which a bulk transfer omits the handshake completely and always goes -+ * via the message channel, if bulk auxiliary is being sent on that service. (The user -+ * can guarantee this by enabling unaligned transmits). -+ * Not API. */ -+#ifndef VCHI_MIN_BULK_SIZE -+# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) -+#endif -+ -+/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between -+ * speed and latency; the smaller the chunk size the better change of messages and other -+ * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not -+ * break transmissions into chunks. -+ */ -+#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI -+# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) -+#endif -+ -+/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode -+ * with multiple-line frames. Only use if the receiver can cope. */ -+#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 -+# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 -+#endif -+ -+/* How many TX messages can we have pending in our transmit slots. Once exhausted, -+ * vchi_msg_queue will be blocked. */ -+#ifndef VCHI_TX_MSG_QUEUE_SIZE -+# define VCHI_TX_MSG_QUEUE_SIZE 256 -+#endif -+ -+/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing -+ * will be suspended until older messages are dequeued/released. */ -+#ifndef VCHI_RX_MSG_QUEUE_SIZE -+# define VCHI_RX_MSG_QUEUE_SIZE 256 -+#endif -+ -+/* Really should be able to cope if we run out of received message descriptors, by -+ * suspending parsing as the comment above says, but we don't. This sweeps the issue -+ * under the carpet. */ -+#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS -+# undef VCHI_RX_MSG_QUEUE_SIZE -+# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS -+#endif -+ -+/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit -+ * will be blocked. */ -+#ifndef VCHI_TX_BULK_QUEUE_SIZE -+# define VCHI_TX_BULK_QUEUE_SIZE 64 -+#endif -+ -+/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive -+ * will be blocked. */ -+#ifndef VCHI_RX_BULK_QUEUE_SIZE -+# define VCHI_RX_BULK_QUEUE_SIZE 64 -+#endif -+ -+/* A limit on how many outstanding bulk requests we expect the peer to give us. If -+ * the peer asks for more than this, VCHI will fail and assert. The number is determined -+ * by the peer's hardware - it's the number of outstanding requests that can be queued -+ * on all bulk channels. VC3's MPHI peripheral allows 16. */ -+#ifndef VCHI_MAX_PEER_BULK_REQUESTS -+# define VCHI_MAX_PEER_BULK_REQUESTS 32 -+#endif -+ -+/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 -+ * transmitter on and off. -+ */ -+/*#define VCHI_CCP2TX_MANUAL_POWER*/ -+ -+#ifndef VCHI_CCP2TX_MANUAL_POWER -+ -+/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set -+ * negative for no IDLE. -+ */ -+# ifndef VCHI_CCP2TX_IDLE_TIMEOUT -+# define VCHI_CCP2TX_IDLE_TIMEOUT 5 -+# endif -+ -+/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set -+ * negative for no OFF. -+ */ -+# ifndef VCHI_CCP2TX_OFF_TIMEOUT -+# define VCHI_CCP2TX_OFF_TIMEOUT 1000 -+# endif -+ -+#endif /* VCHI_CCP2TX_MANUAL_POWER */ -+ -+#endif /* VCHI_CFG_H_ */ -+ -+/****************************** End of file **********************************/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h -new file mode 100644 -index 0000000..35dcba4 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h -@@ -0,0 +1,71 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_CFG_INTERNAL_H_ -+#define VCHI_CFG_INTERNAL_H_ -+ -+/**************************************************************************************** -+ * Control optimisation attempts. -+ ***************************************************************************************/ -+ -+// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second -+#define VCHI_COARSE_LOCKING -+ -+// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) -+// (only relevant if VCHI_COARSE_LOCKING) -+#define VCHI_ELIDE_BLOCK_EXIT_LOCK -+ -+// Avoid lock on non-blocking peek -+// (only relevant if VCHI_COARSE_LOCKING) -+#define VCHI_AVOID_PEEK_LOCK -+ -+// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. -+#define VCHI_MULTIPLE_HANDLER_THREADS -+ -+// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash -+// our way through the pool of descriptors. -+#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD -+ -+// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. -+#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS -+ -+// Don't use message descriptors for TX messages that don't need them -+#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS -+ -+// Nano-locks for multiqueue -+//#define VCHI_MQUEUE_NANOLOCKS -+ -+// Lock-free(er) dequeuing -+//#define VCHI_RX_NANOLOCKS -+ -+#endif /*VCHI_CFG_INTERNAL_H_*/ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -new file mode 100644 -index 0000000..d76118c ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -@@ -0,0 +1,174 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHI_COMMON_H_ -+#define VCHI_COMMON_H_ -+ -+ -+//flags used when sending messages (must be bitmapped) -+typedef enum -+{ -+ VCHI_FLAGS_NONE = 0x0, -+ VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go -+ VCHI_FLAGS_ALLOW_PARTIAL = 0x8, -+ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, -+ VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, -+ -+ VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only -+ VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only -+ VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only -+ VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only -+ VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only -+ VCHI_FLAGS_INTERNAL = 0xFF0000 -+} VCHI_FLAGS_T; -+ -+// constants for vchi_crc_control() -+typedef enum { -+ VCHI_CRC_NOTHING = -1, -+ VCHI_CRC_PER_SERVICE = 0, -+ VCHI_CRC_EVERYTHING = 1, -+} VCHI_CRC_CONTROL_T; -+ -+//callback reasons when an event occurs on a service -+typedef enum -+{ -+ VCHI_CALLBACK_REASON_MIN, -+ -+ //This indicates that there is data available -+ //handle is the msg id that was transmitted with the data -+ // When a message is received and there was no FULL message available previously, send callback -+ // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails -+ VCHI_CALLBACK_MSG_AVAILABLE, -+ VCHI_CALLBACK_MSG_SENT, -+ VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented -+ -+ // This indicates that a transfer from the other side has completed -+ VCHI_CALLBACK_BULK_RECEIVED, -+ //This indicates that data queued up to be sent has now gone -+ //handle is the msg id that was used when sending the data -+ VCHI_CALLBACK_BULK_SENT, -+ VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented -+ VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented -+ -+ VCHI_CALLBACK_SERVICE_CLOSED, -+ -+ // this side has sent XOFF to peer due to lack of data consumption by service -+ // (suggests the service may need to take some recovery action if it has -+ // been deliberately holding off consuming data) -+ VCHI_CALLBACK_SENT_XOFF, -+ VCHI_CALLBACK_SENT_XON, -+ -+ // indicates that a bulk transfer has finished reading the source buffer -+ VCHI_CALLBACK_BULK_DATA_READ, -+ -+ // power notification events (currently host side only) -+ VCHI_CALLBACK_PEER_OFF, -+ VCHI_CALLBACK_PEER_SUSPENDED, -+ VCHI_CALLBACK_PEER_ON, -+ VCHI_CALLBACK_PEER_RESUMED, -+ VCHI_CALLBACK_FORCED_POWER_OFF, -+ -+#ifdef USE_VCHIQ_ARM -+ // some extra notifications provided by vchiq_arm -+ VCHI_CALLBACK_SERVICE_OPENED, -+ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, -+ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, -+#endif -+ -+ VCHI_CALLBACK_REASON_MAX -+} VCHI_CALLBACK_REASON_T; -+ -+// service control options -+typedef enum -+{ -+ VCHI_SERVICE_OPTION_MIN, -+ -+ VCHI_SERVICE_OPTION_TRACE, -+ -+ VCHI_SERVICE_OPTION_MAX -+} VCHI_SERVICE_OPTION_T; -+ -+ -+//Callback used by all services / bulk transfers -+typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param -+ VCHI_CALLBACK_REASON_T reason, -+ void *handle ); //for transmitting msg's only -+ -+ -+ ++++ b/drivers/dma/bcm2708-dmaengine.c +@@ -0,0 +1,989 @@ +/* -+ * Define vector struct for scatter-gather (vector) operations -+ * Vectors can be nested - if a vector element has negative length, then -+ * the data pointer is treated as pointing to another vector array, with -+ * '-vec_len' elements. Thus to append a header onto an existing vector, -+ * you can do this: ++ * BCM2835 DMA engine support + * -+ * void foo(const VCHI_MSG_VECTOR_T *v, int n) -+ * { -+ * VCHI_MSG_VECTOR_T nv[2]; -+ * nv[0].vec_base = my_header; -+ * nv[0].vec_len = sizeof my_header; -+ * nv[1].vec_base = v; -+ * nv[1].vec_len = -n; -+ * ... ++ * This driver supports cyclic and scatter/gather DMA transfers. + * -+ */ -+typedef struct vchi_msg_vector { -+ const void *vec_base; -+ int32_t vec_len; -+} VCHI_MSG_VECTOR_T; -+ -+// Opaque type for a connection API -+typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; -+ -+// Opaque type for a message driver -+typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; -+ -+ -+// Iterator structure for reading ahead through received message queue. Allocated by client, -+// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. -+// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - -+// will not proceed to messages received since. Behaviour is undefined if an iterator -+// is used again after messages for that service are removed/dequeued by any -+// means other than vchi_msg_iter_... calls on the iterator itself. -+typedef struct { -+ struct opaque_vchi_service_t *service; -+ void *last; -+ void *next; -+ void *remove; -+} VCHI_MSG_ITER_T; -+ -+ -+#endif // VCHI_COMMON_H_ -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h -new file mode 100644 -index 0000000..198bd07 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h -@@ -0,0 +1,42 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * Author: Florian Meier ++ * Gellert Weisz ++ * Copyright 2013-2014 + * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. ++ * Based on ++ * OMAP DMAengine support by Russell King + * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. ++ * BCM2708 DMA Driver ++ * Copyright (C) 2010 Broadcom + * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * MARVELL MMP Peripheral DMA Driver ++ * Copyright 2012 Marvell International Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. + */ + -+#ifndef VCHI_MH_H_ -+#define VCHI_MH_H_ -+ -+#include -+ -+typedef int32_t VCHI_MEM_HANDLE_T; -+#define VCHI_MEM_HANDLE_INVALID 0 -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h -new file mode 100644 -index 0000000..ad398ba ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h -@@ -0,0 +1,40 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_VCHIQ_H -+#define VCHIQ_VCHIQ_H -+ -+#include "vchiq_if.h" -+#include "vchiq_util.h" -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h -new file mode 100644 -index 0000000..7ea5c64 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h -@@ -0,0 +1,42 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_2835_H -+#define VCHIQ_2835_H -+ -+#include "vchiq_pagelist.h" -+ -+#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0 -+#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 -+ -+#endif /* VCHIQ_2835_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -new file mode 100644 -index 0000000..b3bdaa2 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -@@ -0,0 +1,561 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include ++#include +#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include +#include -+#include -+#include ++#include + -+#include ++#ifndef CONFIG_ARCH_BCM2835 + -+#include -+#include ++/* dma manager */ ++#include + -+#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) ++//#define DMA_COMPLETE DMA_SUCCESS + -+#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 -+#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x)) ++#endif + -+#include "vchiq_arm.h" -+#include "vchiq_2835.h" -+#include "vchiq_connected.h" ++#include ++#include + -+#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) ++#include "virt-dma.h" + -+typedef struct vchiq_2835_state_struct { -+ int inited; -+ VCHIQ_ARM_STATE_T arm_state; -+} VCHIQ_2835_ARM_STATE_T; + -+static char *g_slot_mem; -+static int g_slot_mem_size; -+dma_addr_t g_slot_phys; -+static FRAGMENTS_T *g_fragments_base; -+static FRAGMENTS_T *g_free_fragments; -+struct semaphore g_free_fragments_sema; ++struct bcm2835_dmadev { ++ struct dma_device ddev; ++ spinlock_t lock; ++ void __iomem *base; ++ struct device_dma_parameters dma_parms; ++}; + -+extern int vchiq_arm_log_level; ++struct bcm2835_dma_cb { ++ uint32_t info; ++ uint32_t src; ++ uint32_t dst; ++ uint32_t length; ++ uint32_t stride; ++ uint32_t next; ++ uint32_t pad[2]; ++}; + -+static DEFINE_SEMAPHORE(g_free_fragments_mutex); ++struct bcm2835_chan { ++ struct virt_dma_chan vc; ++ struct list_head node; + -+static irqreturn_t -+vchiq_doorbell_irq(int irq, void *dev_id); ++ struct dma_slave_config cfg; ++ bool cyclic; + -+static int -+create_pagelist(char __user *buf, size_t count, unsigned short type, -+ struct task_struct *task, PAGELIST_T ** ppagelist); ++ int ch; ++ struct bcm2835_desc *desc; + -+static void -+free_pagelist(PAGELIST_T *pagelist, int actual); ++ void __iomem *chan_base; ++ int irq_number; + -+int __init -+vchiq_platform_init(VCHIQ_STATE_T *state) ++ unsigned int dreq; ++}; ++ ++struct bcm2835_desc { ++ struct virt_dma_desc vd; ++ enum dma_transfer_direction dir; ++ ++ unsigned int control_block_size; ++ struct bcm2835_dma_cb *control_block_base; ++ dma_addr_t control_block_base_phys; ++ ++ unsigned int frames; ++ size_t size; ++}; ++ ++#define BCM2835_DMA_CS 0x00 ++#define BCM2835_DMA_ADDR 0x04 ++#define BCM2835_DMA_SOURCE_AD 0x0c ++#define BCM2835_DMA_DEST_AD 0x10 ++#define BCM2835_DMA_NEXTCB 0x1C ++ ++/* DMA CS Control and Status bits */ ++#define BCM2835_DMA_ACTIVE BIT(0) ++#define BCM2835_DMA_INT BIT(2) ++#define BCM2835_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ ++#define BCM2835_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ ++#define BCM2835_DMA_ERR BIT(8) ++#define BCM2835_DMA_ABORT BIT(30) /* Stop current CB, go to next, WO */ ++#define BCM2835_DMA_RESET BIT(31) /* WO, self clearing */ ++ ++#define BCM2835_DMA_INT_EN BIT(0) ++#define BCM2835_DMA_WAIT_RESP BIT(3) ++#define BCM2835_DMA_D_INC BIT(4) ++#define BCM2835_DMA_D_WIDTH BIT(5) ++#define BCM2835_DMA_D_DREQ BIT(6) ++#define BCM2835_DMA_S_INC BIT(8) ++#define BCM2835_DMA_S_WIDTH BIT(9) ++#define BCM2835_DMA_S_DREQ BIT(10) ++ ++#define BCM2835_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2835_DMA_WAITS(x) (((x)&0x1f) << 21) ++ ++#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ ++ ++#define BCM2835_DMA_DATA_TYPE_S8 1 ++#define BCM2835_DMA_DATA_TYPE_S16 2 ++#define BCM2835_DMA_DATA_TYPE_S32 4 ++#define BCM2835_DMA_DATA_TYPE_S128 16 ++ ++#define BCM2835_DMA_BULK_MASK BIT(0) ++#define BCM2835_DMA_FIQ_MASK (BIT(2) | BIT(3)) ++ ++ ++/* Valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ ++#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) ++ ++#define MAX_LITE_TRANSFER 32768 ++#define MAX_NORMAL_TRANSFER 1073741824 ++ ++static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d) +{ -+ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; -+ int frag_mem_size; -+ int err; -+ int i; -+ -+ /* Allocate space for the channels in coherent memory */ -+ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); -+ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS); -+ -+ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size, -+ &g_slot_phys, GFP_ATOMIC); -+ -+ if (!g_slot_mem) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocate channel memory"); -+ err = -ENOMEM; -+ goto failed_alloc; -+ } -+ -+ WARN_ON(((int)g_slot_mem & (PAGE_SIZE - 1)) != 0); -+ -+ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size); -+ if (!vchiq_slot_zero) { -+ err = -EINVAL; -+ goto failed_init_slots; -+ } -+ -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = -+ (int)g_slot_phys + g_slot_mem_size; -+ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = -+ MAX_FRAGMENTS; -+ -+ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size); -+ g_slot_mem_size += frag_mem_size; -+ -+ g_free_fragments = g_fragments_base; -+ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { -+ *(FRAGMENTS_T **)&g_fragments_base[i] = -+ &g_fragments_base[i + 1]; -+ } -+ *(FRAGMENTS_T **)&g_fragments_base[i] = NULL; -+ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); -+ -+ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) != -+ VCHIQ_SUCCESS) { -+ err = -EINVAL; -+ goto failed_vchiq_init; -+ } -+ -+ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq, -+ IRQF_IRQPOLL, "VCHIQ doorbell", -+ state); -+ if (err < 0) { -+ vchiq_log_error(vchiq_arm_log_level, "%s: failed to register " -+ "irq=%d err=%d", __func__, -+ VCHIQ_DOORBELL_IRQ, err); -+ goto failed_request_irq; -+ } -+ -+ /* Send the base address of the slots to VideoCore */ -+ -+ dsb(); /* Ensure all writes have completed */ -+ -+ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys); -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq_init - done (slots %x, phys %x)", -+ (unsigned int)vchiq_slot_zero, g_slot_phys); -+ -+ vchiq_call_connected_callbacks(); -+ -+ return 0; -+ -+failed_request_irq: -+failed_vchiq_init: -+failed_init_slots: -+ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys); -+ -+failed_alloc: -+ return err; ++ return container_of(d, struct bcm2835_dmadev, ddev); +} + -+void __exit -+vchiq_platform_exit(VCHIQ_STATE_T *state) ++static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c) +{ -+ free_irq(VCHIQ_DOORBELL_IRQ, state); -+ dma_free_coherent(NULL, g_slot_mem_size, -+ g_slot_mem, g_slot_phys); ++ return container_of(c, struct bcm2835_chan, vc.chan); +} + -+ -+VCHIQ_STATUS_T -+vchiq_platform_init_state(VCHIQ_STATE_T *state) ++static inline struct bcm2835_desc *to_bcm2835_dma_desc( ++ struct dma_async_tx_descriptor *t) +{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ state->platform_state = kzalloc(sizeof(VCHIQ_2835_ARM_STATE_T), GFP_KERNEL); -+ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 1; -+ status = vchiq_arm_init_state(state, &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state); -+ if(status != VCHIQ_SUCCESS) -+ { -+ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 0; -+ } -+ return status; ++ return container_of(t, struct bcm2835_desc, vd.tx); +} + -+VCHIQ_ARM_STATE_T* -+vchiq_platform_get_arm_state(VCHIQ_STATE_T *state) ++static void dma_dumpregs(struct bcm2835_chan *c) +{ -+ if(!((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited) -+ { -+ BUG(); -+ } -+ return &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state; ++ pr_debug("-------------DMA DUMPREGS-------------\n"); ++ pr_debug("CS= %u\n", ++ readl(c->chan_base + BCM2835_DMA_CS)); ++ pr_debug("ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_ADDR)); ++ pr_debug("SOURCE_ADDR= %u\n", ++ readl(c->chan_base + BCM2835_DMA_SOURCE_AD)); ++ pr_debug("DEST_AD= %u\n", ++ readl(c->chan_base + BCM2835_DMA_DEST_AD)); ++ pr_debug("NEXTCB= %u\n", ++ readl(c->chan_base + BCM2835_DMA_NEXTCB)); ++ pr_debug("--------------------------------------\n"); +} + -+void -+remote_event_signal(REMOTE_EVENT_T *event) ++static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) +{ -+ wmb(); -+ -+ event->fired = 1; -+ -+ dsb(); /* data barrier operation */ -+ -+ if (event->armed) { -+ /* trigger vc interrupt */ -+ -+ writel(0, __io_address(ARM_0_BELL2)); -+ } ++ struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd); ++ dma_free_coherent(desc->vd.tx.chan->device->dev, ++ desc->control_block_size, ++ desc->control_block_base, ++ desc->control_block_base_phys); ++ kfree(desc); +} + -+int -+vchiq_copy_from_user(void *dst, const void *src, int size) ++static int bcm2835_dma_abort(void __iomem *chan_base) +{ -+ if ((uint32_t)src < TASK_SIZE) { -+ return copy_from_user(dst, src, size); -+ } else { -+ memcpy(dst, src, size); ++ unsigned long cs; ++ long int timeout = 10000; ++ ++ cs = readl(chan_base + BCM2835_DMA_CS); ++ if (!(cs & BCM2835_DMA_ACTIVE)) + return 0; ++ ++ /* Write 0 to the active bit - Pause the DMA */ ++ writel(0, chan_base + BCM2835_DMA_CS); ++ ++ /* Wait for any current AXI transfer to complete */ ++ while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) { ++ cpu_relax(); ++ cs = readl(chan_base + BCM2835_DMA_CS); + } ++ ++ /* We'll un-pause when we set of our next DMA */ ++ if (!timeout) ++ return -ETIMEDOUT; ++ ++ if (!(cs & BCM2835_DMA_ACTIVE)) ++ return 0; ++ ++ /* Terminate the control block chain */ ++ writel(0, chan_base + BCM2835_DMA_NEXTCB); ++ ++ /* Abort the whole DMA */ ++ writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, ++ chan_base + BCM2835_DMA_CS); ++ ++ return 0; +} + -+VCHIQ_STATUS_T -+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, -+ void *offset, int size, int dir) ++ ++static void bcm2835_dma_start_desc(struct bcm2835_chan *c) +{ -+ PAGELIST_T *pagelist; ++ struct virt_dma_desc *vd = vchan_next_desc(&c->vc); ++ struct bcm2835_desc *d; ++ ++ if (!vd) { ++ c->desc = NULL; ++ return; ++ } ++ ++ list_del(&vd->node); ++ ++ c->desc = d = to_bcm2835_dma_desc(&vd->tx); ++ ++ writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR); ++ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); ++ ++} ++ ++static irqreturn_t bcm2835_dma_callback(int irq, void *data) ++{ ++ struct bcm2835_chan *c = data; ++ struct bcm2835_desc *d; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Acknowledge interrupt */ ++ writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); ++ ++ d = c->desc; ++ ++ if (d) { ++ if (c->cyclic) { ++ vchan_cyclic_callback(&d->vd); ++ ++ /* Keep the DMA engine running */ ++ writel(BCM2835_DMA_ACTIVE, ++ c->chan_base + BCM2835_DMA_CS); ++ ++ } else { ++ vchan_cookie_complete(&c->desc->vd); ++ bcm2835_dma_start_desc(c); ++ } ++ } ++ ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + int ret; + -+ WARN_ON(memhandle != VCHI_MEM_HANDLE_INVALID); ++ dev_dbg(c->vc.chan.device->dev, ++ "Allocating DMA channel %d\n", c->ch); + -+ ret = create_pagelist((char __user *)offset, size, -+ (dir == VCHIQ_BULK_RECEIVE) -+ ? PAGELIST_READ -+ : PAGELIST_WRITE, -+ current, -+ &pagelist); -+ if (ret != 0) -+ return VCHIQ_ERROR; -+ -+ bulk->handle = memhandle; -+ bulk->data = VCHIQ_ARM_ADDRESS(pagelist); -+ -+ /* Store the pagelist address in remote_data, which isn't used by the -+ slave. */ -+ bulk->remote_data = pagelist; -+ -+ return VCHIQ_SUCCESS; -+} -+ -+void -+vchiq_complete_bulk(VCHIQ_BULK_T *bulk) -+{ -+ if (bulk && bulk->remote_data && bulk->actual) -+ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual); -+} -+ -+void -+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk) -+{ -+ /* -+ * This should only be called on the master (VideoCore) side, but -+ * provide an implementation to avoid the need for ifdefery. -+ */ -+ BUG(); -+} -+ -+void -+vchiq_dump_platform_state(void *dump_context) -+{ -+ char buf[80]; -+ int len; -+ len = snprintf(buf, sizeof(buf), -+ " Platform: 2835 (VC master)"); -+ vchiq_dump(dump_context, buf, len + 1); -+} -+ -+VCHIQ_STATUS_T -+vchiq_platform_suspend(VCHIQ_STATE_T *state) -+{ -+ return VCHIQ_ERROR; -+} -+ -+VCHIQ_STATUS_T -+vchiq_platform_resume(VCHIQ_STATE_T *state) -+{ -+ return VCHIQ_SUCCESS; -+} -+ -+void -+vchiq_platform_paused(VCHIQ_STATE_T *state) -+{ -+} -+ -+void -+vchiq_platform_resumed(VCHIQ_STATE_T *state) -+{ -+} -+ -+int -+vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state) -+{ -+ return 1; // autosuspend not supported - videocore always wanted -+} -+ -+int -+vchiq_platform_use_suspend_timer(void) -+{ -+ return 0; -+} -+void -+vchiq_dump_platform_use_state(VCHIQ_STATE_T *state) -+{ -+ vchiq_log_info((vchiq_arm_log_level>=VCHIQ_LOG_INFO),"Suspend timer not in use"); -+} -+void -+vchiq_platform_handle_timeout(VCHIQ_STATE_T *state) -+{ -+ (void)state; -+} -+/* -+ * Local functions -+ */ -+ -+static irqreturn_t -+vchiq_doorbell_irq(int irq, void *dev_id) -+{ -+ VCHIQ_STATE_T *state = dev_id; -+ irqreturn_t ret = IRQ_NONE; -+ unsigned int status; -+ -+ /* Read (and clear) the doorbell */ -+ status = readl(__io_address(ARM_0_BELL0)); -+ -+ if (status & 0x4) { /* Was the doorbell rung? */ -+ remote_event_pollall(state); -+ ret = IRQ_HANDLED; -+ } ++ ret = request_irq(c->irq_number, ++ bcm2835_dma_callback, 0, "DMA IRQ", c); + + return ret; +} + -+/* There is a potential problem with partial cache lines (pages?) -+** at the ends of the block when reading. If the CPU accessed anything in -+** the same line (page?) then it may have pulled old data into the cache, -+** obscuring the new data underneath. We can solve this by transferring the -+** partial cache lines separately, and allowing the ARM to copy into the -+** cached area. -+ -+** N.B. This implementation plays slightly fast and loose with the Linux -+** driver programming rules, e.g. its use of __virt_to_bus instead of -+** dma_map_single, but it isn't a multi-platform driver and it benefits -+** from increased speed as a result. -+*/ -+ -+static int -+create_pagelist(char __user *buf, size_t count, unsigned short type, -+ struct task_struct *task, PAGELIST_T ** ppagelist) ++static void bcm2835_dma_free_chan_resources(struct dma_chan *chan) +{ -+ PAGELIST_T *pagelist; -+ struct page **pages; -+ struct page *page; -+ unsigned long *addrs; -+ unsigned int num_pages, offset, i; -+ char *addr, *base_addr, *next_addr; -+ int run, addridx, actual_pages; -+ unsigned long *need_release; ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); + -+ offset = (unsigned int)buf & (PAGE_SIZE - 1); -+ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; ++ vchan_free_chan_resources(&c->vc); ++ free_irq(c->irq_number, c); + -+ *ppagelist = NULL; -+ -+ /* Allocate enough storage to hold the page pointers and the page -+ ** list -+ */ -+ pagelist = kmalloc(sizeof(PAGELIST_T) + -+ (num_pages * sizeof(unsigned long)) + -+ sizeof(unsigned long) + -+ (num_pages * sizeof(pages[0])), -+ GFP_KERNEL); -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "create_pagelist - %x", (unsigned int)pagelist); -+ if (!pagelist) -+ return -ENOMEM; -+ -+ addrs = pagelist->addrs; -+ need_release = (unsigned long *)(addrs + num_pages); -+ pages = (struct page **)(addrs + num_pages + 1); -+ -+ if (is_vmalloc_addr(buf)) { -+ for (actual_pages = 0; actual_pages < num_pages; actual_pages++) { -+ pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE)); -+ } -+ *need_release = 0; /* do not try and release vmalloc pages */ -+ } else { -+ down_read(&task->mm->mmap_sem); -+ actual_pages = get_user_pages(task, task->mm, -+ (unsigned long)buf & ~(PAGE_SIZE - 1), -+ num_pages, -+ (type == PAGELIST_READ) /*Write */ , -+ 0 /*Force */ , -+ pages, -+ NULL /*vmas */); -+ up_read(&task->mm->mmap_sem); -+ -+ if (actual_pages != num_pages) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "create_pagelist - only %d/%d pages locked", -+ actual_pages, -+ num_pages); -+ -+ /* This is probably due to the process being killed */ -+ while (actual_pages > 0) -+ { -+ actual_pages--; -+ page_cache_release(pages[actual_pages]); -+ } -+ kfree(pagelist); -+ if (actual_pages == 0) -+ actual_pages = -ENOMEM; -+ return actual_pages; -+ } -+ *need_release = 1; /* release user pages */ -+ } -+ -+ pagelist->length = count; -+ pagelist->type = type; -+ pagelist->offset = offset; -+ -+ /* Group the pages into runs of contiguous pages */ -+ -+ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0])); -+ next_addr = base_addr + PAGE_SIZE; -+ addridx = 0; -+ run = 0; -+ -+ for (i = 1; i < num_pages; i++) { -+ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i])); -+ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) { -+ next_addr += PAGE_SIZE; -+ run++; -+ } else { -+ addrs[addridx] = (unsigned long)base_addr + run; -+ addridx++; -+ base_addr = addr; -+ next_addr = addr + PAGE_SIZE; -+ run = 0; -+ } -+ } -+ -+ addrs[addridx] = (unsigned long)base_addr + run; -+ addridx++; -+ -+ /* Partial cache lines (fragments) require special measures */ -+ if ((type == PAGELIST_READ) && -+ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) || -+ ((pagelist->offset + pagelist->length) & -+ (CACHE_LINE_SIZE - 1)))) { -+ FRAGMENTS_T *fragments; -+ -+ if (down_interruptible(&g_free_fragments_sema) != 0) { -+ kfree(pagelist); -+ return -EINTR; -+ } -+ -+ WARN_ON(g_free_fragments == NULL); -+ -+ down(&g_free_fragments_mutex); -+ fragments = (FRAGMENTS_T *) g_free_fragments; -+ WARN_ON(fragments == NULL); -+ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments; -+ up(&g_free_fragments_mutex); -+ pagelist->type = -+ PAGELIST_READ_WITH_FRAGMENTS + (fragments - -+ g_fragments_base); -+ } -+ -+ for (page = virt_to_page(pagelist); -+ page <= virt_to_page(addrs + num_pages - 1); page++) { -+ flush_dcache_page(page); -+ } -+ -+ *ppagelist = pagelist; -+ -+ return 0; ++ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); +} + -+static void -+free_pagelist(PAGELIST_T *pagelist, int actual) ++static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d) +{ -+ unsigned long *need_release; -+ struct page **pages; -+ unsigned int num_pages, i; -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "free_pagelist - %x, %d", (unsigned int)pagelist, actual); -+ -+ num_pages = -+ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / -+ PAGE_SIZE; -+ -+ need_release = (unsigned long *)(pagelist->addrs + num_pages); -+ pages = (struct page **)(pagelist->addrs + num_pages + 1); -+ -+ /* Deal with any partial cache lines (fragments) */ -+ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { -+ FRAGMENTS_T *fragments = g_fragments_base + -+ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS); -+ int head_bytes, tail_bytes; -+ head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & -+ (CACHE_LINE_SIZE - 1); -+ tail_bytes = (pagelist->offset + actual) & -+ (CACHE_LINE_SIZE - 1); -+ -+ if ((actual >= 0) && (head_bytes != 0)) { -+ if (head_bytes > actual) -+ head_bytes = actual; -+ -+ memcpy((char *)page_address(pages[0]) + -+ pagelist->offset, -+ fragments->headbuf, -+ head_bytes); -+ } -+ if ((actual >= 0) && (head_bytes < actual) && -+ (tail_bytes != 0)) { -+ memcpy((char *)page_address(pages[num_pages - 1]) + -+ ((pagelist->offset + actual) & -+ (PAGE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)), -+ fragments->tailbuf, tail_bytes); -+ } -+ -+ down(&g_free_fragments_mutex); -+ *(FRAGMENTS_T **) fragments = g_free_fragments; -+ g_free_fragments = fragments; -+ up(&g_free_fragments_mutex); -+ up(&g_free_fragments_sema); -+ } -+ -+ if (*need_release) { -+ for (i = 0; i < num_pages; i++) { -+ if (pagelist->type != PAGELIST_WRITE) -+ set_page_dirty(pages[i]); -+ -+ page_cache_release(pages[i]); -+ } -+ } -+ -+ kfree(pagelist); -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -new file mode 100644 -index 0000000..2596818 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -0,0 +1,2883 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "vchiq_core.h" -+#include "vchiq_ioctl.h" -+#include "vchiq_arm.h" -+#include "vchiq_debugfs.h" -+ -+#define DEVICE_NAME "vchiq" -+ -+/* Override the default prefix, which would be vchiq_arm (from the filename) */ -+#undef MODULE_PARAM_PREFIX -+#define MODULE_PARAM_PREFIX DEVICE_NAME "." -+ -+#define VCHIQ_MINOR 0 -+ -+/* Some per-instance constants */ -+#define MAX_COMPLETIONS 16 -+#define MAX_SERVICES 64 -+#define MAX_ELEMENTS 8 -+#define MSG_QUEUE_SIZE 64 -+ -+#define KEEPALIVE_VER 1 -+#define KEEPALIVE_VER_MIN KEEPALIVE_VER -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_susp_log_level = VCHIQ_LOG_ERROR; -+ -+#define SUSPEND_TIMER_TIMEOUT_MS 100 -+#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 -+ -+#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ -+static const char *const suspend_state_names[] = { -+ "VC_SUSPEND_FORCE_CANCELED", -+ "VC_SUSPEND_REJECTED", -+ "VC_SUSPEND_FAILED", -+ "VC_SUSPEND_IDLE", -+ "VC_SUSPEND_REQUESTED", -+ "VC_SUSPEND_IN_PROGRESS", -+ "VC_SUSPEND_SUSPENDED" -+}; -+#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ -+static const char *const resume_state_names[] = { -+ "VC_RESUME_FAILED", -+ "VC_RESUME_IDLE", -+ "VC_RESUME_REQUESTED", -+ "VC_RESUME_IN_PROGRESS", -+ "VC_RESUME_RESUMED" -+}; -+/* The number of times we allow force suspend to timeout before actually -+** _forcing_ suspend. This is to cater for SW which fails to release vchiq -+** correctly - we don't want to prevent ARM suspend indefinitely in this case. -+*/ -+#define FORCE_SUSPEND_FAIL_MAX 8 -+ -+/* The time in ms allowed for videocore to go idle when force suspend has been -+ * requested */ -+#define FORCE_SUSPEND_TIMEOUT_MS 200 -+ -+ -+static void suspend_timer_callback(unsigned long context); -+ -+ -+typedef struct user_service_struct { -+ VCHIQ_SERVICE_T *service; -+ void *userdata; -+ VCHIQ_INSTANCE_T instance; -+ char is_vchi; -+ char dequeue_pending; -+ char close_pending; -+ int message_available_pos; -+ int msg_insert; -+ int msg_remove; -+ struct semaphore insert_event; -+ struct semaphore remove_event; -+ struct semaphore close_event; -+ VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; -+} USER_SERVICE_T; -+ -+struct bulk_waiter_node { -+ struct bulk_waiter bulk_waiter; -+ int pid; -+ struct list_head list; -+}; -+ -+struct vchiq_instance_struct { -+ VCHIQ_STATE_T *state; -+ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; -+ int completion_insert; -+ int completion_remove; -+ struct semaphore insert_event; -+ struct semaphore remove_event; -+ struct mutex completion_mutex; -+ -+ int connected; -+ int closing; -+ int pid; -+ int mark; -+ int use_close_delivered; -+ int trace; -+ -+ struct list_head bulk_waiter_list; -+ struct mutex bulk_waiter_list_mutex; -+ -+ VCHIQ_DEBUGFS_NODE_T debugfs_node; -+}; -+ -+typedef struct dump_context_struct { -+ char __user *buf; -+ size_t actual; -+ size_t space; -+ loff_t offset; -+} DUMP_CONTEXT_T; -+ -+static struct cdev vchiq_cdev; -+static dev_t vchiq_devid; -+static VCHIQ_STATE_T g_state; -+static struct class *vchiq_class; -+static struct device *vchiq_dev; -+static DEFINE_SPINLOCK(msg_queue_spinlock); -+ -+static const char *const ioctl_names[] = { -+ "CONNECT", -+ "SHUTDOWN", -+ "CREATE_SERVICE", -+ "REMOVE_SERVICE", -+ "QUEUE_MESSAGE", -+ "QUEUE_BULK_TRANSMIT", -+ "QUEUE_BULK_RECEIVE", -+ "AWAIT_COMPLETION", -+ "DEQUEUE_MESSAGE", -+ "GET_CLIENT_ID", -+ "GET_CONFIG", -+ "CLOSE_SERVICE", -+ "USE_SERVICE", -+ "RELEASE_SERVICE", -+ "SET_SERVICE_OPTION", -+ "DUMP_PHYS_MEM", -+ "LIB_VERSION", -+ "CLOSE_DELIVERED" -+}; -+ -+vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == -+ (VCHIQ_IOC_MAX + 1)); -+ -+static void -+dump_phys_mem(void *virt_addr, uint32_t num_bytes); -+ -+/**************************************************************************** -+* -+* add_completion -+* -+***************************************************************************/ -+ -+static VCHIQ_STATUS_T -+add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service, -+ void *bulk_userdata) -+{ -+ VCHIQ_COMPLETION_DATA_T *completion; -+ DEBUG_INITIALISE(g_state.local) -+ -+ while (instance->completion_insert == -+ (instance->completion_remove + MAX_COMPLETIONS)) { -+ /* Out of space - wait for the client */ -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ vchiq_log_trace(vchiq_arm_log_level, -+ "add_completion - completion queue full"); -+ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); -+ if (down_interruptible(&instance->remove_event) != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback interrupted"); -+ return VCHIQ_RETRY; -+ } else if (instance->closing) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback closing"); -+ return VCHIQ_ERROR; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ } -+ -+ completion = -+ &instance->completions[instance->completion_insert & -+ (MAX_COMPLETIONS - 1)]; -+ -+ completion->header = header; -+ completion->reason = reason; -+ /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ -+ completion->service_userdata = user_service->service; -+ completion->bulk_userdata = bulk_userdata; -+ -+ if (reason == VCHIQ_SERVICE_CLOSED) { -+ /* Take an extra reference, to be held until -+ this CLOSED notification is delivered. */ -+ lock_service(user_service->service); -+ if (instance->use_close_delivered) -+ user_service->close_pending = 1; -+ } -+ -+ /* A write barrier is needed here to ensure that the entire completion -+ record is written out before the insert point. */ -+ wmb(); -+ -+ if (reason == VCHIQ_MESSAGE_AVAILABLE) -+ user_service->message_available_pos = -+ instance->completion_insert; -+ instance->completion_insert++; -+ -+ up(&instance->insert_event); -+ -+ return VCHIQ_SUCCESS; ++ return d->size; +} + -+/**************************************************************************** -+* -+* service_callback -+* -+***************************************************************************/ -+ -+static VCHIQ_STATUS_T -+service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, -+ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) ++static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) +{ -+ /* How do we ensure the callback goes to the right client? -+ ** The service_user data points to a USER_SERVICE_T record containing -+ ** the original callback and the user state structure, which contains a -+ ** circular buffer for completion records. -+ */ -+ USER_SERVICE_T *user_service; -+ VCHIQ_SERVICE_T *service; -+ VCHIQ_INSTANCE_T instance; -+ DEBUG_INITIALISE(g_state.local) ++ unsigned int i; ++ size_t size; + -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ for (size = i = 0; i < d->frames; i++) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[i]; ++ size_t this_size = control_block->length; ++ dma_addr_t dma; + -+ service = handle_to_service(handle); -+ BUG_ON(!service); -+ user_service = (USER_SERVICE_T *)service->base.userdata; -+ instance = user_service->instance; -+ -+ if (!instance || instance->closing) -+ return VCHIQ_SUCCESS; -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "service_callback - service %lx(%d,%p), reason %d, header %lx, " -+ "instance %lx, bulk_userdata %lx", -+ (unsigned long)user_service, -+ service->localport, user_service->userdata, -+ reason, (unsigned long)header, -+ (unsigned long)instance, (unsigned long)bulk_userdata); -+ -+ if (header && user_service->is_vchi) { -+ spin_lock(&msg_queue_spinlock); -+ while (user_service->msg_insert == -+ (user_service->msg_remove + MSG_QUEUE_SIZE)) { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); -+ vchiq_log_trace(vchiq_arm_log_level, -+ "service_callback - msg queue full"); -+ /* If there is no MESSAGE_AVAILABLE in the completion -+ ** queue, add one -+ */ -+ if ((user_service->message_available_pos - -+ instance->completion_remove) < 0) { -+ VCHIQ_STATUS_T status; -+ vchiq_log_info(vchiq_arm_log_level, -+ "Inserting extra MESSAGE_AVAILABLE"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ status = add_completion(instance, reason, -+ NULL, user_service, bulk_userdata); -+ if (status != VCHIQ_SUCCESS) { -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return status; -+ } -+ } -+ -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ if (down_interruptible(&user_service->remove_event) -+ != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback interrupted"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return VCHIQ_RETRY; -+ } else if (instance->closing) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "service_callback closing"); -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ return VCHIQ_ERROR; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ spin_lock(&msg_queue_spinlock); -+ } -+ -+ user_service->msg_queue[user_service->msg_insert & -+ (MSG_QUEUE_SIZE - 1)] = header; -+ user_service->msg_insert++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ up(&user_service->insert_event); -+ -+ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if -+ ** there is a MESSAGE_AVAILABLE in the completion queue then -+ ** bypass the completion queue. -+ */ -+ if (((user_service->message_available_pos - -+ instance->completion_remove) >= 0) || -+ user_service->dequeue_pending) { -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ user_service->dequeue_pending = 0; -+ return VCHIQ_SUCCESS; -+ } -+ -+ header = NULL; -+ } -+ DEBUG_TRACE(SERVICE_CALLBACK_LINE); -+ -+ return add_completion(instance, reason, header, user_service, -+ bulk_userdata); -+} -+ -+/**************************************************************************** -+* -+* user_service_free -+* -+***************************************************************************/ -+static void -+user_service_free(void *userdata) -+{ -+ kfree(userdata); -+} -+ -+/**************************************************************************** -+* -+* close_delivered -+* -+***************************************************************************/ -+static void close_delivered(USER_SERVICE_T *user_service) -+{ -+ vchiq_log_info(vchiq_arm_log_level, -+ "close_delivered(handle=%x)", -+ user_service->service->handle); -+ -+ if (user_service->close_pending) { -+ /* Allow the underlying service to be culled */ -+ unlock_service(user_service->service); -+ -+ /* Wake the user-thread blocked in close_ or remove_service */ -+ up(&user_service->close_event); -+ -+ user_service->close_pending = 0; -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_ioctl -+* -+***************************************************************************/ -+static long -+vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ VCHIQ_INSTANCE_T instance = file->private_data; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ VCHIQ_SERVICE_T *service = NULL; -+ long ret = 0; -+ int i, rc; -+ DEBUG_INITIALISE(g_state.local) -+ -+ vchiq_log_trace(vchiq_arm_log_level, -+ "vchiq_ioctl - instance %x, cmd %s, arg %lx", -+ (unsigned int)instance, -+ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? -+ ioctl_names[_IOC_NR(cmd)] : "", arg); -+ -+ switch (cmd) { -+ case VCHIQ_IOC_SHUTDOWN: -+ if (!instance->connected) -+ break; -+ -+ /* Remove all services */ -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ status = vchiq_remove_service(service->handle); -+ unlock_service(service); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ service = NULL; -+ -+ if (status == VCHIQ_SUCCESS) { -+ /* Wake the completion thread and ask it to exit */ -+ instance->closing = 1; -+ up(&instance->insert_event); -+ } -+ -+ break; -+ -+ case VCHIQ_IOC_CONNECT: -+ if (instance->connected) { -+ ret = -EINVAL; -+ break; -+ } -+ rc = mutex_lock_interruptible(&instance->state->mutex); -+ if (rc != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq: connect: could not lock mutex for " -+ "state %d: %d", -+ instance->state->id, rc); -+ ret = -EINTR; -+ break; -+ } -+ status = vchiq_connect_internal(instance->state, instance); -+ mutex_unlock(&instance->state->mutex); -+ -+ if (status == VCHIQ_SUCCESS) -+ instance->connected = 1; ++ if (d->dir == DMA_DEV_TO_MEM) ++ dma = control_block->dst; + else -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq: could not connect: %d", status); -+ break; ++ dma = control_block->src; + -+ case VCHIQ_IOC_CREATE_SERVICE: { -+ VCHIQ_CREATE_SERVICE_T args; -+ USER_SERVICE_T *user_service = NULL; -+ void *userdata; -+ int srvstate; ++ if (size) ++ size += this_size; ++ else if (addr >= dma && addr < dma + this_size) ++ size += dma + this_size - addr; ++ } + -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } ++ return size; ++} + -+ user_service = kmalloc(sizeof(USER_SERVICE_T), GFP_KERNEL); -+ if (!user_service) { -+ ret = -ENOMEM; -+ break; -+ } ++static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, ++ dma_cookie_t cookie, struct dma_tx_state *txstate) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ struct bcm2835_desc *d; ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ dma_addr_t pos; + -+ if (args.is_open) { -+ if (!instance->connected) { -+ ret = -ENOTCONN; -+ kfree(user_service); -+ break; -+ } -+ srvstate = VCHIQ_SRVSTATE_OPENING; -+ } else { -+ srvstate = -+ instance->connected ? -+ VCHIQ_SRVSTATE_LISTENING : -+ VCHIQ_SRVSTATE_HIDDEN; -+ } ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_COMPLETE || !txstate) ++ return ret; + -+ userdata = args.params.userdata; -+ args.params.callback = service_callback; -+ args.params.userdata = user_service; -+ service = vchiq_add_service_internal( -+ instance->state, -+ &args.params, srvstate, -+ instance, user_service_free); ++ spin_lock_irqsave(&c->vc.lock, flags); ++ vd = vchan_find_desc(&c->vc, cookie); ++ if (vd) { ++ txstate->residue = ++ bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx)); ++ } else if (c->desc && c->desc->vd.tx.cookie == cookie) { ++ d = c->desc; + -+ if (service != NULL) { -+ user_service->service = service; -+ user_service->userdata = userdata; -+ user_service->instance = instance; -+ user_service->is_vchi = (args.is_vchi != 0); -+ user_service->dequeue_pending = 0; -+ user_service->close_pending = 0; -+ user_service->message_available_pos = -+ instance->completion_remove - 1; -+ user_service->msg_insert = 0; -+ user_service->msg_remove = 0; -+ sema_init(&user_service->insert_event, 0); -+ sema_init(&user_service->remove_event, 0); -+ sema_init(&user_service->close_event, 0); -+ -+ if (args.is_open) { -+ status = vchiq_open_service_internal -+ (service, instance->pid); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_remove_service(service->handle); -+ service = NULL; -+ ret = (status == VCHIQ_RETRY) ? -+ -EINTR : -EIO; -+ break; -+ } -+ } -+ -+ if (copy_to_user((void __user *) -+ &(((VCHIQ_CREATE_SERVICE_T __user *) -+ arg)->handle), -+ (const void *)&service->handle, -+ sizeof(service->handle)) != 0) { -+ ret = -EFAULT; -+ vchiq_remove_service(service->handle); -+ } -+ -+ service = NULL; -+ } else { -+ ret = -EEXIST; -+ kfree(user_service); -+ } -+ } break; -+ -+ case VCHIQ_IOC_CLOSE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ /* close_pending is false on first entry, and when the -+ wait in vchiq_close_service has been interrupted. */ -+ if (!user_service->close_pending) { -+ status = vchiq_close_service(service->handle); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ -+ /* close_pending is true once the underlying service -+ has been closed until the client library calls the -+ CLOSE_DELIVERED ioctl, signalling close_event. */ -+ if (user_service->close_pending && -+ down_interruptible(&user_service->close_event)) -+ status = VCHIQ_RETRY; -+ } ++ if (d->dir == DMA_MEM_TO_DEV) ++ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); ++ else if (d->dir == DMA_DEV_TO_MEM) ++ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); + else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_REMOVE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ /* close_pending is false on first entry, and when the -+ wait in vchiq_close_service has been interrupted. */ -+ if (!user_service->close_pending) { -+ status = vchiq_remove_service(service->handle); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ -+ /* close_pending is true once the underlying service -+ has been closed until the client library calls the -+ CLOSE_DELIVERED ioctl, signalling close_event. */ -+ if (user_service->close_pending && -+ down_interruptible(&user_service->close_event)) -+ status = VCHIQ_RETRY; -+ } -+ else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_USE_SERVICE: -+ case VCHIQ_IOC_RELEASE_SERVICE: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_service_for_instance(instance, handle); -+ if (service != NULL) { -+ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? -+ vchiq_use_service_internal(service) : -+ vchiq_release_service_internal(service); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s: cmd %s returned error %d for " -+ "service %c%c%c%c:%03d", -+ __func__, -+ (cmd == VCHIQ_IOC_USE_SERVICE) ? -+ "VCHIQ_IOC_USE_SERVICE" : -+ "VCHIQ_IOC_RELEASE_SERVICE", -+ status, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->client_id); -+ ret = -EINVAL; -+ } -+ } else -+ ret = -EINVAL; -+ } break; -+ -+ case VCHIQ_IOC_QUEUE_MESSAGE: { -+ VCHIQ_QUEUE_MESSAGE_T args; -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ -+ if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { -+ /* Copy elements into kernel space */ -+ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; -+ if (copy_from_user(elements, args.elements, -+ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) -+ status = vchiq_queue_message -+ (args.handle, -+ elements, args.count); -+ else -+ ret = -EFAULT; -+ } else { -+ ret = -EINVAL; -+ } -+ } break; -+ -+ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: -+ case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { -+ VCHIQ_QUEUE_BULK_TRANSFER_T args; -+ struct bulk_waiter_node *waiter = NULL; -+ VCHIQ_BULK_DIR_T dir = -+ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? -+ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; -+ -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ if (args.mode == VCHIQ_BULK_MODE_BLOCKING) { -+ waiter = kzalloc(sizeof(struct bulk_waiter_node), -+ GFP_KERNEL); -+ if (!waiter) { -+ ret = -ENOMEM; -+ break; -+ } -+ args.userdata = &waiter->bulk_waiter; -+ } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { -+ struct list_head *pos; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_for_each(pos, &instance->bulk_waiter_list) { -+ if (list_entry(pos, struct bulk_waiter_node, -+ list)->pid == current->pid) { -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ break; -+ } -+ -+ } -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ if (!waiter) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "no bulk_waiter found for pid %d", -+ current->pid); -+ ret = -ESRCH; -+ break; -+ } -+ vchiq_log_info(vchiq_arm_log_level, -+ "found bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); -+ args.userdata = &waiter->bulk_waiter; -+ } -+ status = vchiq_bulk_transfer -+ (args.handle, -+ VCHI_MEM_HANDLE_INVALID, -+ args.data, args.size, -+ args.userdata, args.mode, -+ dir); -+ if (!waiter) -+ break; -+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || -+ !waiter->bulk_waiter.bulk) { -+ if (waiter->bulk_waiter.bulk) { -+ /* Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ waiter->bulk_waiter.bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); -+ } -+ kfree(waiter); -+ } else { -+ const VCHIQ_BULK_MODE_T mode_waiting = -+ VCHIQ_BULK_MODE_WAITING; -+ waiter->pid = current->pid; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_add(&waiter->list, &instance->bulk_waiter_list); -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ vchiq_log_info(vchiq_arm_log_level, -+ "saved bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); -+ -+ if (copy_to_user((void __user *) -+ &(((VCHIQ_QUEUE_BULK_TRANSFER_T __user *) -+ arg)->mode), -+ (const void *)&mode_waiting, -+ sizeof(mode_waiting)) != 0) -+ ret = -EFAULT; -+ } -+ } break; -+ -+ case VCHIQ_IOC_AWAIT_COMPLETION: { -+ VCHIQ_AWAIT_COMPLETION_T args; -+ -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ if (!instance->connected) { -+ ret = -ENOTCONN; -+ break; -+ } -+ -+ if (copy_from_user(&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ mutex_lock(&instance->completion_mutex); -+ -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ while ((instance->completion_remove == -+ instance->completion_insert) -+ && !instance->closing) { -+ int rc; -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ mutex_unlock(&instance->completion_mutex); -+ rc = down_interruptible(&instance->insert_event); -+ mutex_lock(&instance->completion_mutex); -+ if (rc != 0) { -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ vchiq_log_info(vchiq_arm_log_level, -+ "AWAIT_COMPLETION interrupted"); -+ ret = -EINTR; -+ break; -+ } -+ } -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ -+ /* A read memory barrier is needed to stop prefetch of a stale -+ ** completion record -+ */ -+ rmb(); -+ -+ if (ret == 0) { -+ int msgbufcount = args.msgbufcount; -+ for (ret = 0; ret < args.count; ret++) { -+ VCHIQ_COMPLETION_DATA_T *completion; -+ VCHIQ_SERVICE_T *service; -+ USER_SERVICE_T *user_service; -+ VCHIQ_HEADER_T *header; -+ if (instance->completion_remove == -+ instance->completion_insert) -+ break; -+ completion = &instance->completions[ -+ instance->completion_remove & -+ (MAX_COMPLETIONS - 1)]; -+ -+ service = completion->service_userdata; -+ user_service = service->base.userdata; -+ completion->service_userdata = -+ user_service->userdata; -+ -+ header = completion->header; -+ if (header) { -+ void __user *msgbuf; -+ int msglen; -+ -+ msglen = header->size + -+ sizeof(VCHIQ_HEADER_T); -+ /* This must be a VCHIQ-style service */ -+ if (args.msgbufsize < msglen) { -+ vchiq_log_error( -+ vchiq_arm_log_level, -+ "header %x: msgbufsize" -+ " %x < msglen %x", -+ (unsigned int)header, -+ args.msgbufsize, -+ msglen); -+ WARN(1, "invalid message " -+ "size\n"); -+ if (ret == 0) -+ ret = -EMSGSIZE; -+ break; -+ } -+ if (msgbufcount <= 0) -+ /* Stall here for lack of a -+ ** buffer for the message. */ -+ break; -+ /* Get the pointer from user space */ -+ msgbufcount--; -+ if (copy_from_user(&msgbuf, -+ (const void __user *) -+ &args.msgbufs[msgbufcount], -+ sizeof(msgbuf)) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ /* Copy the message to user space */ -+ if (copy_to_user(msgbuf, header, -+ msglen) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ /* Now it has been copied, the message -+ ** can be released. */ -+ vchiq_release_message(service->handle, -+ header); -+ -+ /* The completion must point to the -+ ** msgbuf. */ -+ completion->header = msgbuf; -+ } -+ -+ if ((completion->reason == -+ VCHIQ_SERVICE_CLOSED) && -+ !instance->use_close_delivered) -+ unlock_service(service); -+ -+ if (copy_to_user((void __user *)( -+ (size_t)args.buf + -+ ret * sizeof(VCHIQ_COMPLETION_DATA_T)), -+ completion, -+ sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) { -+ if (ret == 0) -+ ret = -EFAULT; -+ break; -+ } -+ -+ instance->completion_remove++; -+ } -+ -+ if (msgbufcount != args.msgbufcount) { -+ if (copy_to_user((void __user *) -+ &((VCHIQ_AWAIT_COMPLETION_T *)arg)-> -+ msgbufcount, -+ &msgbufcount, -+ sizeof(msgbufcount)) != 0) { -+ ret = -EFAULT; -+ } -+ } -+ } -+ -+ if (ret != 0) -+ up(&instance->remove_event); -+ mutex_unlock(&instance->completion_mutex); -+ DEBUG_TRACE(AWAIT_COMPLETION_LINE); -+ } break; -+ -+ case VCHIQ_IOC_DEQUEUE_MESSAGE: { -+ VCHIQ_DEQUEUE_MESSAGE_T args; -+ USER_SERVICE_T *user_service; -+ VCHIQ_HEADER_T *header; -+ -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ user_service = (USER_SERVICE_T *)service->base.userdata; -+ if (user_service->is_vchi == 0) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ spin_lock(&msg_queue_spinlock); -+ if (user_service->msg_remove == user_service->msg_insert) { -+ if (!args.blocking) { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ ret = -EWOULDBLOCK; -+ break; -+ } -+ user_service->dequeue_pending = 1; -+ do { -+ spin_unlock(&msg_queue_spinlock); -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ if (down_interruptible( -+ &user_service->insert_event) != 0) { -+ vchiq_log_info(vchiq_arm_log_level, -+ "DEQUEUE_MESSAGE interrupted"); -+ ret = -EINTR; -+ break; -+ } -+ spin_lock(&msg_queue_spinlock); -+ } while (user_service->msg_remove == -+ user_service->msg_insert); -+ -+ if (ret) -+ break; -+ } -+ -+ BUG_ON((int)(user_service->msg_insert - -+ user_service->msg_remove) < 0); -+ -+ header = user_service->msg_queue[user_service->msg_remove & -+ (MSG_QUEUE_SIZE - 1)]; -+ user_service->msg_remove++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ up(&user_service->remove_event); -+ if (header == NULL) -+ ret = -ENOTCONN; -+ else if (header->size <= args.bufsize) { -+ /* Copy to user space if msgbuf is not NULL */ -+ if ((args.buf == NULL) || -+ (copy_to_user((void __user *)args.buf, -+ header->data, -+ header->size) == 0)) { -+ ret = header->size; -+ vchiq_release_message( -+ service->handle, -+ header); -+ } else -+ ret = -EFAULT; -+ } else { -+ vchiq_log_error(vchiq_arm_log_level, -+ "header %x: bufsize %x < size %x", -+ (unsigned int)header, args.bufsize, -+ header->size); -+ WARN(1, "invalid size\n"); -+ ret = -EMSGSIZE; -+ } -+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); -+ } break; -+ -+ case VCHIQ_IOC_GET_CLIENT_ID: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ ret = vchiq_get_client_id(handle); -+ } break; -+ -+ case VCHIQ_IOC_GET_CONFIG: { -+ VCHIQ_GET_CONFIG_T args; -+ VCHIQ_CONFIG_T config; -+ -+ if (copy_from_user(&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ if (args.config_size > sizeof(config)) { -+ ret = -EINVAL; -+ break; -+ } -+ status = vchiq_get_config(instance, args.config_size, &config); -+ if (status == VCHIQ_SUCCESS) { -+ if (copy_to_user((void __user *)args.pconfig, -+ &config, args.config_size) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ } -+ } break; -+ -+ case VCHIQ_IOC_SET_SERVICE_OPTION: { -+ VCHIQ_SET_SERVICE_OPTION_T args; -+ -+ if (copy_from_user( -+ &args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ service = find_service_for_instance(instance, args.handle); -+ if (!service) { -+ ret = -EINVAL; -+ break; -+ } -+ -+ status = vchiq_set_service_option( -+ args.handle, args.option, args.value); -+ } break; -+ -+ case VCHIQ_IOC_DUMP_PHYS_MEM: { -+ VCHIQ_DUMP_MEM_T args; -+ -+ if (copy_from_user -+ (&args, (const void __user *)arg, -+ sizeof(args)) != 0) { -+ ret = -EFAULT; -+ break; -+ } -+ dump_phys_mem(args.virt_addr, args.num_bytes); -+ } break; -+ -+ case VCHIQ_IOC_LIB_VERSION: { -+ unsigned int lib_version = (unsigned int)arg; -+ -+ if (lib_version < VCHIQ_VERSION_MIN) -+ ret = -EINVAL; -+ else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) -+ instance->use_close_delivered = 1; -+ } break; -+ -+ case VCHIQ_IOC_CLOSE_DELIVERED: { -+ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; -+ -+ service = find_closed_service_for_instance(instance, handle); -+ if (service != NULL) { -+ USER_SERVICE_T *user_service = -+ (USER_SERVICE_T *)service->base.userdata; -+ close_delivered(user_service); -+ } -+ else -+ ret = -EINVAL; -+ } break; -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ if (service) -+ unlock_service(service); -+ -+ if (ret == 0) { -+ if (status == VCHIQ_ERROR) -+ ret = -EIO; -+ else if (status == VCHIQ_RETRY) -+ ret = -EINTR; -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) && -+ (ret != -EWOULDBLOCK)) -+ vchiq_log_info(vchiq_arm_log_level, -+ " ioctl instance %lx, cmd %s -> status %d, %ld", -+ (unsigned long)instance, -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? -+ ioctl_names[_IOC_NR(cmd)] : -+ "", -+ status, ret); -+ else -+ vchiq_log_trace(vchiq_arm_log_level, -+ " ioctl instance %lx, cmd %s -> status %d, %ld", -+ (unsigned long)instance, -+ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? -+ ioctl_names[_IOC_NR(cmd)] : -+ "", -+ status, ret); -+ -+ return ret; -+} -+ -+/**************************************************************************** -+* -+* vchiq_open -+* -+***************************************************************************/ -+ -+static int -+vchiq_open(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode) & 0x0f; -+ vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); -+ switch (dev) { -+ case VCHIQ_MINOR: { -+ int ret; -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ VCHIQ_INSTANCE_T instance; -+ -+ if (!state) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "vchiq has no connection to VideoCore"); -+ return -ENOTCONN; -+ } -+ -+ instance = kzalloc(sizeof(*instance), GFP_KERNEL); -+ if (!instance) -+ return -ENOMEM; -+ -+ instance->state = state; -+ instance->pid = current->tgid; -+ -+ ret = vchiq_debugfs_add_instance(instance); -+ if (ret != 0) { -+ kfree(instance); -+ return ret; -+ } -+ -+ sema_init(&instance->insert_event, 0); -+ sema_init(&instance->remove_event, 0); -+ mutex_init(&instance->completion_mutex); -+ mutex_init(&instance->bulk_waiter_list_mutex); -+ INIT_LIST_HEAD(&instance->bulk_waiter_list); -+ -+ file->private_data = instance; -+ } break; -+ -+ default: -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unknown minor device: %d", dev); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vchiq_release -+* -+***************************************************************************/ -+ -+static int -+vchiq_release(struct inode *inode, struct file *file) -+{ -+ int dev = iminor(inode) & 0x0f; -+ int ret = 0; -+ switch (dev) { -+ case VCHIQ_MINOR: { -+ VCHIQ_INSTANCE_T instance = file->private_data; -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq_release: instance=%lx", -+ (unsigned long)instance); -+ -+ if (!state) { -+ ret = -EPERM; -+ goto out; -+ } -+ -+ /* Ensure videocore is awake to allow termination. */ -+ vchiq_use_internal(instance->state, NULL, -+ USE_TYPE_VCHIQ); -+ -+ mutex_lock(&instance->completion_mutex); -+ -+ /* Wake the completion thread and ask it to exit */ -+ instance->closing = 1; -+ up(&instance->insert_event); -+ -+ mutex_unlock(&instance->completion_mutex); -+ -+ /* Wake the slot handler if the completion queue is full. */ -+ up(&instance->remove_event); -+ -+ /* Mark all services for termination... */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ USER_SERVICE_T *user_service = service->base.userdata; -+ -+ /* Wake the slot handler if the msg queue is full. */ -+ up(&user_service->remove_event); -+ -+ vchiq_terminate_service_internal(service); -+ unlock_service(service); -+ } -+ -+ /* ...and wait for them to die */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, &i)) -+ != NULL) { -+ USER_SERVICE_T *user_service = service->base.userdata; -+ -+ down(&service->remove_event); -+ -+ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); -+ -+ spin_lock(&msg_queue_spinlock); -+ -+ while (user_service->msg_remove != -+ user_service->msg_insert) { -+ VCHIQ_HEADER_T *header = user_service-> -+ msg_queue[user_service->msg_remove & -+ (MSG_QUEUE_SIZE - 1)]; -+ user_service->msg_remove++; -+ spin_unlock(&msg_queue_spinlock); -+ -+ if (header) -+ vchiq_release_message( -+ service->handle, -+ header); -+ spin_lock(&msg_queue_spinlock); -+ } -+ -+ spin_unlock(&msg_queue_spinlock); -+ -+ unlock_service(service); -+ } -+ -+ /* Release any closed services */ -+ while (instance->completion_remove != -+ instance->completion_insert) { -+ VCHIQ_COMPLETION_DATA_T *completion; -+ VCHIQ_SERVICE_T *service; -+ completion = &instance->completions[ -+ instance->completion_remove & -+ (MAX_COMPLETIONS - 1)]; -+ service = completion->service_userdata; -+ if (completion->reason == VCHIQ_SERVICE_CLOSED) -+ { -+ USER_SERVICE_T *user_service = -+ service->base.userdata; -+ -+ /* Wake any blocked user-thread */ -+ if (instance->use_close_delivered) -+ up(&user_service->close_event); -+ unlock_service(service); -+ } -+ instance->completion_remove++; -+ } -+ -+ /* Release the PEER service count. */ -+ vchiq_release_internal(instance->state, NULL); -+ -+ { -+ struct list_head *pos, *next; -+ list_for_each_safe(pos, next, -+ &instance->bulk_waiter_list) { -+ struct bulk_waiter_node *waiter; -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ vchiq_log_info(vchiq_arm_log_level, -+ "bulk_waiter - cleaned up %x " -+ "for pid %d", -+ (unsigned int)waiter, waiter->pid); -+ kfree(waiter); -+ } -+ } -+ -+ vchiq_debugfs_remove_instance(instance); -+ -+ kfree(instance); -+ file->private_data = NULL; -+ } break; -+ -+ default: -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unknown minor device: %d", dev); -+ ret = -ENXIO; -+ } -+ -+out: -+ return ret; -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump -+* -+***************************************************************************/ -+ -+void -+vchiq_dump(void *dump_context, const char *str, int len) -+{ -+ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; -+ -+ if (context->actual < context->space) { -+ int copy_bytes; -+ if (context->offset > 0) { -+ int skip_bytes = min(len, (int)context->offset); -+ str += skip_bytes; -+ len -= skip_bytes; -+ context->offset -= skip_bytes; -+ if (context->offset > 0) -+ return; -+ } -+ copy_bytes = min(len, (int)(context->space - context->actual)); -+ if (copy_bytes == 0) -+ return; -+ if (copy_to_user(context->buf + context->actual, str, -+ copy_bytes)) -+ context->actual = -EFAULT; -+ context->actual += copy_bytes; -+ len -= copy_bytes; -+ -+ /* If tne terminating NUL is included in the length, then it -+ ** marks the end of a line and should be replaced with a -+ ** carriage return. */ -+ if ((len == 0) && (str[copy_bytes - 1] == '\0')) { -+ char cr = '\n'; -+ if (copy_to_user(context->buf + context->actual - 1, -+ &cr, 1)) -+ context->actual = -EFAULT; -+ } -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump_platform_instance_state -+* -+***************************************************************************/ -+ -+void -+vchiq_dump_platform_instances(void *dump_context) -+{ -+ VCHIQ_STATE_T *state = vchiq_get_state(); -+ char buf[80]; -+ int len; -+ int i; -+ -+ /* There is no list of instances, so instead scan all services, -+ marking those that have been dumped. */ -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ VCHIQ_INSTANCE_T instance; -+ -+ if (service && (service->base.callback == service_callback)) { -+ instance = service->instance; -+ if (instance) -+ instance->mark = 0; -+ } -+ } -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ VCHIQ_INSTANCE_T instance; -+ -+ if (service && (service->base.callback == service_callback)) { -+ instance = service->instance; -+ if (instance && !instance->mark) { -+ len = snprintf(buf, sizeof(buf), -+ "Instance %x: pid %d,%s completions " -+ "%d/%d", -+ (unsigned int)instance, instance->pid, -+ instance->connected ? " connected, " : -+ "", -+ instance->completion_insert - -+ instance->completion_remove, -+ MAX_COMPLETIONS); -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ instance->mark = 1; -+ } -+ } -+ } -+} -+ -+/**************************************************************************** -+* -+* vchiq_dump_platform_service_state -+* -+***************************************************************************/ -+ -+void -+vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) -+{ -+ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; -+ char buf[80]; -+ int len; -+ -+ len = snprintf(buf, sizeof(buf), " instance %x", -+ (unsigned int)service->instance); -+ -+ if ((service->base.callback == service_callback) && -+ user_service->is_vchi) { -+ len += snprintf(buf + len, sizeof(buf) - len, -+ ", %d/%d messages", -+ user_service->msg_insert - user_service->msg_remove, -+ MSG_QUEUE_SIZE); -+ -+ if (user_service->dequeue_pending) -+ len += snprintf(buf + len, sizeof(buf) - len, -+ " (dequeue pending)"); -+ } -+ -+ vchiq_dump(dump_context, buf, len + 1); -+} -+ -+/**************************************************************************** -+* -+* dump_user_mem -+* -+***************************************************************************/ -+ -+static void -+dump_phys_mem(void *virt_addr, uint32_t num_bytes) -+{ -+ int rc; -+ uint8_t *end_virt_addr = virt_addr + num_bytes; -+ int num_pages; -+ int offset; -+ int end_offset; -+ int page_idx; -+ int prev_idx; -+ struct page *page; -+ struct page **pages; -+ uint8_t *kmapped_virt_ptr; -+ -+ /* Align virtAddr and endVirtAddr to 16 byte boundaries. */ -+ -+ virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL); -+ end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) & -+ ~0x0fuL); -+ -+ offset = (int)(long)virt_addr & (PAGE_SIZE - 1); -+ end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); -+ -+ num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; -+ -+ pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); -+ if (pages == NULL) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocation memory for %d pages\n", -+ num_pages); -+ return; -+ } -+ -+ down_read(¤t->mm->mmap_sem); -+ rc = get_user_pages(current, /* task */ -+ current->mm, /* mm */ -+ (unsigned long)virt_addr, /* start */ -+ num_pages, /* len */ -+ 0, /* write */ -+ 0, /* force */ -+ pages, /* pages (array of page pointers) */ -+ NULL); /* vmas */ -+ up_read(¤t->mm->mmap_sem); -+ -+ prev_idx = -1; -+ page = NULL; -+ -+ while (offset < end_offset) { -+ -+ int page_offset = offset % PAGE_SIZE; -+ page_idx = offset / PAGE_SIZE; -+ -+ if (page_idx != prev_idx) { -+ -+ if (page != NULL) -+ kunmap(page); -+ page = pages[page_idx]; -+ kmapped_virt_ptr = kmap(page); -+ -+ prev_idx = page_idx; -+ } -+ -+ if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE) -+ vchiq_log_dump_mem("ph", -+ (uint32_t)(unsigned long)&kmapped_virt_ptr[ -+ page_offset], -+ &kmapped_virt_ptr[page_offset], 16); -+ -+ offset += 16; -+ } -+ if (page != NULL) -+ kunmap(page); -+ -+ for (page_idx = 0; page_idx < num_pages; page_idx++) -+ page_cache_release(pages[page_idx]); -+ -+ kfree(pages); -+} -+ -+/**************************************************************************** -+* -+* vchiq_read -+* -+***************************************************************************/ -+ -+static ssize_t -+vchiq_read(struct file *file, char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ DUMP_CONTEXT_T context; -+ context.buf = buf; -+ context.actual = 0; -+ context.space = count; -+ context.offset = *ppos; -+ -+ vchiq_dump_state(&context, &g_state); -+ -+ *ppos += context.actual; -+ -+ return context.actual; -+} -+ -+VCHIQ_STATE_T * -+vchiq_get_state(void) -+{ -+ -+ if (g_state.remote == NULL) -+ printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); -+ else if (g_state.remote->initialised != 1) -+ printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", -+ __func__, g_state.remote->initialised); -+ -+ return ((g_state.remote != NULL) && -+ (g_state.remote->initialised == 1)) ? &g_state : NULL; -+} -+ -+static const struct file_operations -+vchiq_fops = { -+ .owner = THIS_MODULE, -+ .unlocked_ioctl = vchiq_ioctl, -+ .open = vchiq_open, -+ .release = vchiq_release, -+ .read = vchiq_read -+}; -+ -+/* -+ * Autosuspend related functionality -+ */ -+ -+int -+vchiq_videocore_wanted(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ if (!arm_state) -+ /* autosuspend not supported - always return wanted */ -+ return 1; -+ else if (arm_state->blocked_count) -+ return 1; -+ else if (!arm_state->videocore_use_count) -+ /* usage count zero - check for override unless we're forcing */ -+ if (arm_state->resume_blocked) -+ return 0; -+ else -+ return vchiq_platform_videocore_wanted(state); -+ else -+ /* non-zero usage count - videocore still required */ -+ return 1; -+} -+ -+static VCHIQ_STATUS_T -+vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, -+ VCHIQ_SERVICE_HANDLE_T service_user, -+ void *bulk_user) -+{ -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s callback reason %d", __func__, reason); -+ return 0; -+} -+ -+static int -+vchiq_keepalive_thread_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ VCHIQ_STATUS_T status; -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_SERVICE_HANDLE_T ka_handle; -+ -+ VCHIQ_SERVICE_PARAMS_T params = { -+ .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'), -+ .callback = vchiq_keepalive_vchiq_callback, -+ .version = KEEPALIVE_VER, -+ .version_min = KEEPALIVE_VER_MIN -+ }; -+ -+ status = vchiq_initialise(&instance); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_initialise failed %d", __func__, status); -+ goto exit; -+ } -+ -+ status = vchiq_connect(instance); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_connect failed %d", __func__, status); -+ goto shutdown; -+ } -+ -+ status = vchiq_add_service(instance, ¶ms, &ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_open_service failed %d", __func__, status); -+ goto shutdown; -+ } -+ -+ while (1) { -+ long rc = 0, uc = 0; -+ if (wait_for_completion_interruptible(&arm_state->ka_evt) -+ != 0) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s interrupted", __func__); -+ flush_signals(current); -+ continue; -+ } -+ -+ /* read and clear counters. Do release_count then use_count to -+ * prevent getting more releases than uses */ -+ rc = atomic_xchg(&arm_state->ka_release_count, 0); -+ uc = atomic_xchg(&arm_state->ka_use_count, 0); -+ -+ /* Call use/release service the requisite number of times. -+ * Process use before release so use counts don't go negative */ -+ while (uc--) { -+ atomic_inc(&arm_state->ka_use_ack_count); -+ status = vchiq_use_service(ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_use_service error %d", -+ __func__, status); -+ } -+ } -+ while (rc--) { -+ status = vchiq_release_service(ka_handle); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s vchiq_release_service error %d", -+ __func__, status); -+ } -+ } -+ } -+ -+shutdown: -+ vchiq_shutdown(instance); -+exit: -+ return 0; -+} -+ -+ -+ -+VCHIQ_STATUS_T -+vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (arm_state) { -+ rwlock_init(&arm_state->susp_res_lock); -+ -+ init_completion(&arm_state->ka_evt); -+ atomic_set(&arm_state->ka_use_count, 0); -+ atomic_set(&arm_state->ka_use_ack_count, 0); -+ atomic_set(&arm_state->ka_release_count, 0); -+ -+ init_completion(&arm_state->vc_suspend_complete); -+ -+ init_completion(&arm_state->vc_resume_complete); -+ /* Initialise to 'done' state. We only want to block on resume -+ * completion while videocore is suspended. */ -+ set_resume_state(arm_state, VC_RESUME_RESUMED); -+ -+ init_completion(&arm_state->resume_blocker); -+ /* Initialise to 'done' state. We only want to block on this -+ * completion while resume is blocked */ -+ complete_all(&arm_state->resume_blocker); -+ -+ init_completion(&arm_state->blocked_blocker); -+ /* Initialise to 'done' state. We only want to block on this -+ * completion while things are waiting on the resume blocker */ -+ complete_all(&arm_state->blocked_blocker); -+ -+ arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; -+ arm_state->suspend_timer_running = 0; -+ init_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer.data = (unsigned long)(state); -+ arm_state->suspend_timer.function = suspend_timer_callback; -+ -+ arm_state->first_connect = 0; -+ -+ } -+ return status; -+} -+ -+/* -+** Functions to modify the state variables; -+** set_suspend_state -+** set_resume_state -+** -+** There are more state variables than we might like, so ensure they remain in -+** step. Suspend and resume state are maintained separately, since most of -+** these state machines can operate independently. However, there are a few -+** states where state transitions in one state machine cause a reset to the -+** other state machine. In addition, there are some completion events which -+** need to occur on state machine reset and end-state(s), so these are also -+** dealt with in these functions. -+** -+** In all states we set the state variable according to the input, but in some -+** cases we perform additional steps outlined below; -+** -+** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. -+** The suspend completion is completed after any suspend -+** attempt. When we reset the state machine we also reset -+** the completion. This reset occurs when videocore is -+** resumed, and also if we initiate suspend after a suspend -+** failure. -+** -+** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for -+** suspend - ie from this point on we must try to suspend -+** before resuming can occur. We therefore also reset the -+** resume state machine to VC_RESUME_IDLE in this state. -+** -+** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call -+** complete_all on the suspend completion to notify -+** anything waiting for suspend to happen. -+** -+** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also -+** initiate resume, so no need to alter resume state. -+** We call complete_all on the suspend completion to notify -+** of suspend rejection. -+** -+** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the -+** suspend completion and reset the resume state machine. -+** -+** VC_RESUME_IDLE - Initialise the resume completion at the same time. The -+** resume completion is in it's 'done' state whenever -+** videcore is running. Therfore, the VC_RESUME_IDLE state -+** implies that videocore is suspended. -+** Hence, any thread which needs to wait until videocore is -+** running can wait on this completion - it will only block -+** if videocore is suspended. -+** -+** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. -+** Call complete_all on the resume completion to unblock -+** any threads waiting for resume. Also reset the suspend -+** state machine to it's idle state. -+** -+** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. -+*/ -+ -+inline void -+set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_suspend_status new_state) -+{ -+ /* set the state in all cases */ -+ arm_state->vc_suspend_state = new_state; -+ -+ /* state specific additional actions */ -+ switch (new_state) { -+ case VC_SUSPEND_FORCE_CANCELED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_REJECTED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_FAILED: -+ complete_all(&arm_state->vc_suspend_complete); -+ arm_state->vc_resume_state = VC_RESUME_RESUMED; -+ complete_all(&arm_state->vc_resume_complete); -+ break; -+ case VC_SUSPEND_IDLE: -+ reinit_completion(&arm_state->vc_suspend_complete); -+ break; -+ case VC_SUSPEND_REQUESTED: -+ break; -+ case VC_SUSPEND_IN_PROGRESS: -+ set_resume_state(arm_state, VC_RESUME_IDLE); -+ break; -+ case VC_SUSPEND_SUSPENDED: -+ complete_all(&arm_state->vc_suspend_complete); -+ break; -+ default: -+ BUG(); -+ break; -+ } -+} -+ -+inline void -+set_resume_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_resume_status new_state) -+{ -+ /* set the state in all cases */ -+ arm_state->vc_resume_state = new_state; -+ -+ /* state specific additional actions */ -+ switch (new_state) { -+ case VC_RESUME_FAILED: -+ break; -+ case VC_RESUME_IDLE: -+ reinit_completion(&arm_state->vc_resume_complete); -+ break; -+ case VC_RESUME_REQUESTED: -+ break; -+ case VC_RESUME_IN_PROGRESS: -+ break; -+ case VC_RESUME_RESUMED: -+ complete_all(&arm_state->vc_resume_complete); -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ break; -+ default: -+ BUG(); -+ break; -+ } -+} -+ -+ -+/* should be called with the write lock held */ -+inline void -+start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ del_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer.expires = jiffies + -+ msecs_to_jiffies(arm_state-> -+ suspend_timer_timeout); -+ add_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer_running = 1; -+} -+ -+/* should be called with the write lock held */ -+static inline void -+stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ if (arm_state->suspend_timer_running) { -+ del_timer(&arm_state->suspend_timer); -+ arm_state->suspend_timer_running = 0; -+ } -+} -+ -+static inline int -+need_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && -+ (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && -+ vchiq_videocore_wanted(state); -+} -+ -+static int -+block_resume(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ int status = VCHIQ_SUCCESS; -+ const unsigned long timeout_val = -+ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS); -+ int resume_count = 0; -+ -+ /* Allow any threads which were blocked by the last force suspend to -+ * complete if they haven't already. Only give this one shot; if -+ * blocked_count is incremented after blocked_blocker is completed -+ * (which only happens when blocked_count hits 0) then those threads -+ * will have to wait until next time around */ -+ if (arm_state->blocked_count) { -+ reinit_completion(&arm_state->blocked_blocker); -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " -+ "blocked clients", __func__); -+ if (wait_for_completion_interruptible_timeout( -+ &arm_state->blocked_blocker, timeout_val) -+ <= 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s wait for " -+ "previously blocked clients failed" , __func__); -+ status = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s previously blocked " -+ "clients resumed", __func__); -+ write_lock_bh(&arm_state->susp_res_lock); -+ } -+ -+ /* We need to wait for resume to complete if it's in process */ -+ while (arm_state->vc_resume_state != VC_RESUME_RESUMED && -+ arm_state->vc_resume_state > VC_RESUME_IDLE) { -+ if (resume_count > 1) { -+ status = VCHIQ_ERROR; -+ vchiq_log_error(vchiq_susp_log_level, "%s waited too " -+ "many times for resume" , __func__); -+ goto out; -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s wait for resume", -+ __func__); -+ if (wait_for_completion_interruptible_timeout( -+ &arm_state->vc_resume_complete, timeout_val) -+ <= 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s wait for " -+ "resume failed (%s)", __func__, -+ resume_state_names[arm_state->vc_resume_state + -+ VC_RESUME_NUM_OFFSET]); -+ status = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__); -+ write_lock_bh(&arm_state->susp_res_lock); -+ resume_count++; -+ } -+ reinit_completion(&arm_state->resume_blocker); -+ arm_state->resume_blocked = 1; -+ -+out: -+ return status; -+} -+ -+static inline void -+unblock_resume(VCHIQ_ARM_STATE_T *arm_state) -+{ -+ complete_all(&arm_state->resume_blocker); -+ arm_state->resume_blocked = 0; -+} -+ -+/* Initiate suspend via slot handler. Should be called with the write lock -+ * held */ -+VCHIQ_STATUS_T -+vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ status = VCHIQ_SUCCESS; -+ -+ -+ switch (arm_state->vc_suspend_state) { -+ case VC_SUSPEND_REQUESTED: -+ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " -+ "requested", __func__); -+ break; -+ case VC_SUSPEND_IN_PROGRESS: -+ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " -+ "progress", __func__); -+ break; -+ -+ default: -+ /* We don't expect to be in other states, so log but continue -+ * anyway */ -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s unexpected suspend state %s", __func__, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ /* fall through */ -+ case VC_SUSPEND_REJECTED: -+ case VC_SUSPEND_FAILED: -+ /* Ensure any idle state actions have been run */ -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ /* fall through */ -+ case VC_SUSPEND_IDLE: -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: suspending", __func__); -+ set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); -+ /* kick the slot handler thread to initiate suspend */ -+ request_poll(state, NULL, 0); -+ break; -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); -+ return status; -+} -+ -+void -+vchiq_platform_check_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int susp = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && -+ arm_state->vc_resume_state == VC_RESUME_RESUMED) { -+ set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); -+ susp = 1; -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (susp) -+ vchiq_platform_suspend(state); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+} -+ -+ -+static void -+output_timeout_error(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ char service_err[50] = ""; -+ int vc_use_count = arm_state->videocore_use_count; -+ int active_services = state->unused_service; -+ int i; -+ -+ if (!arm_state->videocore_use_count) { -+ snprintf(service_err, 50, " Videocore usecount is 0"); -+ goto output_msg; -+ } -+ for (i = 0; i < active_services; i++) { -+ VCHIQ_SERVICE_T *service_ptr = state->services[i]; -+ if (service_ptr && service_ptr->service_use_count && -+ (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { -+ snprintf(service_err, 50, " %c%c%c%c(%d) service has " -+ "use count %d%s", VCHIQ_FOURCC_AS_4CHARS( -+ service_ptr->base.fourcc), -+ service_ptr->client_id, -+ service_ptr->service_use_count, -+ service_ptr->service_use_count == -+ vc_use_count ? "" : " (+ more)"); -+ break; -+ } -+ } -+ -+output_msg: -+ vchiq_log_error(vchiq_susp_log_level, -+ "timed out waiting for vc suspend (%d).%s", -+ arm_state->autosuspend_override, service_err); -+ -+} -+ -+/* Try to get videocore into suspended state, regardless of autosuspend state. -+** We don't actually force suspend, since videocore may get into a bad state -+** if we force suspend at a bad time. Instead, we wait for autosuspend to -+** determine a good point to suspend. If this doesn't happen within 100ms we -+** report failure. -+** -+** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if -+** videocore failed to suspend in time or VCHIQ_ERROR if interrupted. -+*/ -+VCHIQ_STATUS_T -+vchiq_arm_force_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ long rc = 0; -+ int repeat = -1; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ -+ status = block_resume(arm_state); -+ if (status != VCHIQ_SUCCESS) -+ goto unlock; -+ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { -+ /* Already suspended - just block resume and exit */ -+ vchiq_log_info(vchiq_susp_log_level, "%s already suspended", -+ __func__); -+ status = VCHIQ_SUCCESS; -+ goto unlock; -+ } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) { -+ /* initiate suspend immediately in the case that we're waiting -+ * for the timeout */ -+ stop_suspend_timer(arm_state); -+ if (!vchiq_videocore_wanted(state)) { -+ vchiq_log_info(vchiq_susp_log_level, "%s videocore " -+ "idle, initiating suspend", __func__); -+ status = vchiq_arm_vcsuspend(state); -+ } else if (arm_state->autosuspend_override < -+ FORCE_SUSPEND_FAIL_MAX) { -+ vchiq_log_info(vchiq_susp_log_level, "%s letting " -+ "videocore go idle", __func__); -+ status = VCHIQ_SUCCESS; -+ } else { -+ vchiq_log_warning(vchiq_susp_log_level, "%s failed too " -+ "many times - attempting suspend", __func__); -+ status = vchiq_arm_vcsuspend(state); -+ } -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend " -+ "in progress - wait for completion", __func__); -+ status = VCHIQ_SUCCESS; -+ } -+ -+ /* Wait for suspend to happen due to system idle (not forced..) */ -+ if (status != VCHIQ_SUCCESS) -+ goto unblock_resume; -+ -+ do { -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ rc = wait_for_completion_interruptible_timeout( -+ &arm_state->vc_suspend_complete, -+ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS)); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (rc < 0) { -+ vchiq_log_warning(vchiq_susp_log_level, "%s " -+ "interrupted waiting for suspend", __func__); -+ status = VCHIQ_ERROR; -+ goto unblock_resume; -+ } else if (rc == 0) { -+ if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) { -+ /* Repeat timeout once if in progress */ -+ if (repeat < 0) { -+ repeat = 1; -+ continue; -+ } -+ } -+ arm_state->autosuspend_override++; -+ output_timeout_error(state); -+ -+ status = VCHIQ_RETRY; -+ goto unblock_resume; -+ } -+ } while (0 < (repeat--)); -+ -+ /* Check and report state in case we need to abort ARM suspend */ -+ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) { -+ status = VCHIQ_RETRY; -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s videocore suspend failed (state %s)", __func__, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ /* Reset the state only if it's still in an error state. -+ * Something could have already initiated another suspend. */ -+ if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE) -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ -+ goto unblock_resume; -+ } -+ -+ /* successfully suspended - unlock and exit */ -+ goto unlock; -+ -+unblock_resume: -+ /* all error states need to unblock resume before exit */ -+ unblock_resume(arm_state); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); -+ return status; -+} -+ -+void -+vchiq_check_suspend(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && -+ arm_state->first_connect && -+ !vchiq_videocore_wanted(state)) { -+ vchiq_arm_vcsuspend(state); -+ } -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+} -+ -+ -+int -+vchiq_arm_allow_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int resume = 0; -+ int ret = -1; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ unblock_resume(arm_state); -+ resume = vchiq_check_resume(state); -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (resume) { -+ if (wait_for_completion_interruptible( -+ &arm_state->vc_resume_complete) < 0) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s interrupted", __func__); -+ /* failed, cannot accurately derive suspend -+ * state, so exit early. */ -+ goto out; -+ } -+ } -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: Videocore remains suspended", __func__); -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: Videocore resumed", __func__); -+ ret = 0; -+ } -+ read_unlock_bh(&arm_state->susp_res_lock); -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+/* This function should be called with the write lock held */ -+int -+vchiq_check_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int resume = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (need_resume(state)) { -+ set_resume_state(arm_state, VC_RESUME_REQUESTED); -+ request_poll(state, NULL, 0); -+ resume = 1; -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return resume; -+} -+ -+void -+vchiq_platform_check_resume(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int res = 0; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (arm_state->wake_address == 0) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: already awake", __func__); -+ goto unlock; -+ } -+ if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s: already resuming", __func__); -+ goto unlock; -+ } -+ -+ if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) { -+ set_resume_state(arm_state, VC_RESUME_IN_PROGRESS); -+ res = 1; -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s: not resuming (resume state %s)", __func__, -+ resume_state_names[arm_state->vc_resume_state + -+ VC_RESUME_NUM_OFFSET]); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (res) -+ vchiq_platform_resume(state); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); -+ return; -+ -+} -+ -+ -+ -+VCHIQ_STATUS_T -+vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ enum USE_TYPE_E use_type) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; -+ char entity[16]; -+ int *entity_uc; -+ int local_uc, local_entity_uc; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (use_type == USE_TYPE_VCHIQ) { -+ sprintf(entity, "VCHIQ: "); -+ entity_uc = &arm_state->peer_use_count; -+ } else if (service) { -+ sprintf(entity, "%c%c%c%c:%03d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id); -+ entity_uc = &service->service_use_count; -+ } else { -+ vchiq_log_error(vchiq_susp_log_level, "%s null service " -+ "ptr", __func__); -+ ret = VCHIQ_ERROR; -+ goto out; -+ } -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ while (arm_state->resume_blocked) { -+ /* If we call 'use' while force suspend is waiting for suspend, -+ * then we're about to block the thread which the force is -+ * waiting to complete, so we're bound to just time out. In this -+ * case, set the suspend state such that the wait will be -+ * canceled, so we can complete as quickly as possible. */ -+ if (arm_state->resume_blocked && arm_state->vc_suspend_state == -+ VC_SUSPEND_IDLE) { -+ set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); -+ break; -+ } -+ /* If suspend is already in progress then we need to block */ -+ if (!try_wait_for_completion(&arm_state->resume_blocker)) { -+ /* Indicate that there are threads waiting on the resume -+ * blocker. These need to be allowed to complete before -+ * a _second_ call to force suspend can complete, -+ * otherwise low priority threads might never actually -+ * continue */ -+ arm_state->blocked_count++; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " -+ "blocked - waiting...", __func__, entity); -+ if (wait_for_completion_killable( -+ &arm_state->resume_blocker) != 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s %s " -+ "wait for resume blocker interrupted", -+ __func__, entity); -+ ret = VCHIQ_ERROR; -+ write_lock_bh(&arm_state->susp_res_lock); -+ arm_state->blocked_count--; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " -+ "unblocked", __func__, entity); -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (--arm_state->blocked_count == 0) -+ complete_all(&arm_state->blocked_blocker); -+ } -+ } -+ -+ stop_suspend_timer(arm_state); -+ -+ local_uc = ++arm_state->videocore_use_count; -+ local_entity_uc = ++(*entity_uc); -+ -+ /* If there's a pending request which hasn't yet been serviced then -+ * just clear it. If we're past VC_SUSPEND_REQUESTED state then -+ * vc_resume_complete will block until we either resume or fail to -+ * suspend */ -+ if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) -+ set_suspend_state(arm_state, VC_SUSPEND_IDLE); -+ -+ if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { -+ set_resume_state(arm_state, VC_RESUME_REQUESTED); -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, local_entity_uc, local_uc); -+ request_poll(state, NULL, 0); -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, *entity_uc, local_uc); -+ -+ -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+ /* Completion is in a done state when we're not suspended, so this won't -+ * block for the non-suspended case. */ -+ if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { -+ vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", -+ __func__, entity); -+ if (wait_for_completion_killable( -+ &arm_state->vc_resume_complete) != 0) { -+ vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " -+ "resume interrupted", __func__, entity); -+ ret = VCHIQ_ERROR; -+ goto out; -+ } -+ vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, -+ entity); -+ } -+ -+ if (ret == VCHIQ_SUCCESS) { -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); -+ while (ack_cnt && (status == VCHIQ_SUCCESS)) { -+ /* Send the use notify to videocore */ -+ status = vchiq_send_remote_use_active(state); -+ if (status == VCHIQ_SUCCESS) -+ ack_cnt--; -+ else -+ atomic_add(ack_cnt, -+ &arm_state->ka_use_ack_count); -+ } -+ } -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; -+ char entity[16]; -+ int *entity_uc; -+ int local_uc, local_entity_uc; -+ -+ if (!arm_state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ if (service) { -+ sprintf(entity, "%c%c%c%c:%03d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id); -+ entity_uc = &service->service_use_count; -+ } else { -+ sprintf(entity, "PEER: "); -+ entity_uc = &arm_state->peer_use_count; -+ } -+ -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (!arm_state->videocore_use_count || !(*entity_uc)) { -+ /* Don't use BUG_ON - don't allow user thread to crash kernel */ -+ WARN_ON(!arm_state->videocore_use_count); -+ WARN_ON(!(*entity_uc)); -+ ret = VCHIQ_ERROR; -+ goto unlock; -+ } -+ local_uc = --arm_state->videocore_use_count; -+ local_entity_uc = --(*entity_uc); -+ -+ if (!vchiq_videocore_wanted(state)) { -+ if (vchiq_platform_use_suspend_timer() && -+ !arm_state->resume_blocked) { -+ /* Only use the timer if we're not trying to force -+ * suspend (=> resume_blocked) */ -+ start_suspend_timer(arm_state); -+ } else { -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s %s count %d, state count %d - suspending", -+ __func__, entity, *entity_uc, -+ arm_state->videocore_use_count); -+ vchiq_arm_vcsuspend(state); -+ } -+ } else -+ vchiq_log_trace(vchiq_susp_log_level, -+ "%s %s count %d, state count %d", -+ __func__, entity, *entity_uc, -+ arm_state->videocore_use_count); -+ -+unlock: -+ write_unlock_bh(&arm_state->susp_res_lock); -+ -+out: -+ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); -+ return ret; -+} -+ -+void -+vchiq_on_remote_use(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ atomic_inc(&arm_state->ka_use_count); -+ complete(&arm_state->ka_evt); -+} -+ -+void -+vchiq_on_remote_release(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ atomic_inc(&arm_state->ka_release_count); -+ complete(&arm_state->ka_evt); -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ return vchiq_release_internal(service->state, service); -+} -+ -+VCHIQ_DEBUGFS_NODE_T * -+vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance) -+{ -+ return &instance->debugfs_node; -+} -+ -+int -+vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int use_count = 0, i; -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ use_count += service->service_use_count; -+ unlock_service(service); -+ } -+ return use_count; -+} -+ -+int -+vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->pid; -+} -+ -+int -+vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->trace; -+} -+ -+void -+vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ i = 0; -+ while ((service = next_service_by_instance(instance->state, -+ instance, &i)) != NULL) { -+ service->trace = trace; -+ unlock_service(service); -+ } -+ instance->trace = (trace != 0); -+} -+ -+static void suspend_timer_callback(unsigned long context) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ if (!arm_state) -+ goto out; -+ vchiq_log_info(vchiq_susp_log_level, -+ "%s - suspend timer expired - check suspend", __func__); -+ vchiq_check_suspend(state); -+out: -+ return; -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_use_internal(service->state, service, -+ USE_TYPE_SERVICE_NO_RESUME); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_use_internal(service->state, service, -+ USE_TYPE_SERVICE); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+VCHIQ_STATUS_T -+vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ if (service) { -+ ret = vchiq_release_internal(service->state, service); -+ unlock_service(service); -+ } -+ return ret; -+} -+ -+void -+vchiq_dump_service_use_state(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ int i, j = 0; -+ /* Only dump 64 services */ -+ static const int local_max_services = 64; -+ /* If there's more than 64 services, only dump ones with -+ * non-zero counts */ -+ int only_nonzero = 0; -+ static const char *nz = "<-- preventing suspend"; -+ -+ enum vc_suspend_status vc_suspend_state; -+ enum vc_resume_status vc_resume_state; -+ int peer_count; -+ int vc_use_count; -+ int active_services; -+ struct service_data_struct { -+ int fourcc; -+ int clientid; -+ int use_count; -+ } service_data[local_max_services]; -+ -+ if (!arm_state) -+ return; -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ vc_suspend_state = arm_state->vc_suspend_state; -+ vc_resume_state = arm_state->vc_resume_state; -+ peer_count = arm_state->peer_use_count; -+ vc_use_count = arm_state->videocore_use_count; -+ active_services = state->unused_service; -+ if (active_services > local_max_services) -+ only_nonzero = 1; -+ -+ for (i = 0; (i < active_services) && (j < local_max_services); i++) { -+ VCHIQ_SERVICE_T *service_ptr = state->services[i]; -+ if (!service_ptr) -+ continue; -+ -+ if (only_nonzero && !service_ptr->service_use_count) -+ continue; -+ -+ if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) { -+ service_data[j].fourcc = service_ptr->base.fourcc; -+ service_data[j].clientid = service_ptr->client_id; -+ service_data[j++].use_count = service_ptr-> -+ service_use_count; -+ } -+ } -+ -+ read_unlock_bh(&arm_state->susp_res_lock); -+ -+ vchiq_log_warning(vchiq_susp_log_level, -+ "-- Videcore suspend state: %s --", -+ suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); -+ vchiq_log_warning(vchiq_susp_log_level, -+ "-- Videcore resume state: %s --", -+ resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); -+ -+ if (only_nonzero) -+ vchiq_log_warning(vchiq_susp_log_level, "Too many active " -+ "services (%d). Only dumping up to first %d services " -+ "with non-zero use-count", active_services, -+ local_max_services); -+ -+ for (i = 0; i < j; i++) { -+ vchiq_log_warning(vchiq_susp_log_level, -+ "----- %c%c%c%c:%d service count %d %s", -+ VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc), -+ service_data[i].clientid, -+ service_data[i].use_count, -+ service_data[i].use_count ? nz : ""); -+ } -+ vchiq_log_warning(vchiq_susp_log_level, -+ "----- VCHIQ use count count %d", peer_count); -+ vchiq_log_warning(vchiq_susp_log_level, -+ "--- Overall vchiq instance use count %d", vc_use_count); -+ -+ vchiq_dump_platform_use_state(state); -+} -+ -+VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_ARM_STATE_T *arm_state; -+ VCHIQ_STATUS_T ret = VCHIQ_ERROR; -+ -+ if (!service || !service->state) -+ goto out; -+ -+ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); -+ -+ arm_state = vchiq_platform_get_arm_state(service->state); -+ -+ read_lock_bh(&arm_state->susp_res_lock); -+ if (service->service_use_count) -+ ret = VCHIQ_SUCCESS; -+ read_unlock_bh(&arm_state->susp_res_lock); -+ -+ if (ret == VCHIQ_ERROR) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "%s ERROR - %c%c%c%c:%d service count %d, " -+ "state count %d, videocore suspend state %s", __func__, -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->client_id, service->service_use_count, -+ arm_state->videocore_use_count, -+ suspend_state_names[arm_state->vc_suspend_state + -+ VC_SUSPEND_NUM_OFFSET]); -+ vchiq_dump_service_use_state(service->state); -+ } -+out: -+ return ret; -+} -+ -+/* stub functions */ -+void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) -+{ -+ (void)state; -+} -+ -+void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, -+ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) -+{ -+ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); -+ vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, -+ get_conn_state_name(oldstate), get_conn_state_name(newstate)); -+ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { -+ write_lock_bh(&arm_state->susp_res_lock); -+ if (!arm_state->first_connect) { -+ char threadname[10]; -+ arm_state->first_connect = 1; -+ write_unlock_bh(&arm_state->susp_res_lock); -+ snprintf(threadname, sizeof(threadname), "VCHIQka-%d", -+ state->id); -+ arm_state->ka_thread = kthread_create( -+ &vchiq_keepalive_thread_func, -+ (void *)state, -+ threadname); -+ if (arm_state->ka_thread == NULL) { -+ vchiq_log_error(vchiq_susp_log_level, -+ "vchiq: FATAL: couldn't create thread %s", -+ threadname); -+ } else { -+ wake_up_process(arm_state->ka_thread); -+ } -+ } else -+ write_unlock_bh(&arm_state->susp_res_lock); -+ } -+} -+ -+ -+/**************************************************************************** -+* -+* vchiq_init - called when the module is loaded. -+* -+***************************************************************************/ -+ -+static int __init -+vchiq_init(void) -+{ -+ int err; -+ void *ptr_err; -+ -+ /* create debugfs entries */ -+ err = vchiq_debugfs_init(); -+ if (err != 0) -+ goto failed_debugfs_init; -+ -+ err = alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1, DEVICE_NAME); -+ if (err != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to allocate device number"); -+ goto failed_alloc_chrdev; -+ } -+ cdev_init(&vchiq_cdev, &vchiq_fops); -+ vchiq_cdev.owner = THIS_MODULE; -+ err = cdev_add(&vchiq_cdev, vchiq_devid, 1); -+ if (err != 0) { -+ vchiq_log_error(vchiq_arm_log_level, -+ "Unable to register device"); -+ goto failed_cdev_add; -+ } -+ -+ /* create sysfs entries */ -+ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); -+ ptr_err = vchiq_class; -+ if (IS_ERR(ptr_err)) -+ goto failed_class_create; -+ -+ vchiq_dev = device_create(vchiq_class, NULL, -+ vchiq_devid, NULL, "vchiq"); -+ ptr_err = vchiq_dev; -+ if (IS_ERR(ptr_err)) -+ goto failed_device_create; -+ -+ err = vchiq_platform_init(&g_state); -+ if (err != 0) -+ goto failed_platform_init; -+ -+ vchiq_log_info(vchiq_arm_log_level, -+ "vchiq: initialised - version %d (min %d), device %d.%d", -+ VCHIQ_VERSION, VCHIQ_VERSION_MIN, -+ MAJOR(vchiq_devid), MINOR(vchiq_devid)); -+ -+ return 0; -+ -+failed_platform_init: -+ device_destroy(vchiq_class, vchiq_devid); -+failed_device_create: -+ class_destroy(vchiq_class); -+failed_class_create: -+ cdev_del(&vchiq_cdev); -+ err = PTR_ERR(ptr_err); -+failed_cdev_add: -+ unregister_chrdev_region(vchiq_devid, 1); -+failed_alloc_chrdev: -+ vchiq_debugfs_deinit(); -+failed_debugfs_init: -+ vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq"); -+ return err; -+} -+ -+/**************************************************************************** -+* -+* vchiq_exit - called when the module is unloaded. -+* -+***************************************************************************/ -+ -+static void __exit -+vchiq_exit(void) -+{ -+ vchiq_platform_exit(&g_state); -+ device_destroy(vchiq_class, vchiq_devid); -+ class_destroy(vchiq_class); -+ cdev_del(&vchiq_cdev); -+ unregister_chrdev_region(vchiq_devid, 1); -+} -+ -+module_init(vchiq_init); -+module_exit(vchiq_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h -new file mode 100644 -index 0000000..d1e2741 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h -@@ -0,0 +1,223 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_ARM_H -+#define VCHIQ_ARM_H -+ -+#include -+#include -+#include -+#include "vchiq_core.h" -+#include "vchiq_debugfs.h" -+ -+ -+enum vc_suspend_status { -+ VC_SUSPEND_FORCE_CANCELED = -3, /* Force suspend canceled, too busy */ -+ VC_SUSPEND_REJECTED = -2, /* Videocore rejected suspend request */ -+ VC_SUSPEND_FAILED = -1, /* Videocore suspend failed */ -+ VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ -+ VC_SUSPEND_REQUESTED, /* User has requested suspend */ -+ VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ -+ VC_SUSPEND_SUSPENDED /* Videocore suspend succeeded */ -+}; -+ -+enum vc_resume_status { -+ VC_RESUME_FAILED = -1, /* Videocore resume failed */ -+ VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ -+ VC_RESUME_REQUESTED, /* User has requested resume */ -+ VC_RESUME_IN_PROGRESS, /* Slot handler has received resume request */ -+ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ -+}; -+ -+ -+enum USE_TYPE_E { -+ USE_TYPE_SERVICE, -+ USE_TYPE_SERVICE_NO_RESUME, -+ USE_TYPE_VCHIQ -+}; -+ -+ -+ -+typedef struct vchiq_arm_state_struct { -+ /* Keepalive-related data */ -+ struct task_struct *ka_thread; -+ struct completion ka_evt; -+ atomic_t ka_use_count; -+ atomic_t ka_use_ack_count; -+ atomic_t ka_release_count; -+ -+ struct completion vc_suspend_complete; -+ struct completion vc_resume_complete; -+ -+ rwlock_t susp_res_lock; -+ enum vc_suspend_status vc_suspend_state; -+ enum vc_resume_status vc_resume_state; -+ -+ unsigned int wake_address; -+ -+ struct timer_list suspend_timer; -+ int suspend_timer_timeout; -+ int suspend_timer_running; -+ -+ /* Global use count for videocore. -+ ** This is equal to the sum of the use counts for all services. When -+ ** this hits zero the videocore suspend procedure will be initiated. -+ */ -+ int videocore_use_count; -+ -+ /* Use count to track requests from videocore peer. -+ ** This use count is not associated with a service, so needs to be -+ ** tracked separately with the state. -+ */ -+ int peer_use_count; -+ -+ /* Flag to indicate whether resume is blocked. This happens when the -+ ** ARM is suspending -+ */ -+ struct completion resume_blocker; -+ int resume_blocked; -+ struct completion blocked_blocker; -+ int blocked_count; -+ -+ int autosuspend_override; -+ -+ /* Flag to indicate that the first vchiq connect has made it through. -+ ** This means that both sides should be fully ready, and we should -+ ** be able to suspend after this point. -+ */ -+ int first_connect; -+ -+ unsigned long long suspend_start_time; -+ unsigned long long sleep_start_time; -+ unsigned long long resume_start_time; -+ unsigned long long last_wake_time; -+ -+} VCHIQ_ARM_STATE_T; -+ -+extern int vchiq_arm_log_level; -+extern int vchiq_susp_log_level; -+ -+extern int __init -+vchiq_platform_init(VCHIQ_STATE_T *state); -+ -+extern void __exit -+vchiq_platform_exit(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATE_T * -+vchiq_get_state(void); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_vcsuspend(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_force_suspend(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_arm_allow_resume(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_vcresume(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state); -+ -+extern int -+vchiq_check_resume(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_check_suspend(VCHIQ_STATE_T *state); -+ VCHIQ_STATUS_T -+vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_STATUS_T -+vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_suspend(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_platform_videocore_wanted(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_platform_use_suspend_timer(void); -+ -+extern void -+vchiq_dump_platform_use_state(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump_service_use_state(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_ARM_STATE_T* -+vchiq_platform_get_arm_state(VCHIQ_STATE_T *state); -+ -+extern int -+vchiq_videocore_wanted(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ enum USE_TYPE_E use_type); -+extern VCHIQ_STATUS_T -+vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_DEBUGFS_NODE_T * -+vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance); -+ -+extern int -+vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance); -+ -+extern void -+vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace); -+ -+extern void -+set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_suspend_status new_state); -+ -+extern void -+set_resume_state(VCHIQ_ARM_STATE_T *arm_state, -+ enum vc_resume_status new_state); -+ -+extern void -+start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state); -+ -+ -+#endif /* VCHIQ_ARM_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h -new file mode 100644 -index 0000000..df64581 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h -@@ -0,0 +1,37 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+const char *vchiq_get_build_hostname(void); -+const char *vchiq_get_build_version(void); -+const char *vchiq_get_build_time(void); -+const char *vchiq_get_build_date(void); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -new file mode 100644 -index 0000000..c382740 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -@@ -0,0 +1,66 @@ -+/** -+ * Copyright (c) 2010-2014 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CFG_H -+#define VCHIQ_CFG_H -+ -+#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') -+/* The version of VCHIQ - change with any non-trivial change */ -+#define VCHIQ_VERSION 7 -+/* The minimum compatible version - update to match VCHIQ_VERSION with any -+** incompatible change */ -+#define VCHIQ_VERSION_MIN 3 -+ -+/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ -+#define VCHIQ_VERSION_LIB_VERSION 7 -+ -+/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ -+#define VCHIQ_VERSION_CLOSE_DELIVERED 7 -+ -+#define VCHIQ_MAX_STATES 1 -+#define VCHIQ_MAX_SERVICES 4096 -+#define VCHIQ_MAX_SLOTS 128 -+#define VCHIQ_MAX_SLOTS_PER_SIDE 64 -+ -+#define VCHIQ_NUM_CURRENT_BULKS 32 -+#define VCHIQ_NUM_SERVICE_BULKS 4 -+ -+#ifndef VCHIQ_ENABLE_DEBUG -+#define VCHIQ_ENABLE_DEBUG 1 -+#endif -+ -+#ifndef VCHIQ_ENABLE_STATS -+#define VCHIQ_ENABLE_STATS 1 -+#endif -+ -+#endif /* VCHIQ_CFG_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -new file mode 100644 -index 0000000..65f4b52 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -@@ -0,0 +1,119 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_connected.h" -+#include "vchiq_core.h" -+#include -+#include -+ -+#define MAX_CALLBACKS 10 -+ -+static int g_connected; -+static int g_num_deferred_callbacks; -+static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[MAX_CALLBACKS]; -+static int g_once_init; -+static struct mutex g_connected_mutex; -+ -+/**************************************************************************** -+* -+* Function to initialize our lock. -+* -+***************************************************************************/ -+ -+static void connected_init(void) -+{ -+ if (!g_once_init) { -+ mutex_init(&g_connected_mutex); -+ g_once_init = 1; -+ } -+} -+ -+/**************************************************************************** -+* -+* This function is used to defer initialization until the vchiq stack is -+* initialized. If the stack is already initialized, then the callback will -+* be made immediately, otherwise it will be deferred until -+* vchiq_call_connected_callbacks is called. -+* -+***************************************************************************/ -+ -+void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback) -+{ -+ connected_init(); -+ -+ if (mutex_lock_interruptible(&g_connected_mutex) != 0) -+ return; -+ -+ if (g_connected) -+ /* We're already connected. Call the callback immediately. */ -+ -+ callback(); -+ else { -+ if (g_num_deferred_callbacks >= MAX_CALLBACKS) -+ vchiq_log_error(vchiq_core_log_level, -+ "There already %d callback registered - " -+ "please increase MAX_CALLBACKS", -+ g_num_deferred_callbacks); -+ else { -+ g_deferred_callback[g_num_deferred_callbacks] = -+ callback; -+ g_num_deferred_callbacks++; -+ } -+ } -+ mutex_unlock(&g_connected_mutex); -+} -+ -+/**************************************************************************** -+* -+* This function is called by the vchiq stack once it has been connected to -+* the videocore and clients can start to use the stack. -+* -+***************************************************************************/ -+ -+void vchiq_call_connected_callbacks(void) -+{ -+ int i; -+ -+ connected_init(); -+ -+ if (mutex_lock_interruptible(&g_connected_mutex) != 0) -+ return; -+ -+ for (i = 0; i < g_num_deferred_callbacks; i++) -+ g_deferred_callback[i](); -+ -+ g_num_deferred_callbacks = 0; -+ g_connected = 1; -+ mutex_unlock(&g_connected_mutex); -+} -+EXPORT_SYMBOL(vchiq_add_connected_callback); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h -new file mode 100644 -index 0000000..863b3e3 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h -@@ -0,0 +1,50 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CONNECTED_H -+#define VCHIQ_CONNECTED_H -+ -+/* ---- Include Files ----------------------------------------------------- */ -+ -+/* ---- Constants and Types ---------------------------------------------- */ -+ -+typedef void (*VCHIQ_CONNECTED_CALLBACK_T)(void); -+ -+/* ---- Variable Externs ------------------------------------------------- */ -+ -+/* ---- Function Prototypes ---------------------------------------------- */ -+ -+void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback); -+void vchiq_call_connected_callbacks(void); -+ -+#endif /* VCHIQ_CONNECTED_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -new file mode 100644 -index 0000000..f962027 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -0,0 +1,3861 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_core.h" -+ -+#define VCHIQ_SLOT_HANDLER_STACK 8192 -+ -+#define HANDLE_STATE_SHIFT 12 -+ -+#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index)) -+#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index)) -+#define SLOT_INDEX_FROM_DATA(state, data) \ -+ (((unsigned int)((char *)data - (char *)state->slot_data)) / \ -+ VCHIQ_SLOT_SIZE) -+#define SLOT_INDEX_FROM_INFO(state, info) \ -+ ((unsigned int)(info - state->slot_info)) -+#define SLOT_QUEUE_INDEX_FROM_POS(pos) \ -+ ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) -+ -+#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) -+ -+#define SRVTRACE_LEVEL(srv) \ -+ (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level) -+#define SRVTRACE_ENABLED(srv, lev) \ -+ (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev))) -+ -+struct vchiq_open_payload { -+ int fourcc; -+ int client_id; -+ short version; -+ short version_min; -+}; -+ -+struct vchiq_openack_payload { -+ short version; -+}; -+ -+/* we require this for consistency between endpoints */ -+vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); -+vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); -+vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS)); -+vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES)); -+vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN); -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; -+int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; -+ -+static atomic_t pause_bulks_count = ATOMIC_INIT(0); -+ -+static DEFINE_SPINLOCK(service_spinlock); -+DEFINE_SPINLOCK(bulk_waiter_spinlock); -+DEFINE_SPINLOCK(quota_spinlock); -+ -+VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; -+static unsigned int handle_seq; -+ -+static const char *const srvstate_names[] = { -+ "FREE", -+ "HIDDEN", -+ "LISTENING", -+ "OPENING", -+ "OPEN", -+ "OPENSYNC", -+ "CLOSESENT", -+ "CLOSERECVD", -+ "CLOSEWAIT", -+ "CLOSED" -+}; -+ -+static const char *const reason_names[] = { -+ "SERVICE_OPENED", -+ "SERVICE_CLOSED", -+ "MESSAGE_AVAILABLE", -+ "BULK_TRANSMIT_DONE", -+ "BULK_RECEIVE_DONE", -+ "BULK_TRANSMIT_ABORTED", -+ "BULK_RECEIVE_ABORTED" -+}; -+ -+static const char *const conn_state_names[] = { -+ "DISCONNECTED", -+ "CONNECTING", -+ "CONNECTED", -+ "PAUSING", -+ "PAUSE_SENT", -+ "PAUSED", -+ "RESUMING", -+ "PAUSE_TIMEOUT", -+ "RESUME_TIMEOUT" -+}; -+ -+ -+static void -+release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header); -+ -+static const char *msg_type_str(unsigned int msg_type) -+{ -+ switch (msg_type) { -+ case VCHIQ_MSG_PADDING: return "PADDING"; -+ case VCHIQ_MSG_CONNECT: return "CONNECT"; -+ case VCHIQ_MSG_OPEN: return "OPEN"; -+ case VCHIQ_MSG_OPENACK: return "OPENACK"; -+ case VCHIQ_MSG_CLOSE: return "CLOSE"; -+ case VCHIQ_MSG_DATA: return "DATA"; -+ case VCHIQ_MSG_BULK_RX: return "BULK_RX"; -+ case VCHIQ_MSG_BULK_TX: return "BULK_TX"; -+ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE"; -+ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE"; -+ case VCHIQ_MSG_PAUSE: return "PAUSE"; -+ case VCHIQ_MSG_RESUME: return "RESUME"; -+ case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE"; -+ case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE"; -+ case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE"; -+ } -+ return "???"; -+} -+ -+static inline void -+vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate) -+{ -+ vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate], -+ srvstate_names[newstate]); -+ service->srvstate = newstate; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->handle == handle)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_by_port(VCHIQ_STATE_T *state, int localport) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ if ((unsigned int)localport <= VCHIQ_PORT_MAX) { -+ spin_lock(&service_spinlock); -+ service = state->services[localport]; -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ } -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid port %d", localport); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle) { -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->handle == handle) && -+ (service->instance == instance)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle) { -+ VCHIQ_SERVICE_T *service; -+ -+ spin_lock(&service_spinlock); -+ service = handle_to_service(handle); -+ if (service && -+ ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && -+ (service->handle == handle) && -+ (service->instance == instance)) { -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ } else -+ service = NULL; -+ spin_unlock(&service_spinlock); -+ -+ if (!service) -+ vchiq_log_info(vchiq_core_log_level, -+ "Invalid service handle 0x%x", handle); -+ -+ return service; -+} -+ -+VCHIQ_SERVICE_T * -+next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, -+ int *pidx) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ int idx = *pidx; -+ -+ spin_lock(&service_spinlock); -+ while (idx < state->unused_service) { -+ VCHIQ_SERVICE_T *srv = state->services[idx++]; -+ if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (srv->instance == instance)) { -+ service = srv; -+ BUG_ON(service->ref_count == 0); -+ service->ref_count++; -+ break; -+ } -+ } -+ spin_unlock(&service_spinlock); -+ -+ *pidx = idx; -+ -+ return service; -+} -+ -+void -+lock_service(VCHIQ_SERVICE_T *service) -+{ -+ spin_lock(&service_spinlock); -+ BUG_ON(!service || (service->ref_count == 0)); -+ if (service) -+ service->ref_count++; -+ spin_unlock(&service_spinlock); -+} -+ -+void -+unlock_service(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ spin_lock(&service_spinlock); -+ BUG_ON(!service || (service->ref_count == 0)); -+ if (service && service->ref_count) { -+ service->ref_count--; -+ if (!service->ref_count) { -+ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); -+ state->services[service->localport] = NULL; -+ } else -+ service = NULL; -+ } -+ spin_unlock(&service_spinlock); -+ -+ if (service && service->userdata_term) -+ service->userdata_term(service->base.userdata); -+ -+ kfree(service); -+} -+ -+int -+vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ int id; -+ -+ id = service ? service->client_id : 0; -+ if (service) -+ unlock_service(service); -+ -+ return id; -+} -+ -+void * -+vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = handle_to_service(handle); -+ -+ return service ? service->base.userdata : NULL; -+} -+ -+int -+vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_SERVICE_T *service = handle_to_service(handle); -+ -+ return service ? service->base.fourcc : 0; -+} -+ -+static void -+mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ VCHIQ_SERVICE_QUOTA_T *service_quota; -+ -+ service->closing = 1; -+ -+ /* Synchronise with other threads. */ -+ mutex_lock(&state->recycle_mutex); -+ mutex_unlock(&state->recycle_mutex); -+ if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) { -+ /* If we're pausing then the slot_mutex is held until resume -+ * by the slot handler. Therefore don't try to acquire this -+ * mutex if we're the slot handler and in the pause sent state. -+ * We don't need to in this case anyway. */ -+ mutex_lock(&state->slot_mutex); -+ mutex_unlock(&state->slot_mutex); -+ } -+ -+ /* Unblock any sending thread. */ -+ service_quota = &state->service_quotas[service->localport]; -+ up(&service_quota->quota_event); -+} -+ -+static void -+mark_service_closing(VCHIQ_SERVICE_T *service) -+{ -+ mark_service_closing_internal(service, 0); -+} -+ -+static inline VCHIQ_STATUS_T -+make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, void *bulk_userdata) -+{ -+ VCHIQ_STATUS_T status; -+ vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %x, %x)", -+ service->state->id, service->localport, reason_names[reason], -+ (unsigned int)header, (unsigned int)bulk_userdata); -+ status = service->base.callback(reason, header, service->handle, -+ bulk_userdata); -+ if (status == VCHIQ_ERROR) { -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: ignoring ERROR from callback to service %x", -+ service->state->id, service->handle); -+ status = VCHIQ_SUCCESS; -+ } -+ return status; -+} -+ -+inline void -+vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) -+{ -+ VCHIQ_CONNSTATE_T oldstate = state->conn_state; -+ vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id, -+ conn_state_names[oldstate], -+ conn_state_names[newstate]); -+ state->conn_state = newstate; -+ vchiq_platform_conn_state_changed(state, oldstate, newstate); -+} -+ -+static inline void -+remote_event_create(REMOTE_EVENT_T *event) -+{ -+ event->armed = 0; -+ /* Don't clear the 'fired' flag because it may already have been set -+ ** by the other side. */ -+ sema_init(event->event, 0); -+} -+ -+static inline void -+remote_event_destroy(REMOTE_EVENT_T *event) -+{ -+ (void)event; -+} -+ -+static inline int -+remote_event_wait(REMOTE_EVENT_T *event) -+{ -+ if (!event->fired) { -+ event->armed = 1; -+ dsb(); -+ if (!event->fired) { -+ if (down_interruptible(event->event) != 0) { -+ event->armed = 0; -+ return 0; -+ } -+ } -+ event->armed = 0; -+ wmb(); -+ } -+ -+ event->fired = 0; -+ return 1; -+} -+ -+static inline void -+remote_event_signal_local(REMOTE_EVENT_T *event) -+{ -+ event->armed = 0; -+ up(event->event); -+} -+ -+static inline void -+remote_event_poll(REMOTE_EVENT_T *event) -+{ -+ if (event->fired && event->armed) -+ remote_event_signal_local(event); -+} -+ -+void -+remote_event_pollall(VCHIQ_STATE_T *state) -+{ -+ remote_event_poll(&state->local->sync_trigger); -+ remote_event_poll(&state->local->sync_release); -+ remote_event_poll(&state->local->trigger); -+ remote_event_poll(&state->local->recycle); -+} -+ -+/* Round up message sizes so that any space at the end of a slot is always big -+** enough for a header. This relies on header size being a power of two, which -+** has been verified earlier by a static assertion. */ -+ -+static inline unsigned int -+calc_stride(unsigned int size) -+{ -+ /* Allow room for the header */ -+ size += sizeof(VCHIQ_HEADER_T); -+ -+ /* Round up */ -+ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) -+ - 1); -+} -+ -+/* Called by the slot handler thread */ -+static VCHIQ_SERVICE_T * -+get_listening_service(VCHIQ_STATE_T *state, int fourcc) -+{ -+ int i; -+ -+ WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ if (service && -+ (service->public_fourcc == fourcc) && -+ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && -+ (service->remoteport == VCHIQ_PORT_FREE)))) { -+ lock_service(service); -+ return service; -+ } -+ } -+ -+ return NULL; -+} -+ -+/* Called by the slot handler thread */ -+static VCHIQ_SERVICE_T * -+get_connected_service(VCHIQ_STATE_T *state, unsigned int port) -+{ -+ int i; -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) -+ && (service->remoteport == port)) { -+ lock_service(service); -+ return service; -+ } -+ } -+ return NULL; -+} -+ -+inline void -+request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type) -+{ -+ uint32_t value; -+ -+ if (service) { -+ do { -+ value = atomic_read(&service->poll_flags); -+ } while (atomic_cmpxchg(&service->poll_flags, value, -+ value | (1 << poll_type)) != value); -+ -+ do { -+ value = atomic_read(&state->poll_services[ -+ service->localport>>5]); -+ } while (atomic_cmpxchg( -+ &state->poll_services[service->localport>>5], -+ value, value | (1 << (service->localport & 0x1f))) -+ != value); -+ } -+ -+ state->poll_needed = 1; -+ wmb(); -+ -+ /* ... and ensure the slot handler runs. */ -+ remote_event_signal_local(&state->local->trigger); -+} -+ -+/* Called from queue_message, by the slot handler and application threads, -+** with slot_mutex held */ -+static VCHIQ_HEADER_T * -+reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ int tx_pos = state->local_tx_pos; -+ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK); -+ -+ if (space > slot_space) { -+ VCHIQ_HEADER_T *header; -+ /* Fill the remaining space with padding */ -+ WARN_ON(state->tx_data == NULL); -+ header = (VCHIQ_HEADER_T *) -+ (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); -+ header->msgid = VCHIQ_MSGID_PADDING; -+ header->size = slot_space - sizeof(VCHIQ_HEADER_T); -+ -+ tx_pos += slot_space; -+ } -+ -+ /* If necessary, get the next slot. */ -+ if ((tx_pos & VCHIQ_SLOT_MASK) == 0) { -+ int slot_index; -+ -+ /* If there is no free slot... */ -+ -+ if (down_trylock(&state->slot_available_event) != 0) { -+ /* ...wait for one. */ -+ -+ VCHIQ_STATS_INC(state, slot_stalls); -+ -+ /* But first, flush through the last slot. */ -+ state->local_tx_pos = tx_pos; -+ local->tx_pos = tx_pos; -+ remote_event_signal(&state->remote->trigger); -+ -+ if (!is_blocking || -+ (down_interruptible( -+ &state->slot_available_event) != 0)) -+ return NULL; /* No space available */ -+ } -+ -+ BUG_ON(tx_pos == -+ (state->slot_queue_available * VCHIQ_SLOT_SIZE)); -+ -+ slot_index = local->slot_queue[ -+ SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ state->tx_data = -+ (char *)SLOT_DATA_FROM_INDEX(state, slot_index); -+ } -+ -+ state->local_tx_pos = tx_pos + space; -+ -+ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); -+} -+ -+/* Called by the recycle thread. */ -+static void -+process_free_queue(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; -+ int slot_queue_available; -+ -+ /* Use a read memory barrier to ensure that any state that may have -+ ** been modified by another thread is not masked by stale prefetched -+ ** values. */ -+ rmb(); -+ -+ /* Find slots which have been freed by the other side, and return them -+ ** to the available queue. */ -+ slot_queue_available = state->slot_queue_available; -+ -+ while (slot_queue_available != local->slot_queue_recycle) { -+ unsigned int pos; -+ int slot_index = local->slot_queue[slot_queue_available++ & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); -+ int data_found = 0; -+ -+ vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%x %x %x", -+ state->id, slot_index, (unsigned int)data, -+ local->slot_queue_recycle, slot_queue_available); -+ -+ /* Initialise the bitmask for services which have used this -+ ** slot */ -+ BITSET_ZERO(service_found); -+ -+ pos = 0; -+ -+ while (pos < VCHIQ_SLOT_SIZE) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)(data + pos); -+ int msgid = header->msgid; -+ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) { -+ int port = VCHIQ_MSG_SRCPORT(msgid); -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[port]; -+ int count; -+ spin_lock("a_spinlock); -+ count = service_quota->message_use_count; -+ if (count > 0) -+ service_quota->message_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ -+ if (count == service_quota->message_quota) -+ /* Signal the service that it -+ ** has dropped below its quota -+ */ -+ up(&service_quota->quota_event); -+ else if (count == 0) { -+ vchiq_log_error(vchiq_core_log_level, -+ "service %d " -+ "message_use_count=%d " -+ "(header %x, msgid %x, " -+ "header->msgid %x, " -+ "header->size %x)", -+ port, -+ service_quota-> -+ message_use_count, -+ (unsigned int)header, msgid, -+ header->msgid, -+ header->size); -+ WARN(1, "invalid message use count\n"); -+ } -+ if (!BITSET_IS_SET(service_found, port)) { -+ /* Set the found bit for this service */ -+ BITSET_SET(service_found, port); -+ -+ spin_lock("a_spinlock); -+ count = service_quota->slot_use_count; -+ if (count > 0) -+ service_quota->slot_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ -+ if (count > 0) { -+ /* Signal the service in case -+ ** it has dropped below its -+ ** quota */ -+ up(&service_quota->quota_event); -+ vchiq_log_trace( -+ vchiq_core_log_level, -+ "%d: pfq:%d %x@%x - " -+ "slot_use->%d", -+ state->id, port, -+ header->size, -+ (unsigned int)header, -+ count - 1); -+ } else { -+ vchiq_log_error( -+ vchiq_core_log_level, -+ "service %d " -+ "slot_use_count" -+ "=%d (header %x" -+ ", msgid %x, " -+ "header->msgid" -+ " %x, header->" -+ "size %x)", -+ port, count, -+ (unsigned int)header, -+ msgid, -+ header->msgid, -+ header->size); -+ WARN(1, "bad slot use count\n"); -+ } -+ } -+ -+ data_found = 1; -+ } -+ -+ pos += calc_stride(header->size); -+ if (pos > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "pfq - pos %x: header %x, msgid %x, " -+ "header->msgid %x, header->size %x", -+ pos, (unsigned int)header, msgid, -+ header->msgid, header->size); -+ WARN(1, "invalid slot position\n"); -+ } -+ } -+ -+ if (data_found) { -+ int count; -+ spin_lock("a_spinlock); -+ count = state->data_use_count; -+ if (count > 0) -+ state->data_use_count = -+ count - 1; -+ spin_unlock("a_spinlock); -+ if (count == state->data_quota) -+ up(&state->data_quota_event); -+ } -+ -+ state->slot_queue_available = slot_queue_available; -+ up(&state->slot_available_event); -+ } -+} -+ -+/* Called by the slot handler and application threads */ -+static VCHIQ_STATUS_T -+queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ int msgid, const VCHIQ_ELEMENT_T *elements, -+ int count, int size, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; -+ VCHIQ_HEADER_T *header; -+ int type = VCHIQ_MSG_TYPE(msgid); -+ -+ unsigned int stride; -+ -+ local = state->local; -+ -+ stride = calc_stride(size); -+ -+ WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); -+ -+ if ((type != VCHIQ_MSG_RESUME) && -+ (mutex_lock_interruptible(&state->slot_mutex) != 0)) -+ return VCHIQ_RETRY; -+ -+ if (type == VCHIQ_MSG_DATA) { -+ int tx_end_index; -+ -+ BUG_ON(!service); -+ -+ if (service->closing) { -+ /* The service has been closed */ -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_ERROR; -+ } -+ -+ service_quota = &state->service_quotas[service->localport]; -+ -+ spin_lock("a_spinlock); -+ -+ /* Ensure this service doesn't use more than its quota of -+ ** messages or slots */ -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ -+ /* Ensure data messages don't use more than their quota of -+ ** slots */ -+ while ((tx_end_index != state->previous_data_index) && -+ (state->data_use_count == state->data_quota)) { -+ VCHIQ_STATS_INC(state, data_stalls); -+ spin_unlock("a_spinlock); -+ mutex_unlock(&state->slot_mutex); -+ -+ if (down_interruptible(&state->data_quota_event) -+ != 0) -+ return VCHIQ_RETRY; -+ -+ mutex_lock(&state->slot_mutex); -+ spin_lock("a_spinlock); -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ if ((tx_end_index == state->previous_data_index) || -+ (state->data_use_count < state->data_quota)) { -+ /* Pass the signal on to other waiters */ -+ up(&state->data_quota_event); -+ break; -+ } -+ } -+ -+ while ((service_quota->message_use_count == -+ service_quota->message_quota) || -+ ((tx_end_index != service_quota->previous_tx_index) && -+ (service_quota->slot_use_count == -+ service_quota->slot_quota))) { -+ spin_unlock("a_spinlock); -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: qm:%d %s,%x - quota stall " -+ "(msg %d, slot %d)", -+ state->id, service->localport, -+ msg_type_str(type), size, -+ service_quota->message_use_count, -+ service_quota->slot_use_count); -+ VCHIQ_SERVICE_STATS_INC(service, quota_stalls); -+ mutex_unlock(&state->slot_mutex); -+ if (down_interruptible(&service_quota->quota_event) -+ != 0) -+ return VCHIQ_RETRY; -+ if (service->closing) -+ return VCHIQ_ERROR; -+ if (mutex_lock_interruptible(&state->slot_mutex) != 0) -+ return VCHIQ_RETRY; -+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { -+ /* The service has been closed */ -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_ERROR; -+ } -+ spin_lock("a_spinlock); -+ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( -+ state->local_tx_pos + stride - 1); -+ } -+ -+ spin_unlock("a_spinlock); -+ } -+ -+ header = reserve_space(state, stride, is_blocking); -+ -+ if (!header) { -+ if (service) -+ VCHIQ_SERVICE_STATS_INC(service, slot_stalls); -+ mutex_unlock(&state->slot_mutex); -+ return VCHIQ_RETRY; -+ } -+ -+ if (type == VCHIQ_MSG_DATA) { -+ int i, pos; -+ int tx_end_index; -+ int slot_use_count; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: qm %s@%x,%x (%d->%d)", -+ state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ -+ BUG_ON(!service); -+ -+ for (i = 0, pos = 0; i < (unsigned int)count; -+ pos += elements[i++].size) -+ if (elements[i].size) { -+ if (vchiq_copy_from_user -+ (header->data + pos, elements[i].data, -+ (size_t) elements[i].size) != -+ VCHIQ_SUCCESS) { -+ mutex_unlock(&state->slot_mutex); -+ VCHIQ_SERVICE_STATS_INC(service, -+ error_count); -+ return VCHIQ_ERROR; -+ } -+ if (i == 0) { -+ if (SRVTRACE_ENABLED(service, -+ VCHIQ_LOG_INFO)) -+ vchiq_log_dump_mem("Sent", 0, -+ header->data + pos, -+ min(64u, -+ elements[0].size)); -+ } -+ } -+ -+ spin_lock("a_spinlock); -+ service_quota->message_use_count++; -+ -+ tx_end_index = -+ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1); -+ -+ /* If this transmission can't fit in the last slot used by any -+ ** service, the data_use_count must be increased. */ -+ if (tx_end_index != state->previous_data_index) { -+ state->previous_data_index = tx_end_index; -+ state->data_use_count++; -+ } -+ -+ /* If this isn't the same slot last used by this service, -+ ** the service's slot_use_count must be increased. */ -+ if (tx_end_index != service_quota->previous_tx_index) { -+ service_quota->previous_tx_index = tx_end_index; -+ slot_use_count = ++service_quota->slot_use_count; -+ } else { -+ slot_use_count = 0; -+ } -+ -+ spin_unlock("a_spinlock); -+ -+ if (slot_use_count) -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: qm:%d %s,%x - slot_use->%d (hdr %p)", -+ state->id, service->localport, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, -+ slot_use_count, header); -+ -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); -+ } else { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: qm %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ if (size != 0) { -+ WARN_ON(!((count == 1) && (size == elements[0].size))); -+ memcpy(header->data, elements[0].data, -+ elements[0].size); -+ } -+ VCHIQ_STATS_INC(state, ctrl_tx_count); -+ } -+ -+ header->msgid = msgid; -+ header->size = size; -+ -+ { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ VCHIQ_MSG_TYPE(msgid), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid), -+ size); -+ } -+ -+ /* Make sure the new header is visible to the peer. */ -+ wmb(); -+ -+ /* Make the new tx_pos visible to the peer. */ -+ local->tx_pos = state->local_tx_pos; -+ wmb(); -+ -+ if (service && (type == VCHIQ_MSG_CLOSE)) -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); -+ -+ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ mutex_unlock(&state->slot_mutex); -+ -+ remote_event_signal(&state->remote->trigger); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+/* Called by the slot handler and application threads */ -+static VCHIQ_STATUS_T -+queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, -+ int msgid, const VCHIQ_ELEMENT_T *elements, -+ int count, int size, int is_blocking) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_HEADER_T *header; -+ -+ local = state->local; -+ -+ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) && -+ (mutex_lock_interruptible(&state->sync_mutex) != 0)) -+ return VCHIQ_RETRY; -+ -+ remote_event_wait(&local->sync_release); -+ -+ rmb(); -+ -+ header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ local->slot_sync); -+ -+ { -+ int oldmsgid = header->msgid; -+ if (oldmsgid != VCHIQ_MSGID_PADDING) -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: qms - msgid %x, not PADDING", -+ state->id, oldmsgid); -+ } -+ -+ if (service) { -+ int i, pos; -+ -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: qms %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ -+ for (i = 0, pos = 0; i < (unsigned int)count; -+ pos += elements[i++].size) -+ if (elements[i].size) { -+ if (vchiq_copy_from_user -+ (header->data + pos, elements[i].data, -+ (size_t) elements[i].size) != -+ VCHIQ_SUCCESS) { -+ mutex_unlock(&state->sync_mutex); -+ VCHIQ_SERVICE_STATS_INC(service, -+ error_count); -+ return VCHIQ_ERROR; -+ } -+ if (i == 0) { -+ if (vchiq_sync_log_level >= -+ VCHIQ_LOG_TRACE) -+ vchiq_log_dump_mem("Sent Sync", -+ 0, header->data + pos, -+ min(64u, -+ elements[0].size)); -+ } -+ } -+ -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); -+ } else { -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: qms %s@%x,%x (%d->%d)", state->id, -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ (unsigned int)header, size, -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid)); -+ if (size != 0) { -+ WARN_ON(!((count == 1) && (size == elements[0].size))); -+ memcpy(header->data, elements[0].data, -+ elements[0].size); -+ } -+ VCHIQ_STATS_INC(state, ctrl_tx_count); -+ } -+ -+ header->size = size; -+ header->msgid = msgid; -+ -+ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ -+ vchiq_log_trace(vchiq_sync_log_level, -+ "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", -+ msg_type_str(VCHIQ_MSG_TYPE(msgid)), -+ VCHIQ_MSG_TYPE(msgid), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ VCHIQ_MSG_SRCPORT(msgid), -+ VCHIQ_MSG_DSTPORT(msgid), -+ size); -+ } -+ -+ /* Make sure the new header is visible to the peer. */ -+ wmb(); -+ -+ remote_event_signal(&state->remote->sync_trigger); -+ -+ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ mutex_unlock(&state->sync_mutex); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+static inline void -+claim_slot(VCHIQ_SLOT_INFO_T *slot) -+{ -+ slot->use_count++; -+} -+ -+static void -+release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info, -+ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service) -+{ -+ int release_count; -+ -+ mutex_lock(&state->recycle_mutex); -+ -+ if (header) { -+ int msgid = header->msgid; -+ if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) || -+ (service && service->closing)) { -+ mutex_unlock(&state->recycle_mutex); -+ return; -+ } -+ -+ /* Rewrite the message header to prevent a double -+ ** release */ -+ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; -+ } -+ -+ release_count = slot_info->release_count; -+ slot_info->release_count = ++release_count; -+ -+ if (release_count == slot_info->use_count) { -+ int slot_queue_recycle; -+ /* Add to the freed queue */ -+ -+ /* A read barrier is necessary here to prevent speculative -+ ** fetches of remote->slot_queue_recycle from overtaking the -+ ** mutex. */ -+ rmb(); -+ -+ slot_queue_recycle = state->remote->slot_queue_recycle; -+ state->remote->slot_queue[slot_queue_recycle & -+ VCHIQ_SLOT_QUEUE_MASK] = -+ SLOT_INDEX_FROM_INFO(state, slot_info); -+ state->remote->slot_queue_recycle = slot_queue_recycle + 1; -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: release_slot %d - recycle->%x", -+ state->id, SLOT_INDEX_FROM_INFO(state, slot_info), -+ state->remote->slot_queue_recycle); -+ -+ /* A write barrier is necessary, but remote_event_signal -+ ** contains one. */ -+ remote_event_signal(&state->remote->recycle); -+ } -+ -+ mutex_unlock(&state->recycle_mutex); -+} -+ -+/* Called by the slot handler - don't hold the bulk mutex */ -+static VCHIQ_STATUS_T -+notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue, -+ int retry_poll) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: nb:%d %cx - p=%x rn=%x r=%x", -+ service->state->id, service->localport, -+ (queue == &service->bulk_tx) ? 't' : 'r', -+ queue->process, queue->remote_notify, queue->remove); -+ -+ if (service->state->is_master) { -+ while (queue->remote_notify != queue->process) { -+ VCHIQ_BULK_T *bulk = -+ &queue->bulks[BULK_INDEX(queue->remote_notify)]; -+ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE; -+ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, -+ service->remoteport); -+ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 }; -+ /* Only reply to non-dummy bulk requests */ -+ if (bulk->remote_data) { -+ status = queue_message(service->state, NULL, -+ msgid, &element, 1, 4, 0); -+ if (status != VCHIQ_SUCCESS) -+ break; -+ } -+ queue->remote_notify++; -+ } -+ } else { -+ queue->remote_notify = queue->process; -+ } -+ -+ if (status == VCHIQ_SUCCESS) { -+ while (queue->remove != queue->remote_notify) { -+ VCHIQ_BULK_T *bulk = -+ &queue->bulks[BULK_INDEX(queue->remove)]; -+ -+ /* Only generate callbacks for non-dummy bulk -+ ** requests, and non-terminated services */ -+ if (bulk->data && service->instance) { -+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { -+ if (bulk->dir == VCHIQ_BULK_TRANSMIT) { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_tx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, -+ bulk_tx_bytes, -+ bulk->actual); -+ } else { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_rx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, -+ bulk_rx_bytes, -+ bulk->actual); -+ } -+ } else { -+ VCHIQ_SERVICE_STATS_INC(service, -+ bulk_aborted_count); -+ } -+ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { -+ struct bulk_waiter *waiter; -+ spin_lock(&bulk_waiter_spinlock); -+ waiter = bulk->userdata; -+ if (waiter) { -+ waiter->actual = bulk->actual; -+ up(&waiter->event); -+ } -+ spin_unlock(&bulk_waiter_spinlock); -+ } else if (bulk->mode == -+ VCHIQ_BULK_MODE_CALLBACK) { -+ VCHIQ_REASON_T reason = (bulk->dir == -+ VCHIQ_BULK_TRANSMIT) ? -+ ((bulk->actual == -+ VCHIQ_BULK_ACTUAL_ABORTED) ? -+ VCHIQ_BULK_TRANSMIT_ABORTED : -+ VCHIQ_BULK_TRANSMIT_DONE) : -+ ((bulk->actual == -+ VCHIQ_BULK_ACTUAL_ABORTED) ? -+ VCHIQ_BULK_RECEIVE_ABORTED : -+ VCHIQ_BULK_RECEIVE_DONE); -+ status = make_service_callback(service, -+ reason, NULL, bulk->userdata); -+ if (status == VCHIQ_RETRY) -+ break; -+ } -+ } -+ -+ queue->remove++; -+ up(&service->bulk_remove_event); -+ } -+ if (!retry_poll) -+ status = VCHIQ_SUCCESS; -+ } -+ -+ if (status == VCHIQ_RETRY) -+ request_poll(service->state, service, -+ (queue == &service->bulk_tx) ? -+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); -+ -+ return status; -+} -+ -+/* Called by the slot handler thread */ -+static void -+poll_services(VCHIQ_STATE_T *state) -+{ -+ int group, i; -+ -+ for (group = 0; group < BITSET_SIZE(state->unused_service); group++) { -+ uint32_t flags; -+ flags = atomic_xchg(&state->poll_services[group], 0); -+ for (i = 0; flags; i++) { -+ if (flags & (1 << i)) { -+ VCHIQ_SERVICE_T *service = -+ find_service_by_port(state, -+ (group<<5) + i); -+ uint32_t service_flags; -+ flags &= ~(1 << i); -+ if (!service) -+ continue; -+ service_flags = -+ atomic_xchg(&service->poll_flags, 0); -+ if (service_flags & -+ (1 << VCHIQ_POLL_REMOVE)) { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: ps - remove %d<->%d", -+ state->id, service->localport, -+ service->remoteport); -+ -+ /* Make it look like a client, because -+ it must be removed and not left in -+ the LISTENING state. */ -+ service->public_fourcc = -+ VCHIQ_FOURCC_INVALID; -+ -+ if (vchiq_close_service_internal( -+ service, 0/*!close_recvd*/) != -+ VCHIQ_SUCCESS) -+ request_poll(state, service, -+ VCHIQ_POLL_REMOVE); -+ } else if (service_flags & -+ (1 << VCHIQ_POLL_TERMINATE)) { -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: ps - terminate %d<->%d", -+ state->id, service->localport, -+ service->remoteport); -+ if (vchiq_close_service_internal( -+ service, 0/*!close_recvd*/) != -+ VCHIQ_SUCCESS) -+ request_poll(state, service, -+ VCHIQ_POLL_TERMINATE); -+ } -+ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY)) -+ notify_bulks(service, -+ &service->bulk_tx, -+ 1/*retry_poll*/); -+ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY)) -+ notify_bulks(service, -+ &service->bulk_rx, -+ 1/*retry_poll*/); -+ unlock_service(service); -+ } -+ } -+ } -+} -+ -+/* Called by the slot handler or application threads, holding the bulk mutex. */ -+static int -+resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ int resolved = 0; -+ int rc; -+ -+ while ((queue->process != queue->local_insert) && -+ (queue->process != queue->remote_insert)) { -+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: rb:%d %cx - li=%x ri=%x p=%x", -+ state->id, service->localport, -+ (queue == &service->bulk_tx) ? 't' : 'r', -+ queue->local_insert, queue->remote_insert, -+ queue->process); -+ -+ WARN_ON(!((int)(queue->local_insert - queue->process) > 0)); -+ WARN_ON(!((int)(queue->remote_insert - queue->process) > 0)); -+ -+ rc = mutex_lock_interruptible(&state->bulk_transfer_mutex); -+ if (rc != 0) -+ break; -+ -+ vchiq_transfer_bulk(bulk); -+ mutex_unlock(&state->bulk_transfer_mutex); -+ -+ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { -+ const char *header = (queue == &service->bulk_tx) ? -+ "Send Bulk to" : "Recv Bulk from"; -+ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d len:%d %x<->%x", -+ header, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ (unsigned int)bulk->data, -+ (unsigned int)bulk->remote_data); -+ else -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d ABORTED - tx len:%d," -+ " rx len:%d %x<->%x", -+ header, -+ VCHIQ_FOURCC_AS_4CHARS( -+ service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ bulk->remote_size, -+ (unsigned int)bulk->data, -+ (unsigned int)bulk->remote_data); -+ } -+ -+ vchiq_complete_bulk(bulk); -+ queue->process++; -+ resolved++; -+ } -+ return resolved; -+} -+ -+/* Called with the bulk_mutex held */ -+static void -+abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) -+{ -+ int is_tx = (queue == &service->bulk_tx); -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: aob:%d %cx - li=%x ri=%x p=%x", -+ service->state->id, service->localport, is_tx ? 't' : 'r', -+ queue->local_insert, queue->remote_insert, queue->process); -+ -+ WARN_ON(!((int)(queue->local_insert - queue->process) >= 0)); -+ WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0)); -+ -+ while ((queue->process != queue->local_insert) || -+ (queue->process != queue->remote_insert)) { -+ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; -+ -+ if (queue->process == queue->remote_insert) { -+ /* fabricate a matching dummy bulk */ -+ bulk->remote_data = NULL; -+ bulk->remote_size = 0; -+ queue->remote_insert++; -+ } -+ -+ if (queue->process != queue->local_insert) { -+ vchiq_complete_bulk(bulk); -+ -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "%s %c%c%c%c d:%d ABORTED - tx len:%d, " -+ "rx len:%d", -+ is_tx ? "Send Bulk to" : "Recv Bulk from", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->remoteport, -+ bulk->size, -+ bulk->remote_size); -+ } else { -+ /* fabricate a matching dummy bulk */ -+ bulk->data = NULL; -+ bulk->size = 0; -+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; -+ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : -+ VCHIQ_BULK_RECEIVE; -+ queue->local_insert++; -+ } -+ -+ queue->process++; -+ } -+} -+ -+/* Called from the slot handler thread */ -+static void -+pause_bulks(VCHIQ_STATE_T *state) -+{ -+ if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) { -+ WARN_ON_ONCE(1); -+ atomic_set(&pause_bulks_count, 1); -+ return; -+ } -+ -+ /* Block bulk transfers from all services */ -+ mutex_lock(&state->bulk_transfer_mutex); -+} -+ -+/* Called from the slot handler thread */ -+static void -+resume_bulks(VCHIQ_STATE_T *state) -+{ -+ int i; -+ if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) { -+ WARN_ON_ONCE(1); -+ atomic_set(&pause_bulks_count, 0); -+ return; -+ } -+ -+ /* Allow bulk transfers from all services */ -+ mutex_unlock(&state->bulk_transfer_mutex); -+ -+ if (state->deferred_bulks == 0) -+ return; -+ -+ /* Deal with any bulks which had to be deferred due to being in -+ * paused state. Don't try to match up to number of deferred bulks -+ * in case we've had something come and close the service in the -+ * interim - just process all bulk queues for all services */ -+ vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks", -+ __func__, state->deferred_bulks); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = state->services[i]; -+ int resolved_rx = 0; -+ int resolved_tx = 0; -+ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) -+ continue; -+ -+ mutex_lock(&service->bulk_mutex); -+ resolved_rx = resolve_bulks(service, &service->bulk_rx); -+ resolved_tx = resolve_bulks(service, &service->bulk_tx); -+ mutex_unlock(&service->bulk_mutex); -+ if (resolved_rx) -+ notify_bulks(service, &service->bulk_rx, 1); -+ if (resolved_tx) -+ notify_bulks(service, &service->bulk_tx, 1); -+ } -+ state->deferred_bulks = 0; -+} -+ -+static int -+parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) -+{ -+ VCHIQ_SERVICE_T *service = NULL; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ msgid = header->msgid; -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ if (size >= sizeof(struct vchiq_open_payload)) { -+ const struct vchiq_open_payload *payload = -+ (struct vchiq_open_payload *)header->data; -+ unsigned int fourcc; -+ -+ fourcc = payload->fourcc; -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs OPEN@%x (%d->'%c%c%c%c')", -+ state->id, (unsigned int)header, -+ localport, -+ VCHIQ_FOURCC_AS_4CHARS(fourcc)); -+ -+ service = get_listening_service(state, fourcc); -+ -+ if (service) { -+ /* A matching service exists */ -+ short version = payload->version; -+ short version_min = payload->version_min; -+ if ((service->version < version_min) || -+ (version < service->version_min)) { -+ /* Version mismatch */ -+ vchiq_loud_error_header(); -+ vchiq_loud_error("%d: service %d (%c%c%c%c) " -+ "version mismatch - local (%d, min %d)" -+ " vs. remote (%d, min %d)", -+ state->id, service->localport, -+ VCHIQ_FOURCC_AS_4CHARS(fourcc), -+ service->version, service->version_min, -+ version, version_min); -+ vchiq_loud_error_footer(); -+ unlock_service(service); -+ service = NULL; -+ goto fail_open; -+ } -+ service->peer_version = version; -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { -+ struct vchiq_openack_payload ack_payload = { -+ service->version -+ }; -+ VCHIQ_ELEMENT_T body = { -+ &ack_payload, -+ sizeof(ack_payload) -+ }; -+ -+ /* Acknowledge the OPEN */ -+ if (service->sync) { -+ if (queue_message_sync(state, NULL, -+ VCHIQ_MAKE_MSG( -+ VCHIQ_MSG_OPENACK, -+ service->localport, -+ remoteport), -+ &body, 1, sizeof(ack_payload), -+ 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ } else { -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG( -+ VCHIQ_MSG_OPENACK, -+ service->localport, -+ remoteport), -+ &body, 1, sizeof(ack_payload), -+ 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ } -+ -+ /* The service is now open */ -+ vchiq_set_service_state(service, -+ service->sync ? VCHIQ_SRVSTATE_OPENSYNC -+ : VCHIQ_SRVSTATE_OPEN); -+ } -+ -+ service->remoteport = remoteport; -+ service->client_id = ((int *)header->data)[1]; -+ if (make_service_callback(service, VCHIQ_SERVICE_OPENED, -+ NULL, NULL) == VCHIQ_RETRY) { -+ /* Bail out if not ready */ -+ service->remoteport = VCHIQ_PORT_FREE; -+ goto bail_not_ready; -+ } -+ -+ /* Success - the message has been dealt with */ -+ unlock_service(service); -+ return 1; -+ } -+ } -+ -+fail_open: -+ /* No available service, or an invalid request - send a CLOSE */ -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)), -+ NULL, 0, 0, 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ -+ return 1; -+ -+bail_not_ready: -+ if (service) -+ unlock_service(service); -+ -+ return 0; -+} -+ -+/* Called by the slot handler thread */ -+static void -+parse_rx_slots(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_SHARED_STATE_T *remote = state->remote; -+ VCHIQ_SERVICE_T *service = NULL; -+ int tx_pos; -+ DEBUG_INITIALISE(state->local) -+ -+ tx_pos = remote->tx_pos; -+ -+ while (state->rx_pos != tx_pos) { -+ VCHIQ_HEADER_T *header; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (!state->rx_data) { -+ int rx_index; -+ WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0)); -+ rx_index = remote->slot_queue[ -+ SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & -+ VCHIQ_SLOT_QUEUE_MASK]; -+ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, -+ rx_index); -+ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index); -+ -+ /* Initialise use_count to one, and increment -+ ** release_count at the end of the slot to avoid -+ ** releasing the slot prematurely. */ -+ state->rx_info->use_count = 1; -+ state->rx_info->release_count = 0; -+ } -+ -+ header = (VCHIQ_HEADER_T *)(state->rx_data + -+ (state->rx_pos & VCHIQ_SLOT_MASK)); -+ DEBUG_VALUE(PARSE_HEADER, (int)header); -+ msgid = header->msgid; -+ DEBUG_VALUE(PARSE_MSGID, msgid); -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ -+ if (type != VCHIQ_MSG_DATA) -+ VCHIQ_STATS_INC(state, ctrl_rx_count); -+ -+ switch (type) { -+ case VCHIQ_MSG_OPENACK: -+ case VCHIQ_MSG_CLOSE: -+ case VCHIQ_MSG_DATA: -+ case VCHIQ_MSG_BULK_RX: -+ case VCHIQ_MSG_BULK_TX: -+ case VCHIQ_MSG_BULK_RX_DONE: -+ case VCHIQ_MSG_BULK_TX_DONE: -+ service = find_service_by_port(state, localport); -+ if ((!service || -+ ((service->remoteport != remoteport) && -+ (service->remoteport != VCHIQ_PORT_FREE))) && -+ (localport == 0) && -+ (type == VCHIQ_MSG_CLOSE)) { -+ /* This could be a CLOSE from a client which -+ hadn't yet received the OPENACK - look for -+ the connected service */ -+ if (service) -+ unlock_service(service); -+ service = get_connected_service(state, -+ remoteport); -+ if (service) -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) - " -+ "found connected service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ service->localport); -+ } -+ -+ if (!service) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) - " -+ "invalid/closed service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, localport); -+ goto skip_message; -+ } -+ break; -+ default: -+ break; -+ } -+ -+ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ vchiq_log_info(SRVTRACE_LEVEL(service), -+ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d " -+ "len:%d", -+ msg_type_str(type), type, -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ remoteport, localport, size); -+ if (size > 0) -+ vchiq_log_dump_mem("Rcvd", 0, header->data, -+ min(64, size)); -+ } -+ -+ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) -+ > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "header %x (msgid %x) - size %x too big for " -+ "slot", -+ (unsigned int)header, (unsigned int)msgid, -+ (unsigned int)size); -+ WARN(1, "oversized for slot\n"); -+ } -+ -+ switch (type) { -+ case VCHIQ_MSG_OPEN: -+ WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0)); -+ if (!parse_open(state, header)) -+ goto bail_not_ready; -+ break; -+ case VCHIQ_MSG_OPENACK: -+ if (size >= sizeof(struct vchiq_openack_payload)) { -+ const struct vchiq_openack_payload *payload = -+ (struct vchiq_openack_payload *) -+ header->data; -+ service->peer_version = payload->version; -+ } -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs OPENACK@%x,%x (%d->%d) v:%d", -+ state->id, (unsigned int)header, size, -+ remoteport, localport, service->peer_version); -+ if (service->srvstate == -+ VCHIQ_SRVSTATE_OPENING) { -+ service->remoteport = remoteport; -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_OPEN); -+ up(&service->remove_event); -+ } else -+ vchiq_log_error(vchiq_core_log_level, -+ "OPENACK received in state %s", -+ srvstate_names[service->srvstate]); -+ break; -+ case VCHIQ_MSG_CLOSE: -+ WARN_ON(size != 0); /* There should be no data */ -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs CLOSE@%x (%d->%d)", -+ state->id, (unsigned int)header, -+ remoteport, localport); -+ -+ mark_service_closing_internal(service, 1); -+ -+ if (vchiq_close_service_internal(service, -+ 1/*close_recvd*/) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "Close Service %c%c%c%c s:%u d:%d", -+ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), -+ service->localport, -+ service->remoteport); -+ break; -+ case VCHIQ_MSG_DATA: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs DATA@%x,%x (%d->%d)", -+ state->id, (unsigned int)header, size, -+ remoteport, localport); -+ -+ if ((service->remoteport == remoteport) -+ && (service->srvstate == -+ VCHIQ_SRVSTATE_OPEN)) { -+ header->msgid = msgid | VCHIQ_MSGID_CLAIMED; -+ claim_slot(state->rx_info); -+ DEBUG_TRACE(PARSE_LINE); -+ if (make_service_callback(service, -+ VCHIQ_MESSAGE_AVAILABLE, header, -+ NULL) == VCHIQ_RETRY) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count); -+ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, -+ size); -+ } else { -+ VCHIQ_STATS_INC(state, error_count); -+ } -+ break; -+ case VCHIQ_MSG_CONNECT: -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs CONNECT@%x", -+ state->id, (unsigned int)header); -+ up(&state->connect); -+ break; -+ case VCHIQ_MSG_BULK_RX: -+ case VCHIQ_MSG_BULK_TX: { -+ VCHIQ_BULK_QUEUE_T *queue; -+ WARN_ON(!state->is_master); -+ queue = (type == VCHIQ_MSG_BULK_RX) ? -+ &service->bulk_tx : &service->bulk_rx; -+ if ((service->remoteport == remoteport) -+ && (service->srvstate == -+ VCHIQ_SRVSTATE_OPEN)) { -+ VCHIQ_BULK_T *bulk; -+ int resolved = 0; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (mutex_lock_interruptible( -+ &service->bulk_mutex) != 0) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ -+ WARN_ON(!(queue->remote_insert < queue->remove + -+ VCHIQ_NUM_SERVICE_BULKS)); -+ bulk = &queue->bulks[ -+ BULK_INDEX(queue->remote_insert)]; -+ bulk->remote_data = -+ (void *)((int *)header->data)[0]; -+ bulk->remote_size = ((int *)header->data)[1]; -+ wmb(); -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) %x@%x", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ bulk->remote_size, -+ (unsigned int)bulk->remote_data); -+ -+ queue->remote_insert++; -+ -+ if (atomic_read(&pause_bulks_count)) { -+ state->deferred_bulks++; -+ vchiq_log_info(vchiq_core_log_level, -+ "%s: deferring bulk (%d)", -+ __func__, -+ state->deferred_bulks); -+ if (state->conn_state != -+ VCHIQ_CONNSTATE_PAUSE_SENT) -+ vchiq_log_error( -+ vchiq_core_log_level, -+ "%s: bulks paused in " -+ "unexpected state %s", -+ __func__, -+ conn_state_names[ -+ state->conn_state]); -+ } else if (state->conn_state == -+ VCHIQ_CONNSTATE_CONNECTED) { -+ DEBUG_TRACE(PARSE_LINE); -+ resolved = resolve_bulks(service, -+ queue); -+ } -+ -+ mutex_unlock(&service->bulk_mutex); -+ if (resolved) -+ notify_bulks(service, queue, -+ 1/*retry_poll*/); -+ } -+ } break; -+ case VCHIQ_MSG_BULK_RX_DONE: -+ case VCHIQ_MSG_BULK_TX_DONE: -+ WARN_ON(state->is_master); -+ if ((service->remoteport == remoteport) -+ && (service->srvstate != -+ VCHIQ_SRVSTATE_FREE)) { -+ VCHIQ_BULK_QUEUE_T *queue; -+ VCHIQ_BULK_T *bulk; -+ -+ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ? -+ &service->bulk_rx : &service->bulk_tx; -+ -+ DEBUG_TRACE(PARSE_LINE); -+ if (mutex_lock_interruptible( -+ &service->bulk_mutex) != 0) { -+ DEBUG_TRACE(PARSE_LINE); -+ goto bail_not_ready; -+ } -+ if ((int)(queue->remote_insert - -+ queue->local_insert) >= 0) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) " -+ "unexpected (ri=%d,li=%d)", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ queue->remote_insert, -+ queue->local_insert); -+ mutex_unlock(&service->bulk_mutex); -+ break; -+ } -+ -+ BUG_ON(queue->process == queue->local_insert); -+ BUG_ON(queue->process != queue->remote_insert); -+ -+ bulk = &queue->bulks[ -+ BULK_INDEX(queue->remote_insert)]; -+ bulk->actual = *(int *)header->data; -+ queue->remote_insert++; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: prs %s@%x (%d->%d) %x@%x", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, -+ bulk->actual, (unsigned int)bulk->data); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs:%d %cx li=%x ri=%x p=%x", -+ state->id, localport, -+ (type == VCHIQ_MSG_BULK_RX_DONE) ? -+ 'r' : 't', -+ queue->local_insert, -+ queue->remote_insert, queue->process); -+ -+ DEBUG_TRACE(PARSE_LINE); -+ WARN_ON(queue->process == queue->local_insert); -+ vchiq_complete_bulk(bulk); -+ queue->process++; -+ mutex_unlock(&service->bulk_mutex); -+ DEBUG_TRACE(PARSE_LINE); -+ notify_bulks(service, queue, 1/*retry_poll*/); -+ DEBUG_TRACE(PARSE_LINE); -+ } -+ break; -+ case VCHIQ_MSG_PADDING: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs PADDING@%x,%x", -+ state->id, (unsigned int)header, size); -+ break; -+ case VCHIQ_MSG_PAUSE: -+ /* If initiated, signal the application thread */ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs PAUSE@%x,%x", -+ state->id, (unsigned int)header, size); -+ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: PAUSE received in state PAUSED", -+ state->id); -+ break; -+ } -+ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { -+ /* Send a PAUSE in response */ -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -+ NULL, 0, 0, 0) == VCHIQ_RETRY) -+ goto bail_not_ready; -+ if (state->is_master) -+ pause_bulks(state); -+ } -+ /* At this point slot_mutex is held */ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); -+ vchiq_platform_paused(state); -+ break; -+ case VCHIQ_MSG_RESUME: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: prs RESUME@%x,%x", -+ state->id, (unsigned int)header, size); -+ /* Release the slot mutex */ -+ mutex_unlock(&state->slot_mutex); -+ if (state->is_master) -+ resume_bulks(state); -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); -+ vchiq_platform_resumed(state); -+ break; -+ -+ case VCHIQ_MSG_REMOTE_USE: -+ vchiq_on_remote_use(state); -+ break; -+ case VCHIQ_MSG_REMOTE_RELEASE: -+ vchiq_on_remote_release(state); -+ break; -+ case VCHIQ_MSG_REMOTE_USE_ACTIVE: -+ vchiq_on_remote_use_active(state); -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: prs invalid msgid %x@%x,%x", -+ state->id, msgid, (unsigned int)header, size); -+ WARN(1, "invalid message\n"); -+ break; -+ } -+ -+skip_message: -+ if (service) { -+ unlock_service(service); -+ service = NULL; -+ } -+ -+ state->rx_pos += calc_stride(size); -+ -+ DEBUG_TRACE(PARSE_LINE); -+ /* Perform some housekeeping when the end of the slot is -+ ** reached. */ -+ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) { -+ /* Remove the extra reference count. */ -+ release_slot(state, state->rx_info, NULL, NULL); -+ state->rx_data = NULL; -+ } -+ } -+ -+bail_not_ready: -+ if (service) -+ unlock_service(service); -+} -+ -+/* Called by the slot handler thread */ -+static int -+slot_handler_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ DEBUG_INITIALISE(local) -+ -+ while (1) { -+ DEBUG_COUNT(SLOT_HANDLER_COUNT); -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ remote_event_wait(&local->trigger); -+ -+ rmb(); -+ -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ if (state->poll_needed) { -+ /* Check if we need to suspend - may change our -+ * conn_state */ -+ vchiq_platform_check_suspend(state); -+ -+ state->poll_needed = 0; -+ -+ /* Handle service polling and other rare conditions here -+ ** out of the mainline code */ -+ switch (state->conn_state) { -+ case VCHIQ_CONNSTATE_CONNECTED: -+ /* Poll the services as requested */ -+ poll_services(state); -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSING: -+ if (state->is_master) -+ pause_bulks(state); -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -+ NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ vchiq_set_conn_state(state, -+ VCHIQ_CONNSTATE_PAUSE_SENT); -+ } else { -+ if (state->is_master) -+ resume_bulks(state); -+ /* Retry later */ -+ state->poll_needed = 1; -+ } -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSED: -+ vchiq_platform_resume(state); -+ break; -+ -+ case VCHIQ_CONNSTATE_RESUMING: -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), -+ NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ if (state->is_master) -+ resume_bulks(state); -+ vchiq_set_conn_state(state, -+ VCHIQ_CONNSTATE_CONNECTED); -+ vchiq_platform_resumed(state); -+ } else { -+ /* This should really be impossible, -+ ** since the PAUSE should have flushed -+ ** through outstanding messages. */ -+ vchiq_log_error(vchiq_core_log_level, -+ "Failed to send RESUME " -+ "message"); -+ BUG(); -+ } -+ break; -+ -+ case VCHIQ_CONNSTATE_PAUSE_TIMEOUT: -+ case VCHIQ_CONNSTATE_RESUME_TIMEOUT: -+ vchiq_platform_handle_timeout(state); -+ break; -+ default: -+ break; -+ } -+ -+ -+ } -+ -+ DEBUG_TRACE(SLOT_HANDLER_LINE); -+ parse_rx_slots(state); -+ } -+ return 0; -+} -+ -+ -+/* Called by the recycle thread */ -+static int -+recycle_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ -+ while (1) { -+ remote_event_wait(&local->recycle); -+ -+ process_free_queue(state); -+ } -+ return 0; -+} -+ -+ -+/* Called by the sync thread */ -+static int -+sync_func(void *v) -+{ -+ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; -+ VCHIQ_SHARED_STATE_T *local = state->local; -+ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ state->remote->slot_sync); -+ -+ while (1) { -+ VCHIQ_SERVICE_T *service; -+ int msgid, size; -+ int type; -+ unsigned int localport, remoteport; -+ -+ remote_event_wait(&local->sync_trigger); -+ -+ rmb(); -+ -+ msgid = header->msgid; -+ size = header->size; -+ type = VCHIQ_MSG_TYPE(msgid); -+ localport = VCHIQ_MSG_DSTPORT(msgid); -+ remoteport = VCHIQ_MSG_SRCPORT(msgid); -+ -+ service = find_service_by_port(state, localport); -+ -+ if (!service) { -+ vchiq_log_error(vchiq_sync_log_level, -+ "%d: sf %s@%x (%d->%d) - " -+ "invalid/closed service %d", -+ state->id, msg_type_str(type), -+ (unsigned int)header, -+ remoteport, localport, localport); -+ release_message_sync(state, header); -+ continue; -+ } -+ -+ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { -+ int svc_fourcc; -+ -+ svc_fourcc = service -+ ? service->base.fourcc -+ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); -+ vchiq_log_trace(vchiq_sync_log_level, -+ "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d", -+ msg_type_str(type), -+ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), -+ remoteport, localport, size); -+ if (size > 0) -+ vchiq_log_dump_mem("Rcvd", 0, header->data, -+ min(64, size)); -+ } -+ -+ switch (type) { -+ case VCHIQ_MSG_OPENACK: -+ if (size >= sizeof(struct vchiq_openack_payload)) { -+ const struct vchiq_openack_payload *payload = -+ (struct vchiq_openack_payload *) -+ header->data; -+ service->peer_version = payload->version; -+ } -+ vchiq_log_info(vchiq_sync_log_level, -+ "%d: sf OPENACK@%x,%x (%d->%d) v:%d", -+ state->id, (unsigned int)header, size, -+ remoteport, localport, service->peer_version); -+ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { -+ service->remoteport = remoteport; -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_OPENSYNC); -+ up(&service->remove_event); -+ } -+ release_message_sync(state, header); -+ break; -+ -+ case VCHIQ_MSG_DATA: -+ vchiq_log_trace(vchiq_sync_log_level, -+ "%d: sf DATA@%x,%x (%d->%d)", -+ state->id, (unsigned int)header, size, -+ remoteport, localport); -+ -+ if ((service->remoteport == remoteport) && -+ (service->srvstate == -+ VCHIQ_SRVSTATE_OPENSYNC)) { -+ if (make_service_callback(service, -+ VCHIQ_MESSAGE_AVAILABLE, header, -+ NULL) == VCHIQ_RETRY) -+ vchiq_log_error(vchiq_sync_log_level, -+ "synchronous callback to " -+ "service %d returns " -+ "VCHIQ_RETRY", -+ localport); -+ } -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_sync_log_level, -+ "%d: sf unexpected msgid %x@%x,%x", -+ state->id, msgid, (unsigned int)header, size); -+ release_message_sync(state, header); -+ break; -+ } -+ -+ unlock_service(service); -+ } -+ -+ return 0; -+} -+ -+ -+static void -+init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) -+{ -+ queue->local_insert = 0; -+ queue->remote_insert = 0; -+ queue->process = 0; -+ queue->remote_notify = 0; -+ queue->remove = 0; -+} -+ -+ -+inline const char * -+get_conn_state_name(VCHIQ_CONNSTATE_T conn_state) -+{ -+ return conn_state_names[conn_state]; -+} -+ -+ -+VCHIQ_SLOT_ZERO_T * -+vchiq_init_slots(void *mem_base, int mem_size) -+{ -+ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK; -+ VCHIQ_SLOT_ZERO_T *slot_zero = -+ (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align); -+ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE; -+ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS; -+ -+ /* Ensure there is enough memory to run an absolutely minimum system */ -+ num_slots -= first_data_slot; -+ -+ if (num_slots < 4) { -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_init_slots - insufficient memory %x bytes", -+ mem_size); -+ return NULL; -+ } -+ -+ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T)); -+ -+ slot_zero->magic = VCHIQ_MAGIC; -+ slot_zero->version = VCHIQ_VERSION; -+ slot_zero->version_min = VCHIQ_VERSION_MIN; -+ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T); -+ slot_zero->slot_size = VCHIQ_SLOT_SIZE; -+ slot_zero->max_slots = VCHIQ_MAX_SLOTS; -+ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE; -+ -+ slot_zero->master.slot_sync = first_data_slot; -+ slot_zero->master.slot_first = first_data_slot + 1; -+ slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1; -+ slot_zero->slave.slot_sync = first_data_slot + (num_slots/2); -+ slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1; -+ slot_zero->slave.slot_last = first_data_slot + num_slots - 1; -+ -+ return slot_zero; -+} -+ -+VCHIQ_STATUS_T -+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, -+ int is_master) -+{ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_STATUS_T status; -+ char threadname[10]; -+ static int id; -+ int i; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%s: slot_zero = 0x%08lx, is_master = %d", -+ __func__, (unsigned long)slot_zero, is_master); -+ -+ /* Check the input configuration */ -+ -+ if (slot_zero->magic != VCHIQ_MAGIC) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Invalid VCHIQ magic value found."); -+ vchiq_loud_error("slot_zero=%x: magic=%x (expected %x)", -+ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (slot_zero->version < VCHIQ_VERSION_MIN) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Incompatible VCHIQ versions found."); -+ vchiq_loud_error("slot_zero=%x: VideoCore version=%d " -+ "(minimum %d)", -+ (unsigned int)slot_zero, slot_zero->version, -+ VCHIQ_VERSION_MIN); -+ vchiq_loud_error("Restart with a newer VideoCore image."); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (VCHIQ_VERSION < slot_zero->version_min) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("Incompatible VCHIQ versions found."); -+ vchiq_loud_error("slot_zero=%x: version=%d (VideoCore " -+ "minimum %d)", -+ (unsigned int)slot_zero, VCHIQ_VERSION, -+ slot_zero->version_min); -+ vchiq_loud_error("Restart with a newer kernel."); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) || -+ (slot_zero->slot_size != VCHIQ_SLOT_SIZE) || -+ (slot_zero->max_slots != VCHIQ_MAX_SLOTS) || -+ (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) { -+ vchiq_loud_error_header(); -+ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) -+ vchiq_loud_error("slot_zero=%x: slot_zero_size=%x " -+ "(expected %x)", -+ (unsigned int)slot_zero, -+ slot_zero->slot_zero_size, -+ sizeof(VCHIQ_SLOT_ZERO_T)); -+ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE) -+ vchiq_loud_error("slot_zero=%x: slot_size=%d " -+ "(expected %d", -+ (unsigned int)slot_zero, slot_zero->slot_size, -+ VCHIQ_SLOT_SIZE); -+ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS) -+ vchiq_loud_error("slot_zero=%x: max_slots=%d " -+ "(expected %d)", -+ (unsigned int)slot_zero, slot_zero->max_slots, -+ VCHIQ_MAX_SLOTS); -+ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE) -+ vchiq_loud_error("slot_zero=%x: max_slots_per_side=%d " -+ "(expected %d)", -+ (unsigned int)slot_zero, -+ slot_zero->max_slots_per_side, -+ VCHIQ_MAX_SLOTS_PER_SIDE); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ if (is_master) { -+ local = &slot_zero->master; -+ remote = &slot_zero->slave; -+ } else { -+ local = &slot_zero->slave; -+ remote = &slot_zero->master; -+ } -+ -+ if (local->initialised) { -+ vchiq_loud_error_header(); -+ if (remote->initialised) -+ vchiq_loud_error("local state has already been " -+ "initialised"); -+ else -+ vchiq_loud_error("master/slave mismatch - two %ss", -+ is_master ? "master" : "slave"); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ -+ memset(state, 0, sizeof(VCHIQ_STATE_T)); -+ -+ state->id = id++; -+ state->is_master = is_master; -+ -+ /* -+ initialize shared state pointers -+ */ -+ -+ state->local = local; -+ state->remote = remote; -+ state->slot_data = (VCHIQ_SLOT_T *)slot_zero; -+ -+ /* -+ initialize events and mutexes -+ */ -+ -+ sema_init(&state->connect, 0); -+ mutex_init(&state->mutex); -+ sema_init(&state->trigger_event, 0); -+ sema_init(&state->recycle_event, 0); -+ sema_init(&state->sync_trigger_event, 0); -+ sema_init(&state->sync_release_event, 0); -+ -+ mutex_init(&state->slot_mutex); -+ mutex_init(&state->recycle_mutex); -+ mutex_init(&state->sync_mutex); -+ mutex_init(&state->bulk_transfer_mutex); -+ -+ sema_init(&state->slot_available_event, 0); -+ sema_init(&state->slot_remove_event, 0); -+ sema_init(&state->data_quota_event, 0); -+ -+ state->slot_queue_available = 0; -+ -+ for (i = 0; i < VCHIQ_MAX_SERVICES; i++) { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[i]; -+ sema_init(&service_quota->quota_event, 0); -+ } -+ -+ for (i = local->slot_first; i <= local->slot_last; i++) { -+ local->slot_queue[state->slot_queue_available++] = i; -+ up(&state->slot_available_event); -+ } -+ -+ state->default_slot_quota = state->slot_queue_available/2; -+ state->default_message_quota = -+ min((unsigned short)(state->default_slot_quota * 256), -+ (unsigned short)~0); -+ -+ state->previous_data_index = -1; -+ state->data_use_count = 0; -+ state->data_quota = state->slot_queue_available - 1; -+ -+ local->trigger.event = &state->trigger_event; -+ remote_event_create(&local->trigger); -+ local->tx_pos = 0; -+ -+ local->recycle.event = &state->recycle_event; -+ remote_event_create(&local->recycle); -+ local->slot_queue_recycle = state->slot_queue_available; -+ -+ local->sync_trigger.event = &state->sync_trigger_event; -+ remote_event_create(&local->sync_trigger); -+ -+ local->sync_release.event = &state->sync_release_event; -+ remote_event_create(&local->sync_release); -+ -+ /* At start-of-day, the slot is empty and available */ -+ ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid -+ = VCHIQ_MSGID_PADDING; -+ remote_event_signal_local(&local->sync_release); -+ -+ local->debug[DEBUG_ENTRIES] = DEBUG_MAX; -+ -+ status = vchiq_platform_init_state(state); -+ -+ /* -+ bring up slot handler thread -+ */ -+ snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); -+ state->slot_handler_thread = kthread_create(&slot_handler_func, -+ (void *)state, -+ threadname); -+ -+ if (state->slot_handler_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->slot_handler_thread, -19); -+ wake_up_process(state->slot_handler_thread); -+ -+ snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); -+ state->recycle_thread = kthread_create(&recycle_func, -+ (void *)state, -+ threadname); -+ if (state->recycle_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->recycle_thread, -19); -+ wake_up_process(state->recycle_thread); -+ -+ snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id); -+ state->sync_thread = kthread_create(&sync_func, -+ (void *)state, -+ threadname); -+ if (state->sync_thread == NULL) { -+ vchiq_loud_error_header(); -+ vchiq_loud_error("couldn't create thread %s", threadname); -+ vchiq_loud_error_footer(); -+ return VCHIQ_ERROR; -+ } -+ set_user_nice(state->sync_thread, -20); -+ wake_up_process(state->sync_thread); -+ -+ BUG_ON(state->id >= VCHIQ_MAX_STATES); -+ vchiq_states[state->id] = state; -+ -+ /* Indicate readiness to the other side */ -+ local->initialised = 1; -+ -+ return status; -+} -+ -+/* Called from application thread when a client or server service is created. */ -+VCHIQ_SERVICE_T * -+vchiq_add_service_internal(VCHIQ_STATE_T *state, -+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, -+ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term) -+{ -+ VCHIQ_SERVICE_T *service; -+ -+ service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL); -+ if (service) { -+ service->base.fourcc = params->fourcc; -+ service->base.callback = params->callback; -+ service->base.userdata = params->userdata; -+ service->handle = VCHIQ_SERVICE_HANDLE_INVALID; -+ service->ref_count = 1; -+ service->srvstate = VCHIQ_SRVSTATE_FREE; -+ service->userdata_term = userdata_term; -+ service->localport = VCHIQ_PORT_FREE; -+ service->remoteport = VCHIQ_PORT_FREE; -+ -+ service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? -+ VCHIQ_FOURCC_INVALID : params->fourcc; -+ service->client_id = 0; -+ service->auto_close = 1; -+ service->sync = 0; -+ service->closing = 0; -+ service->trace = 0; -+ atomic_set(&service->poll_flags, 0); -+ service->version = params->version; -+ service->version_min = params->version_min; -+ service->state = state; -+ service->instance = instance; -+ service->service_use_count = 0; -+ init_bulk_queue(&service->bulk_tx); -+ init_bulk_queue(&service->bulk_rx); -+ sema_init(&service->remove_event, 0); -+ sema_init(&service->bulk_remove_event, 0); -+ mutex_init(&service->bulk_mutex); -+ memset(&service->stats, 0, sizeof(service->stats)); -+ } else { -+ vchiq_log_error(vchiq_core_log_level, -+ "Out of memory"); -+ } -+ -+ if (service) { -+ VCHIQ_SERVICE_T **pservice = NULL; -+ int i; -+ -+ /* Although it is perfectly possible to use service_spinlock -+ ** to protect the creation of services, it is overkill as it -+ ** disables interrupts while the array is searched. -+ ** The only danger is of another thread trying to create a -+ ** service - service deletion is safe. -+ ** Therefore it is preferable to use state->mutex which, -+ ** although slower to claim, doesn't block interrupts while -+ ** it is held. -+ */ -+ -+ mutex_lock(&state->mutex); -+ -+ /* Prepare to use a previously unused service */ -+ if (state->unused_service < VCHIQ_MAX_SERVICES) -+ pservice = &state->services[state->unused_service]; -+ -+ if (srvstate == VCHIQ_SRVSTATE_OPENING) { -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *srv = state->services[i]; -+ if (!srv) { -+ pservice = &state->services[i]; -+ break; -+ } -+ } -+ } else { -+ for (i = (state->unused_service - 1); i >= 0; i--) { -+ VCHIQ_SERVICE_T *srv = state->services[i]; -+ if (!srv) -+ pservice = &state->services[i]; -+ else if ((srv->public_fourcc == params->fourcc) -+ && ((srv->instance != instance) || -+ (srv->base.callback != -+ params->callback))) { -+ /* There is another server using this -+ ** fourcc which doesn't match. */ -+ pservice = NULL; -+ break; -+ } -+ } -+ } -+ -+ if (pservice) { -+ service->localport = (pservice - state->services); -+ if (!handle_seq) -+ handle_seq = VCHIQ_MAX_STATES * -+ VCHIQ_MAX_SERVICES; -+ service->handle = handle_seq | -+ (state->id * VCHIQ_MAX_SERVICES) | -+ service->localport; -+ handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; -+ *pservice = service; -+ if (pservice == &state->services[state->unused_service]) -+ state->unused_service++; -+ } -+ -+ mutex_unlock(&state->mutex); -+ -+ if (!pservice) { -+ kfree(service); -+ service = NULL; -+ } -+ } -+ -+ if (service) { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &state->service_quotas[service->localport]; -+ service_quota->slot_quota = state->default_slot_quota; -+ service_quota->message_quota = state->default_message_quota; -+ if (service_quota->slot_use_count == 0) -+ service_quota->previous_tx_index = -+ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) -+ - 1; -+ -+ /* Bring this service online */ -+ vchiq_set_service_state(service, srvstate); -+ -+ vchiq_log_info(vchiq_core_msg_log_level, -+ "%s Service %c%c%c%c SrcPort:%d", -+ (srvstate == VCHIQ_SRVSTATE_OPENING) -+ ? "Open" : "Add", -+ VCHIQ_FOURCC_AS_4CHARS(params->fourcc), -+ service->localport); -+ } -+ -+ /* Don't unlock the service - leave it with a ref_count of 1. */ -+ -+ return service; -+} -+ -+VCHIQ_STATUS_T -+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) -+{ -+ struct vchiq_open_payload payload = { -+ service->base.fourcc, -+ client_id, -+ service->version, -+ service->version_min -+ }; -+ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) }; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ service->client_id = client_id; -+ vchiq_use_service_internal(service); -+ status = queue_message(service->state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), -+ &body, 1, sizeof(payload), 1); -+ if (status == VCHIQ_SUCCESS) { -+ /* Wait for the ACK/NAK */ -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ vchiq_release_service_internal(service); -+ } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && -+ (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { -+ if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: osi - srvstate = %s (ref %d)", -+ service->state->id, -+ srvstate_names[service->srvstate], -+ service->ref_count); -+ status = VCHIQ_ERROR; -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ vchiq_release_service_internal(service); -+ } -+ } -+ return status; -+} -+ -+static void -+release_service_messages(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ int slot_last = state->remote->slot_last; -+ int i; -+ -+ /* Release any claimed messages */ -+ for (i = state->remote->slot_first; i <= slot_last; i++) { -+ VCHIQ_SLOT_INFO_T *slot_info = -+ SLOT_INFO_FROM_INDEX(state, i); -+ if (slot_info->release_count != slot_info->use_count) { -+ char *data = -+ (char *)SLOT_DATA_FROM_INDEX(state, i); -+ unsigned int pos, end; -+ -+ end = VCHIQ_SLOT_SIZE; -+ if (data == state->rx_data) -+ /* This buffer is still being read from - stop -+ ** at the current read position */ -+ end = state->rx_pos & VCHIQ_SLOT_MASK; -+ + pos = 0; + -+ while (pos < end) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)(data + pos); -+ int msgid = header->msgid; -+ int port = VCHIQ_MSG_DSTPORT(msgid); -+ if ((port == service->localport) && -+ (msgid & VCHIQ_MSGID_CLAIMED)) { -+ vchiq_log_info(vchiq_core_log_level, -+ " fsi - hdr %x", -+ (unsigned int)header); -+ release_slot(state, slot_info, header, -+ NULL); -+ } -+ pos += calc_stride(header->size); -+ if (pos > VCHIQ_SLOT_SIZE) { -+ vchiq_log_error(vchiq_core_log_level, -+ "fsi - pos %x: header %x, " -+ "msgid %x, header->msgid %x, " -+ "header->size %x", -+ pos, (unsigned int)header, -+ msgid, header->msgid, -+ header->size); -+ WARN(1, "invalid slot position\n"); -+ } -+ } -+ } -+ } -+} -+ -+static int -+do_abort_bulks(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATUS_T status; -+ -+ /* Abort any outstanding bulk transfers */ -+ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) -+ return 0; -+ abort_outstanding_bulks(service, &service->bulk_tx); -+ abort_outstanding_bulks(service, &service->bulk_rx); -+ mutex_unlock(&service->bulk_mutex); -+ -+ status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/); -+ if (status == VCHIQ_SUCCESS) -+ status = notify_bulks(service, &service->bulk_rx, -+ 0/*!retry_poll*/); -+ return (status == VCHIQ_SUCCESS); -+} -+ -+static VCHIQ_STATUS_T -+close_service_complete(VCHIQ_SERVICE_T *service, int failstate) -+{ -+ VCHIQ_STATUS_T status; -+ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); -+ int newstate; -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPEN: -+ case VCHIQ_SRVSTATE_CLOSESENT: -+ case VCHIQ_SRVSTATE_CLOSERECVD: -+ if (is_server) { -+ if (service->auto_close) { -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ newstate = VCHIQ_SRVSTATE_LISTENING; -+ } else -+ newstate = VCHIQ_SRVSTATE_CLOSEWAIT; -+ } else -+ newstate = VCHIQ_SRVSTATE_CLOSED; -+ vchiq_set_service_state(service, newstate); -+ break; -+ case VCHIQ_SRVSTATE_LISTENING: -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "close_service_complete(%x) called in state %s", -+ service->handle, srvstate_names[service->srvstate]); -+ WARN(1, "close_service_complete in unexpected state\n"); -+ return VCHIQ_ERROR; -+ } -+ -+ status = make_service_callback(service, -+ VCHIQ_SERVICE_CLOSED, NULL, NULL); -+ -+ if (status != VCHIQ_RETRY) { -+ int uc = service->service_use_count; -+ int i; -+ /* Complete the close process */ -+ for (i = 0; i < uc; i++) -+ /* cater for cases where close is forced and the -+ ** client may not close all it's handles */ -+ vchiq_release_service_internal(service); -+ -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_CLOSED) -+ vchiq_free_service_internal(service); -+ else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) { -+ if (is_server) -+ service->closing = 0; -+ -+ up(&service->remove_event); -+ } -+ } else -+ vchiq_set_service_state(service, failstate); -+ -+ return status; -+} -+ -+/* Called by the slot handler */ -+VCHIQ_STATUS_T -+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)", -+ service->state->id, service->localport, close_recvd, -+ srvstate_names[service->srvstate]); -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_CLOSED: -+ case VCHIQ_SRVSTATE_HIDDEN: -+ case VCHIQ_SRVSTATE_LISTENING: -+ case VCHIQ_SRVSTATE_CLOSEWAIT: -+ if (close_recvd) -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_close_service_internal(1) called " -+ "in state %s", -+ srvstate_names[service->srvstate]); -+ else if (is_server) { -+ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { -+ status = VCHIQ_ERROR; -+ } else { -+ service->client_id = 0; -+ service->remoteport = VCHIQ_PORT_FREE; -+ if (service->srvstate == -+ VCHIQ_SRVSTATE_CLOSEWAIT) -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ } -+ up(&service->remove_event); -+ } else -+ vchiq_free_service_internal(service); -+ break; -+ case VCHIQ_SRVSTATE_OPENING: -+ if (close_recvd) { -+ /* The open was rejected - tell the user */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_CLOSEWAIT); -+ up(&service->remove_event); -+ } else { -+ /* Shutdown mid-open - let the other side know */ -+ status = queue_message(state, service, -+ VCHIQ_MAKE_MSG -+ (VCHIQ_MSG_CLOSE, -+ service->localport, -+ VCHIQ_MSG_DSTPORT(service->remoteport)), -+ NULL, 0, 0, 0); -+ } -+ break; -+ -+ case VCHIQ_SRVSTATE_OPENSYNC: -+ mutex_lock(&state->sync_mutex); -+ /* Drop through */ -+ -+ case VCHIQ_SRVSTATE_OPEN: -+ if (state->is_master || close_recvd) { -+ if (!do_abort_bulks(service)) -+ status = VCHIQ_RETRY; -+ } -+ -+ release_service_messages(service); -+ -+ if (status == VCHIQ_SUCCESS) -+ status = queue_message(state, service, -+ VCHIQ_MAKE_MSG -+ (VCHIQ_MSG_CLOSE, -+ service->localport, -+ VCHIQ_MSG_DSTPORT(service->remoteport)), -+ NULL, 0, 0, 0); -+ -+ if (status == VCHIQ_SUCCESS) { -+ if (!close_recvd) -+ break; -+ } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { -+ mutex_unlock(&state->sync_mutex); -+ break; -+ } else -+ break; -+ -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ case VCHIQ_SRVSTATE_CLOSESENT: -+ if (!close_recvd) -+ /* This happens when a process is killed mid-close */ -+ break; -+ -+ if (!state->is_master) { -+ if (!do_abort_bulks(service)) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ } -+ -+ if (status == VCHIQ_SUCCESS) -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ case VCHIQ_SRVSTATE_CLOSERECVD: -+ if (!close_recvd && is_server) -+ /* Force into LISTENING mode */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ status = close_service_complete(service, -+ VCHIQ_SRVSTATE_CLOSERECVD); -+ break; -+ -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_close_service_internal(%d) called in state %s", -+ close_recvd, srvstate_names[service->srvstate]); -+ break; -+ } -+ -+ return status; -+} -+ -+/* Called from the application process upon process death */ -+void -+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)", -+ state->id, service->localport, service->remoteport); -+ -+ mark_service_closing(service); -+ -+ /* Mark the service for removal by the slot handler */ -+ request_poll(state, service, VCHIQ_POLL_REMOVE); -+} -+ -+/* Called from the slot handler */ -+void -+vchiq_free_service_internal(VCHIQ_SERVICE_T *service) -+{ -+ VCHIQ_STATE_T *state = service->state; -+ -+ vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)", -+ state->id, service->localport); -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPENING: -+ case VCHIQ_SRVSTATE_CLOSED: -+ case VCHIQ_SRVSTATE_HIDDEN: -+ case VCHIQ_SRVSTATE_LISTENING: -+ case VCHIQ_SRVSTATE_CLOSEWAIT: -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "%d: fsi - (%d) in state %s", -+ state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ return; -+ } -+ -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE); -+ -+ up(&service->remove_event); -+ -+ /* Release the initial lock */ -+ unlock_service(service); -+} -+ -+VCHIQ_STATUS_T -+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ /* Find all services registered to this client and enable them. */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN) -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_LISTENING); -+ unlock_service(service); -+ } -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { -+ if (queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, -+ 0, 1) == VCHIQ_RETRY) -+ return VCHIQ_RETRY; -+ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); -+ } -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) { -+ if (down_interruptible(&state->connect) != 0) -+ return VCHIQ_RETRY; -+ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); -+ up(&state->connect); -+ } -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_SERVICE_T *service; -+ int i; -+ -+ /* Find all services registered to this client and enable them. */ -+ i = 0; -+ while ((service = next_service_by_instance(state, instance, -+ &i)) != NULL) { -+ (void)vchiq_remove_service(service->handle); -+ unlock_service(service); -+ } -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_pause_internal(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ switch (state->conn_state) { -+ case VCHIQ_CONNSTATE_CONNECTED: -+ /* Request a pause */ -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING); -+ request_poll(state, NULL, 0); -+ break; -+ default: -+ vchiq_log_error(vchiq_core_log_level, -+ "vchiq_pause_internal in state %s\n", -+ conn_state_names[state->conn_state]); -+ status = VCHIQ_ERROR; -+ VCHIQ_STATS_INC(state, error_count); -+ break; -+ } -+ -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_resume_internal(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { -+ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING); -+ request_poll(state, NULL, 0); ++ txstate->residue = bcm2835_dma_desc_size_pos(d, pos); + } else { -+ status = VCHIQ_ERROR; -+ VCHIQ_STATS_INC(state, error_count); ++ txstate->residue = 0; + } + -+ return status; ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return ret; +} + -+VCHIQ_STATUS_T -+vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++static void bcm2835_dma_issue_pending(struct dma_chan *chan) +{ -+ /* Unregister the service */ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ unsigned long flags; + -+ if (!service) -+ return VCHIQ_ERROR; ++ spin_lock_irqsave(&c->vc.lock, flags); ++ if (vchan_issue_pending(&c->vc) && !c->desc) ++ bcm2835_dma_start_desc(c); + -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: close_service:%d", -+ service->state->id, service->localport); ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++} + -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) { -+ unlock_service(service); -+ return VCHIQ_ERROR; ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( ++ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, ++ size_t period_len, enum dma_transfer_direction direction, ++ unsigned long flags) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2835_desc *d; ++ dma_addr_t dev_addr; ++ unsigned int es, sync_type; ++ unsigned int frame, max_size; ++ ++ /* Grab configuration */ ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; + } + -+ mark_service_closing(service); -+ -+ if (current == service->state->slot_handler_thread) { -+ status = vchiq_close_service_internal(service, -+ 0/*!close_recvd*/); -+ BUG_ON(status == VCHIQ_RETRY); ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2835_DMA_S_DREQ; + } else { -+ /* Mark the service for termination by the slot handler */ -+ request_poll(service->state, service, VCHIQ_POLL_TERMINATE); ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2835_DMA_D_DREQ; + } + -+ while (1) { -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || -+ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) -+ break; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: close_service:%d - waiting in state %s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && -+ (service->srvstate != VCHIQ_SRVSTATE_FREE) && -+ (service->srvstate != VCHIQ_SRVSTATE_LISTENING)) -+ status = VCHIQ_ERROR; -+ -+ unlock_service(service); -+ -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ /* Unregister the service */ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; -+ -+ if (!service) -+ return VCHIQ_ERROR; -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: remove_service:%d", -+ service->state->id, service->localport); -+ -+ if (service->srvstate == VCHIQ_SRVSTATE_FREE) { -+ unlock_service(service); -+ return VCHIQ_ERROR; -+ } -+ -+ mark_service_closing(service); -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || -+ (current == service->state->slot_handler_thread)) { -+ /* Make it look like a client, because it must be removed and -+ not left in the LISTENING state. */ -+ service->public_fourcc = VCHIQ_FOURCC_INVALID; -+ -+ status = vchiq_close_service_internal(service, -+ 0/*!close_recvd*/); -+ BUG_ON(status == VCHIQ_RETRY); -+ } else { -+ /* Mark the service for removal by the slot handler */ -+ request_poll(service->state, service, VCHIQ_POLL_REMOVE); -+ } -+ while (1) { -+ if (down_interruptible(&service->remove_event) != 0) { -+ status = VCHIQ_RETRY; -+ break; -+ } -+ -+ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || -+ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) -+ break; -+ -+ vchiq_log_warning(vchiq_core_log_level, -+ "%d: remove_service:%d - waiting in state %s", -+ service->state->id, service->localport, -+ srvstate_names[service->srvstate]); -+ } -+ -+ if ((status == VCHIQ_SUCCESS) && -+ (service->srvstate != VCHIQ_SRVSTATE_FREE)) -+ status = VCHIQ_ERROR; -+ -+ unlock_service(service); -+ -+ return status; -+} -+ -+ -+/* This function may be called by kernel threads or user threads. -+ * User threads may receive VCHIQ_RETRY to indicate that a signal has been -+ * received and the call should be retried after being returned to user -+ * context. -+ * When called in blocking mode, the userdata field points to a bulk_waiter -+ * structure. -+ */ -+VCHIQ_STATUS_T -+vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_BULK_QUEUE_T *queue; -+ VCHIQ_BULK_T *bulk; -+ VCHIQ_STATE_T *state; -+ struct bulk_waiter *bulk_waiter = NULL; -+ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; -+ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ if (!service || -+ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || -+ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS)) -+ goto error_exit; -+ -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ bulk_waiter = (struct bulk_waiter *)userdata; -+ sema_init(&bulk_waiter->event, 0); -+ bulk_waiter->actual = 0; -+ bulk_waiter->bulk = NULL; -+ break; -+ case VCHIQ_BULK_MODE_WAITING: -+ bulk_waiter = (struct bulk_waiter *)userdata; -+ bulk = bulk_waiter->bulk; -+ goto waiting; -+ default: -+ goto error_exit; -+ } -+ -+ state = service->state; -+ -+ queue = (dir == VCHIQ_BULK_TRANSMIT) ? -+ &service->bulk_tx : &service->bulk_rx; -+ -+ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ -+ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) { -+ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); -+ do { -+ mutex_unlock(&service->bulk_mutex); -+ if (down_interruptible(&service->bulk_remove_event) -+ != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ if (mutex_lock_interruptible(&service->bulk_mutex) -+ != 0) { -+ status = VCHIQ_RETRY; -+ goto error_exit; -+ } -+ } while (queue->local_insert == queue->remove + -+ VCHIQ_NUM_SERVICE_BULKS); -+ } -+ -+ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; -+ -+ bulk->mode = mode; -+ bulk->dir = dir; -+ bulk->userdata = userdata; -+ bulk->size = size; -+ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; -+ -+ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != -+ VCHIQ_SUCCESS) -+ goto unlock_error_exit; -+ -+ wmb(); -+ -+ vchiq_log_info(vchiq_core_log_level, -+ "%d: bt (%d->%d) %cx %x@%x %x", -+ state->id, -+ service->localport, service->remoteport, dir_char, -+ size, (unsigned int)bulk->data, (unsigned int)userdata); -+ -+ if (state->is_master) { -+ queue->local_insert++; -+ if (resolve_bulks(service, queue)) -+ request_poll(state, service, -+ (dir == VCHIQ_BULK_TRANSMIT) ? -+ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); -+ } else { -+ int payload[2] = { (int)bulk->data, bulk->size }; -+ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) }; -+ -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(dir_msgtype, -+ service->localport, service->remoteport), -+ &element, 1, sizeof(payload), 1); -+ if (status != VCHIQ_SUCCESS) { -+ vchiq_complete_bulk(bulk); -+ goto unlock_error_exit; -+ } -+ queue->local_insert++; -+ } -+ -+ mutex_unlock(&service->bulk_mutex); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%d: bt:%d %cx li=%x ri=%x p=%x", -+ state->id, -+ service->localport, dir_char, -+ queue->local_insert, queue->remote_insert, queue->process); -+ -+waiting: -+ unlock_service(service); -+ -+ status = VCHIQ_SUCCESS; -+ -+ if (bulk_waiter) { -+ bulk_waiter->bulk = bulk; -+ if (down_interruptible(&bulk_waiter->event) != 0) -+ status = VCHIQ_RETRY; -+ else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) -+ status = VCHIQ_ERROR; -+ } -+ -+ return status; -+ -+unlock_error_exit: -+ mutex_unlock(&service->bulk_mutex); -+ -+error_exit: -+ if (service) -+ unlock_service(service); -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, -+ const VCHIQ_ELEMENT_T *elements, unsigned int count) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ unsigned int size = 0; -+ unsigned int i; -+ -+ if (!service || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS)) -+ goto error_exit; -+ -+ for (i = 0; i < (unsigned int)count; i++) { -+ if (elements[i].size) { -+ if (elements[i].data == NULL) { -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ goto error_exit; -+ } -+ size += elements[i].size; -+ } -+ } -+ -+ if (size > VCHIQ_MAX_MSG_SIZE) { -+ VCHIQ_SERVICE_STATS_INC(service, error_count); -+ goto error_exit; -+ } -+ -+ switch (service->srvstate) { -+ case VCHIQ_SRVSTATE_OPEN: -+ status = queue_message(service->state, service, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, -+ service->localport, -+ service->remoteport), -+ elements, count, size, 1); -+ break; -+ case VCHIQ_SRVSTATE_OPENSYNC: -+ status = queue_message_sync(service->state, service, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, -+ service->localport, -+ service->remoteport), -+ elements, count, size, 1); ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2835_DMA_DATA_TYPE_S32; + break; + default: -+ status = VCHIQ_ERROR; -+ break; ++ return NULL; + } + -+error_exit: -+ if (service) -+ unlock_service(service); -+ -+ return status; -+} -+ -+void -+vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_STATE_T *state; -+ int slot_index; -+ -+ if (!service) -+ return; -+ -+ state = service->state; -+ remote = state->remote; -+ -+ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header); -+ -+ if ((slot_index >= remote->slot_first) && -+ (slot_index <= remote->slot_last)) { -+ int msgid = header->msgid; -+ if (msgid & VCHIQ_MSGID_CLAIMED) { -+ VCHIQ_SLOT_INFO_T *slot_info = -+ SLOT_INFO_FROM_INDEX(state, slot_index); -+ -+ release_slot(state, slot_info, header, service); -+ } -+ } else if (slot_index == remote->slot_sync) -+ release_message_sync(state, header); -+ -+ unlock_service(service); -+} -+ -+static void -+release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) -+{ -+ header->msgid = VCHIQ_MSGID_PADDING; -+ wmb(); -+ remote_event_signal(&state->remote->sync_release); -+} -+ -+VCHIQ_STATUS_T -+vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ -+ if (!service || -+ (vchiq_check_service(service) != VCHIQ_SUCCESS) || -+ !peer_version) -+ goto exit; -+ *peer_version = service->peer_version; -+ status = VCHIQ_SUCCESS; -+ -+exit: -+ if (service) -+ unlock_service(service); -+ return status; -+} -+ -+VCHIQ_STATUS_T -+vchiq_get_config(VCHIQ_INSTANCE_T instance, -+ int config_size, VCHIQ_CONFIG_T *pconfig) -+{ -+ VCHIQ_CONFIG_T config; -+ -+ (void)instance; -+ -+ config.max_msg_size = VCHIQ_MAX_MSG_SIZE; -+ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE; -+ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS; -+ config.max_services = VCHIQ_MAX_SERVICES; -+ config.version = VCHIQ_VERSION; -+ config.version_min = VCHIQ_VERSION_MIN; -+ -+ if (config_size > sizeof(VCHIQ_CONFIG_T)) -+ return VCHIQ_ERROR; -+ -+ memcpy(pconfig, &config, -+ min(config_size, (int)(sizeof(VCHIQ_CONFIG_T)))); -+ -+ return VCHIQ_SUCCESS; -+} -+ -+VCHIQ_STATUS_T -+vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHIQ_SERVICE_OPTION_T option, int value) -+{ -+ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ -+ if (service) { -+ switch (option) { -+ case VCHIQ_SERVICE_OPTION_AUTOCLOSE: -+ service->auto_close = value; -+ status = VCHIQ_SUCCESS; -+ break; -+ -+ case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[ -+ service->localport]; -+ if (value == 0) -+ value = service->state->default_slot_quota; -+ if ((value >= service_quota->slot_use_count) && -+ (value < (unsigned short)~0)) { -+ service_quota->slot_quota = value; -+ if ((value >= service_quota->slot_use_count) && -+ (service_quota->message_quota >= -+ service_quota->message_use_count)) { -+ /* Signal the service that it may have -+ ** dropped below its quota */ -+ up(&service_quota->quota_event); -+ } -+ status = VCHIQ_SUCCESS; -+ } -+ } break; -+ -+ case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: { -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[ -+ service->localport]; -+ if (value == 0) -+ value = service->state->default_message_quota; -+ if ((value >= service_quota->message_use_count) && -+ (value < (unsigned short)~0)) { -+ service_quota->message_quota = value; -+ if ((value >= -+ service_quota->message_use_count) && -+ (service_quota->slot_quota >= -+ service_quota->slot_use_count)) -+ /* Signal the service that it may have -+ ** dropped below its quota */ -+ up(&service_quota->quota_event); -+ status = VCHIQ_SUCCESS; -+ } -+ } break; -+ -+ case VCHIQ_SERVICE_OPTION_SYNCHRONOUS: -+ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || -+ (service->srvstate == -+ VCHIQ_SRVSTATE_LISTENING)) { -+ service->sync = value; -+ status = VCHIQ_SUCCESS; -+ } -+ break; -+ -+ case VCHIQ_SERVICE_OPTION_TRACE: -+ service->trace = value; -+ status = VCHIQ_SUCCESS; -+ break; -+ -+ default: -+ break; -+ } -+ unlock_service(service); -+ } -+ -+ return status; -+} -+ -+void -+vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, -+ VCHIQ_SHARED_STATE_T *shared, const char *label) -+{ -+ static const char *const debug_names[] = { -+ "", -+ "SLOT_HANDLER_COUNT", -+ "SLOT_HANDLER_LINE", -+ "PARSE_LINE", -+ "PARSE_HEADER", -+ "PARSE_MSGID", -+ "AWAIT_COMPLETION_LINE", -+ "DEQUEUE_MESSAGE_LINE", -+ "SERVICE_CALLBACK_LINE", -+ "MSG_QUEUE_FULL_COUNT", -+ "COMPLETION_QUEUE_FULL_COUNT" -+ }; -+ int i; -+ -+ char buf[80]; -+ int len; -+ len = snprintf(buf, sizeof(buf), -+ " %s: slots %d-%d tx_pos=%x recycle=%x", -+ label, shared->slot_first, shared->slot_last, -+ shared->tx_pos, shared->slot_queue_recycle); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Slots claimed:"); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ for (i = shared->slot_first; i <= shared->slot_last; i++) { -+ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i); -+ if (slot_info.use_count != slot_info.release_count) { -+ len = snprintf(buf, sizeof(buf), -+ " %d: %d/%d", i, slot_info.use_count, -+ slot_info.release_count); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+ } -+ -+ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) { -+ len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", -+ debug_names[i], shared->debug[i], shared->debug[i]); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+} -+ -+void -+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state) -+{ -+ char buf[80]; -+ int len; -+ int i; -+ -+ len = snprintf(buf, sizeof(buf), "State %d: %s", state->id, -+ conn_state_names[state->conn_state]); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " tx_pos=%x(@%x), rx_pos=%x(@%x)", -+ state->local->tx_pos, -+ (uint32_t)state->tx_data + -+ (state->local_tx_pos & VCHIQ_SLOT_MASK), -+ state->rx_pos, -+ (uint32_t)state->rx_data + -+ (state->rx_pos & VCHIQ_SLOT_MASK)); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Version: %d (min %d)", -+ VCHIQ_VERSION, VCHIQ_VERSION_MIN); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ if (VCHIQ_ENABLE_STATS) { -+ len = snprintf(buf, sizeof(buf), -+ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, " -+ "error_count=%d", -+ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, -+ state->stats.error_count); -+ vchiq_dump(dump_context, buf, len + 1); -+ } -+ -+ len = snprintf(buf, sizeof(buf), -+ " Slots: %d available (%d data), %d recyclable, %d stalls " -+ "(%d data)", -+ ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - -+ state->local_tx_pos) / VCHIQ_SLOT_SIZE, -+ state->data_quota - state->data_use_count, -+ state->local->slot_queue_recycle - state->slot_queue_available, -+ state->stats.slot_stalls, state->stats.data_stalls); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ vchiq_dump_platform_state(dump_context); -+ -+ vchiq_dump_shared_state(dump_context, state, state->local, "Local"); -+ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote"); -+ -+ vchiq_dump_platform_instances(dump_context); -+ -+ for (i = 0; i < state->unused_service; i++) { -+ VCHIQ_SERVICE_T *service = find_service_by_port(state, i); -+ -+ if (service) { -+ vchiq_dump_service_state(dump_context, service); -+ unlock_service(service); -+ } -+ } -+} -+ -+void -+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) -+{ -+ char buf[80]; -+ int len; -+ -+ len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)", -+ service->localport, srvstate_names[service->srvstate], -+ service->ref_count - 1); /*Don't include the lock just taken*/ -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_FREE) { -+ char remoteport[30]; -+ VCHIQ_SERVICE_QUOTA_T *service_quota = -+ &service->state->service_quotas[service->localport]; -+ int fourcc = service->base.fourcc; -+ int tx_pending, rx_pending; -+ if (service->remoteport != VCHIQ_PORT_FREE) { -+ int len2 = snprintf(remoteport, sizeof(remoteport), -+ "%d", service->remoteport); -+ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) -+ snprintf(remoteport + len2, -+ sizeof(remoteport) - len2, -+ " (client %x)", service->client_id); -+ } else -+ strcpy(remoteport, "n/a"); -+ -+ len += snprintf(buf + len, sizeof(buf) - len, -+ " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)", -+ VCHIQ_FOURCC_AS_4CHARS(fourcc), -+ remoteport, -+ service_quota->message_use_count, -+ service_quota->message_quota, -+ service_quota->slot_use_count, -+ service_quota->slot_quota); -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ tx_pending = service->bulk_tx.local_insert - -+ service->bulk_tx.remote_insert; -+ -+ rx_pending = service->bulk_rx.local_insert - -+ service->bulk_rx.remote_insert; -+ -+ len = snprintf(buf, sizeof(buf), -+ " Bulk: tx_pending=%d (size %d)," -+ " rx_pending=%d (size %d)", -+ tx_pending, -+ tx_pending ? service->bulk_tx.bulks[ -+ BULK_INDEX(service->bulk_tx.remove)].size : 0, -+ rx_pending, -+ rx_pending ? service->bulk_rx.bulks[ -+ BULK_INDEX(service->bulk_rx.remove)].size : 0); -+ -+ if (VCHIQ_ENABLE_STATS) { -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Ctrl: tx_count=%d, tx_bytes=%llu, " -+ "rx_count=%d, rx_bytes=%llu", -+ service->stats.ctrl_tx_count, -+ service->stats.ctrl_tx_bytes, -+ service->stats.ctrl_rx_count, -+ service->stats.ctrl_rx_bytes); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " Bulk: tx_count=%d, tx_bytes=%llu, " -+ "rx_count=%d, rx_bytes=%llu", -+ service->stats.bulk_tx_count, -+ service->stats.bulk_tx_bytes, -+ service->stats.bulk_rx_count, -+ service->stats.bulk_rx_bytes); -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ len = snprintf(buf, sizeof(buf), -+ " %d quota stalls, %d slot stalls, " -+ "%d bulk stalls, %d aborted, %d errors", -+ service->stats.quota_stalls, -+ service->stats.slot_stalls, -+ service->stats.bulk_stalls, -+ service->stats.bulk_aborted_count, -+ service->stats.error_count); -+ } -+ } -+ -+ vchiq_dump(dump_context, buf, len + 1); -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_FREE) -+ vchiq_dump_platform_service_state(dump_context, service); -+} -+ -+ -+void -+vchiq_loud_error_header(void) -+{ -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, "====="); -+} -+ -+void -+vchiq_loud_error_footer(void) -+{ -+ vchiq_log_error(vchiq_core_log_level, "====="); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+ vchiq_log_error(vchiq_core_log_level, -+ "============================================================" -+ "================"); -+} -+ -+ -+VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_RETRY; -+ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) -+ status = queue_message(state, NULL, -+ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), -+ NULL, 0, 0, 0); -+ return status; -+} -+ -+void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, -+ size_t numBytes) -+{ -+ const uint8_t *mem = (const uint8_t *)voidMem; -+ size_t offset; -+ char lineBuf[100]; -+ char *s; -+ -+ while (numBytes > 0) { -+ s = lineBuf; -+ -+ for (offset = 0; offset < 16; offset++) { -+ if (offset < numBytes) -+ s += snprintf(s, 4, "%02x ", mem[offset]); -+ else -+ s += snprintf(s, 4, " "); -+ } -+ -+ for (offset = 0; offset < 16; offset++) { -+ if (offset < numBytes) { -+ uint8_t ch = mem[offset]; -+ -+ if ((ch < ' ') || (ch > '~')) -+ ch = '.'; -+ *s++ = (char)ch; -+ } -+ } -+ *s++ = '\0'; -+ -+ if ((label != NULL) && (*label != '\0')) -+ vchiq_log_trace(VCHIQ_LOG_TRACE, -+ "%s: %08x: %s", label, addr, lineBuf); -+ else -+ vchiq_log_trace(VCHIQ_LOG_TRACE, -+ "%08x: %s", addr, lineBuf); -+ -+ addr += 16; -+ mem += 16; -+ if (numBytes > 16) -+ numBytes -= 16; -+ else -+ numBytes = 0; -+ } -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -new file mode 100644 -index 0000000..1b27917 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -@@ -0,0 +1,711 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_CORE_H -+#define VCHIQ_CORE_H -+ -+#include -+#include -+#include -+ -+#include "vchiq_cfg.h" -+ -+#include "vchiq.h" -+ -+/* Run time control of log level, based on KERN_XXX level. */ -+#define VCHIQ_LOG_DEFAULT 4 -+#define VCHIQ_LOG_ERROR 3 -+#define VCHIQ_LOG_WARNING 4 -+#define VCHIQ_LOG_INFO 6 -+#define VCHIQ_LOG_TRACE 7 -+ -+#define VCHIQ_LOG_PREFIX KERN_INFO "vchiq: " -+ -+#ifndef vchiq_log_error -+#define vchiq_log_error(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_ERROR) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_warning -+#define vchiq_log_warning(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_WARNING) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_info -+#define vchiq_log_info(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_INFO) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+#ifndef vchiq_log_trace -+#define vchiq_log_trace(cat, fmt, ...) \ -+ do { if (cat >= VCHIQ_LOG_TRACE) \ -+ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) -+#endif -+ -+#define vchiq_loud_error(...) \ -+ vchiq_log_error(vchiq_core_log_level, "===== " __VA_ARGS__) -+ -+#ifndef vchiq_static_assert -+#define vchiq_static_assert(cond) __attribute__((unused)) \ -+ extern int vchiq_static_assert[(cond) ? 1 : -1] -+#endif -+ -+#define IS_POW2(x) (x && ((x & (x - 1)) == 0)) -+ -+/* Ensure that the slot size and maximum number of slots are powers of 2 */ -+vchiq_static_assert(IS_POW2(VCHIQ_SLOT_SIZE)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS)); -+vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); -+ -+#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) -+#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) -+#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \ -+ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) -+ -+#define VCHIQ_MSG_PADDING 0 /* - */ -+#define VCHIQ_MSG_CONNECT 1 /* - */ -+#define VCHIQ_MSG_OPEN 2 /* + (srcport, -), fourcc, client_id */ -+#define VCHIQ_MSG_OPENACK 3 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_CLOSE 4 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_DATA 5 /* + (srcport, dstport) */ -+#define VCHIQ_MSG_BULK_RX 6 /* + (srcport, dstport), data, size */ -+#define VCHIQ_MSG_BULK_TX 7 /* + (srcport, dstport), data, size */ -+#define VCHIQ_MSG_BULK_RX_DONE 8 /* + (srcport, dstport), actual */ -+#define VCHIQ_MSG_BULK_TX_DONE 9 /* + (srcport, dstport), actual */ -+#define VCHIQ_MSG_PAUSE 10 /* - */ -+#define VCHIQ_MSG_RESUME 11 /* - */ -+#define VCHIQ_MSG_REMOTE_USE 12 /* - */ -+#define VCHIQ_MSG_REMOTE_RELEASE 13 /* - */ -+#define VCHIQ_MSG_REMOTE_USE_ACTIVE 14 /* - */ -+ -+#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) -+#define VCHIQ_PORT_FREE 0x1000 -+#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE) -+#define VCHIQ_MAKE_MSG(type, srcport, dstport) \ -+ ((type<<24) | (srcport<<12) | (dstport<<0)) -+#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24) -+#define VCHIQ_MSG_SRCPORT(msgid) \ -+ (unsigned short)(((unsigned int)msgid >> 12) & 0xfff) -+#define VCHIQ_MSG_DSTPORT(msgid) \ -+ ((unsigned short)msgid & 0xfff) -+ -+#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \ -+ ((fourcc) >> 24) & 0xff, \ -+ ((fourcc) >> 16) & 0xff, \ -+ ((fourcc) >> 8) & 0xff, \ -+ (fourcc) & 0xff -+ -+/* Ensure the fields are wide enough */ -+vchiq_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) -+ == 0); -+vchiq_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0, VCHIQ_PORT_MAX, 0)) == 0); -+vchiq_static_assert((unsigned int)VCHIQ_PORT_MAX < -+ (unsigned int)VCHIQ_PORT_FREE); -+ -+#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING, 0, 0) -+#define VCHIQ_MSGID_CLAIMED 0x40000000 -+ -+#define VCHIQ_FOURCC_INVALID 0x00000000 -+#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID) -+ -+#define VCHIQ_BULK_ACTUAL_ABORTED -1 -+ -+typedef uint32_t BITSET_T; -+ -+vchiq_static_assert((sizeof(BITSET_T) * 8) == 32); -+ -+#define BITSET_SIZE(b) ((b + 31) >> 5) -+#define BITSET_WORD(b) (b >> 5) -+#define BITSET_BIT(b) (1 << (b & 31)) -+#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs)) -+#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b)) -+#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b)) -+#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b)) -+ -+#if VCHIQ_ENABLE_STATS -+#define VCHIQ_STATS_INC(state, stat) (state->stats. stat++) -+#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat++) -+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) \ -+ (service->stats. stat += addend) -+#else -+#define VCHIQ_STATS_INC(state, stat) ((void)0) -+#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0) -+#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0) -+#endif -+ -+enum { -+ DEBUG_ENTRIES, -+#if VCHIQ_ENABLE_DEBUG -+ DEBUG_SLOT_HANDLER_COUNT, -+ DEBUG_SLOT_HANDLER_LINE, -+ DEBUG_PARSE_LINE, -+ DEBUG_PARSE_HEADER, -+ DEBUG_PARSE_MSGID, -+ DEBUG_AWAIT_COMPLETION_LINE, -+ DEBUG_DEQUEUE_MESSAGE_LINE, -+ DEBUG_SERVICE_CALLBACK_LINE, -+ DEBUG_MSG_QUEUE_FULL_COUNT, -+ DEBUG_COMPLETION_QUEUE_FULL_COUNT, -+#endif -+ DEBUG_MAX -+}; -+ -+#if VCHIQ_ENABLE_DEBUG -+ -+#define DEBUG_INITIALISE(local) int *debug_ptr = (local)->debug; -+#define DEBUG_TRACE(d) \ -+ do { debug_ptr[DEBUG_ ## d] = __LINE__; dsb(); } while (0) -+#define DEBUG_VALUE(d, v) \ -+ do { debug_ptr[DEBUG_ ## d] = (v); dsb(); } while (0) -+#define DEBUG_COUNT(d) \ -+ do { debug_ptr[DEBUG_ ## d]++; dsb(); } while (0) -+ -+#else /* VCHIQ_ENABLE_DEBUG */ -+ -+#define DEBUG_INITIALISE(local) -+#define DEBUG_TRACE(d) -+#define DEBUG_VALUE(d, v) -+#define DEBUG_COUNT(d) -+ -+#endif /* VCHIQ_ENABLE_DEBUG */ -+ -+typedef enum { -+ VCHIQ_CONNSTATE_DISCONNECTED, -+ VCHIQ_CONNSTATE_CONNECTING, -+ VCHIQ_CONNSTATE_CONNECTED, -+ VCHIQ_CONNSTATE_PAUSING, -+ VCHIQ_CONNSTATE_PAUSE_SENT, -+ VCHIQ_CONNSTATE_PAUSED, -+ VCHIQ_CONNSTATE_RESUMING, -+ VCHIQ_CONNSTATE_PAUSE_TIMEOUT, -+ VCHIQ_CONNSTATE_RESUME_TIMEOUT -+} VCHIQ_CONNSTATE_T; -+ -+enum { -+ VCHIQ_SRVSTATE_FREE, -+ VCHIQ_SRVSTATE_HIDDEN, -+ VCHIQ_SRVSTATE_LISTENING, -+ VCHIQ_SRVSTATE_OPENING, -+ VCHIQ_SRVSTATE_OPEN, -+ VCHIQ_SRVSTATE_OPENSYNC, -+ VCHIQ_SRVSTATE_CLOSESENT, -+ VCHIQ_SRVSTATE_CLOSERECVD, -+ VCHIQ_SRVSTATE_CLOSEWAIT, -+ VCHIQ_SRVSTATE_CLOSED -+}; -+ -+enum { -+ VCHIQ_POLL_TERMINATE, -+ VCHIQ_POLL_REMOVE, -+ VCHIQ_POLL_TXNOTIFY, -+ VCHIQ_POLL_RXNOTIFY, -+ VCHIQ_POLL_COUNT -+}; -+ -+typedef enum { -+ VCHIQ_BULK_TRANSMIT, -+ VCHIQ_BULK_RECEIVE -+} VCHIQ_BULK_DIR_T; -+ -+typedef void (*VCHIQ_USERDATA_TERM_T)(void *userdata); -+ -+typedef struct vchiq_bulk_struct { -+ short mode; -+ short dir; -+ void *userdata; -+ VCHI_MEM_HANDLE_T handle; -+ void *data; -+ int size; -+ void *remote_data; -+ int remote_size; -+ int actual; -+} VCHIQ_BULK_T; -+ -+typedef struct vchiq_bulk_queue_struct { -+ int local_insert; /* Where to insert the next local bulk */ -+ int remote_insert; /* Where to insert the next remote bulk (master) */ -+ int process; /* Bulk to transfer next */ -+ int remote_notify; /* Bulk to notify the remote client of next (mstr) */ -+ int remove; /* Bulk to notify the local client of, and remove, -+ ** next */ -+ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS]; -+} VCHIQ_BULK_QUEUE_T; -+ -+typedef struct remote_event_struct { -+ int armed; -+ int fired; -+ struct semaphore *event; -+} REMOTE_EVENT_T; -+ -+typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T; -+ -+typedef struct vchiq_state_struct VCHIQ_STATE_T; -+ -+typedef struct vchiq_slot_struct { -+ char data[VCHIQ_SLOT_SIZE]; -+} VCHIQ_SLOT_T; -+ -+typedef struct vchiq_slot_info_struct { -+ /* Use two counters rather than one to avoid the need for a mutex. */ -+ short use_count; -+ short release_count; -+} VCHIQ_SLOT_INFO_T; -+ -+typedef struct vchiq_service_struct { -+ VCHIQ_SERVICE_BASE_T base; -+ VCHIQ_SERVICE_HANDLE_T handle; -+ unsigned int ref_count; -+ int srvstate; -+ VCHIQ_USERDATA_TERM_T userdata_term; -+ unsigned int localport; -+ unsigned int remoteport; -+ int public_fourcc; -+ int client_id; -+ char auto_close; -+ char sync; -+ char closing; -+ char trace; -+ atomic_t poll_flags; -+ short version; -+ short version_min; -+ short peer_version; -+ -+ VCHIQ_STATE_T *state; -+ VCHIQ_INSTANCE_T instance; -+ -+ int service_use_count; -+ -+ VCHIQ_BULK_QUEUE_T bulk_tx; -+ VCHIQ_BULK_QUEUE_T bulk_rx; -+ -+ struct semaphore remove_event; -+ struct semaphore bulk_remove_event; -+ struct mutex bulk_mutex; -+ -+ struct service_stats_struct { -+ int quota_stalls; -+ int slot_stalls; -+ int bulk_stalls; -+ int error_count; -+ int ctrl_tx_count; -+ int ctrl_rx_count; -+ int bulk_tx_count; -+ int bulk_rx_count; -+ int bulk_aborted_count; -+ uint64_t ctrl_tx_bytes; -+ uint64_t ctrl_rx_bytes; -+ uint64_t bulk_tx_bytes; -+ uint64_t bulk_rx_bytes; -+ } stats; -+} VCHIQ_SERVICE_T; -+ -+/* The quota information is outside VCHIQ_SERVICE_T so that it can be -+ statically allocated, since for accounting reasons a service's slot -+ usage is carried over between users of the same port number. -+ */ -+typedef struct vchiq_service_quota_struct { -+ unsigned short slot_quota; -+ unsigned short slot_use_count; -+ unsigned short message_quota; -+ unsigned short message_use_count; -+ struct semaphore quota_event; -+ int previous_tx_index; -+} VCHIQ_SERVICE_QUOTA_T; -+ -+typedef struct vchiq_shared_state_struct { -+ -+ /* A non-zero value here indicates that the content is valid. */ -+ int initialised; -+ -+ /* The first and last (inclusive) slots allocated to the owner. */ -+ int slot_first; -+ int slot_last; -+ -+ /* The slot allocated to synchronous messages from the owner. */ -+ int slot_sync; -+ -+ /* Signalling this event indicates that owner's slot handler thread -+ ** should run. */ -+ REMOTE_EVENT_T trigger; -+ -+ /* Indicates the byte position within the stream where the next message -+ ** will be written. The least significant bits are an index into the -+ ** slot. The next bits are the index of the slot in slot_queue. */ -+ int tx_pos; -+ -+ /* This event should be signalled when a slot is recycled. */ -+ REMOTE_EVENT_T recycle; -+ -+ /* The slot_queue index where the next recycled slot will be written. */ -+ int slot_queue_recycle; -+ -+ /* This event should be signalled when a synchronous message is sent. */ -+ REMOTE_EVENT_T sync_trigger; -+ -+ /* This event should be signalled when a synchronous message has been -+ ** released. */ -+ REMOTE_EVENT_T sync_release; -+ -+ /* A circular buffer of slot indexes. */ -+ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE]; -+ -+ /* Debugging state */ -+ int debug[DEBUG_MAX]; -+} VCHIQ_SHARED_STATE_T; -+ -+typedef struct vchiq_slot_zero_struct { -+ int magic; -+ short version; -+ short version_min; -+ int slot_zero_size; -+ int slot_size; -+ int max_slots; -+ int max_slots_per_side; -+ int platform_data[2]; -+ VCHIQ_SHARED_STATE_T master; -+ VCHIQ_SHARED_STATE_T slave; -+ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS]; -+} VCHIQ_SLOT_ZERO_T; -+ -+struct vchiq_state_struct { -+ int id; -+ int initialised; -+ VCHIQ_CONNSTATE_T conn_state; -+ int is_master; -+ -+ VCHIQ_SHARED_STATE_T *local; -+ VCHIQ_SHARED_STATE_T *remote; -+ VCHIQ_SLOT_T *slot_data; -+ -+ unsigned short default_slot_quota; -+ unsigned short default_message_quota; -+ -+ /* Event indicating connect message received */ -+ struct semaphore connect; -+ -+ /* Mutex protecting services */ -+ struct mutex mutex; -+ VCHIQ_INSTANCE_T *instance; -+ -+ /* Processes incoming messages */ -+ struct task_struct *slot_handler_thread; -+ -+ /* Processes recycled slots */ -+ struct task_struct *recycle_thread; -+ -+ /* Processes synchronous messages */ -+ struct task_struct *sync_thread; -+ -+ /* Local implementation of the trigger remote event */ -+ struct semaphore trigger_event; -+ -+ /* Local implementation of the recycle remote event */ -+ struct semaphore recycle_event; -+ -+ /* Local implementation of the sync trigger remote event */ -+ struct semaphore sync_trigger_event; -+ -+ /* Local implementation of the sync release remote event */ -+ struct semaphore sync_release_event; -+ -+ char *tx_data; -+ char *rx_data; -+ VCHIQ_SLOT_INFO_T *rx_info; -+ -+ struct mutex slot_mutex; -+ -+ struct mutex recycle_mutex; -+ -+ struct mutex sync_mutex; -+ -+ struct mutex bulk_transfer_mutex; -+ -+ /* Indicates the byte position within the stream from where the next -+ ** message will be read. The least significant bits are an index into -+ ** the slot.The next bits are the index of the slot in -+ ** remote->slot_queue. */ -+ int rx_pos; -+ -+ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read -+ from remote->tx_pos. */ -+ int local_tx_pos; -+ -+ /* The slot_queue index of the slot to become available next. */ -+ int slot_queue_available; -+ -+ /* A flag to indicate if any poll has been requested */ -+ int poll_needed; -+ -+ /* Ths index of the previous slot used for data messages. */ -+ int previous_data_index; -+ -+ /* The number of slots occupied by data messages. */ -+ unsigned short data_use_count; -+ -+ /* The maximum number of slots to be occupied by data messages. */ -+ unsigned short data_quota; -+ -+ /* An array of bit sets indicating which services must be polled. */ -+ atomic_t poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; -+ -+ /* The number of the first unused service */ -+ int unused_service; -+ -+ /* Signalled when a free slot becomes available. */ -+ struct semaphore slot_available_event; -+ -+ struct semaphore slot_remove_event; -+ -+ /* Signalled when a free data slot becomes available. */ -+ struct semaphore data_quota_event; -+ -+ /* Incremented when there are bulk transfers which cannot be processed -+ * whilst paused and must be processed on resume */ -+ int deferred_bulks; -+ -+ struct state_stats_struct { -+ int slot_stalls; -+ int data_stalls; -+ int ctrl_tx_count; -+ int ctrl_rx_count; -+ int error_count; -+ } stats; -+ -+ VCHIQ_SERVICE_T * services[VCHIQ_MAX_SERVICES]; -+ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; -+ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; -+ -+ VCHIQ_PLATFORM_STATE_T platform_state; -+}; -+ -+struct bulk_waiter { -+ VCHIQ_BULK_T *bulk; -+ struct semaphore event; -+ int actual; -+}; -+ -+extern spinlock_t bulk_waiter_spinlock; -+ -+extern int vchiq_core_log_level; -+extern int vchiq_core_msg_log_level; -+extern int vchiq_sync_log_level; -+ -+extern VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; -+ -+extern const char * -+get_conn_state_name(VCHIQ_CONNSTATE_T conn_state); -+ -+extern VCHIQ_SLOT_ZERO_T * -+vchiq_init_slots(void *mem_base, int mem_size); -+ -+extern VCHIQ_STATUS_T -+vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, -+ int is_master); -+ -+extern VCHIQ_STATUS_T -+vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_SERVICE_T * -+vchiq_add_service_internal(VCHIQ_STATE_T *state, -+ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, -+ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term); -+ -+extern VCHIQ_STATUS_T -+vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id); -+ -+extern VCHIQ_STATUS_T -+vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd); -+ -+extern void -+vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_free_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_STATUS_T -+vchiq_pause_internal(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_resume_internal(VCHIQ_STATE_T *state); -+ -+extern void -+remote_event_pollall(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir); -+ -+extern void -+vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_loud_error_header(void); -+ -+extern void -+vchiq_loud_error_footer(void); -+ -+extern void -+request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type); -+ -+static inline VCHIQ_SERVICE_T * -+handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) -+{ -+ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & -+ (VCHIQ_MAX_STATES - 1)]; -+ if (!state) ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) + return NULL; + -+ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; -+} ++ d->dir = direction; + -+extern VCHIQ_SERVICE_T * -+find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+find_service_by_port(VCHIQ_STATE_T *state, int localport); -+ -+extern VCHIQ_SERVICE_T * -+find_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, -+ VCHIQ_SERVICE_HANDLE_T handle); -+ -+extern VCHIQ_SERVICE_T * -+next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, -+ int *pidx); -+ -+extern void -+lock_service(VCHIQ_SERVICE_T *service); -+ -+extern void -+unlock_service(VCHIQ_SERVICE_T *service); -+ -+/* The following functions are called from vchiq_core, and external -+** implementations must be provided. */ -+ -+extern VCHIQ_STATUS_T -+vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, -+ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir); -+ -+extern void -+vchiq_transfer_bulk(VCHIQ_BULK_T *bulk); -+ -+extern void -+vchiq_complete_bulk(VCHIQ_BULK_T *bulk); -+ -+extern VCHIQ_STATUS_T -+vchiq_copy_from_user(void *dst, const void *src, int size); -+ -+extern void -+remote_event_signal(REMOTE_EVENT_T *event); -+ -+void -+vchiq_platform_check_suspend(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_paused(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_resume(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_resumed(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_dump(void *dump_context, const char *str, int len); -+ -+extern void -+vchiq_dump_platform_state(void *dump_context); -+ -+extern void -+vchiq_dump_platform_instances(void *dump_context); -+ -+extern void -+vchiq_dump_platform_service_state(void *dump_context, -+ VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_use_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern VCHIQ_STATUS_T -+vchiq_release_service_internal(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_on_remote_use(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_on_remote_release(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_platform_init_state(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_check_service(VCHIQ_SERVICE_T *service); -+ -+extern void -+vchiq_on_remote_use_active(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_use(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_release(VCHIQ_STATE_T *state); -+ -+extern VCHIQ_STATUS_T -+vchiq_send_remote_use_active(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, -+ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate); -+ -+extern void -+vchiq_platform_handle_timeout(VCHIQ_STATE_T *state); -+ -+extern void -+vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate); -+ -+ -+extern void -+vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, -+ size_t numBytes); -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c -new file mode 100644 -index 0000000..7e03213 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c -@@ -0,0 +1,383 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+#include -+#include "vchiq_core.h" -+#include "vchiq_arm.h" -+#include "vchiq_debugfs.h" -+ -+#ifdef CONFIG_DEBUG_FS -+ -+/**************************************************************************** -+* -+* log category entries -+* -+***************************************************************************/ -+#define DEBUGFS_WRITE_BUF_SIZE 256 -+ -+#define VCHIQ_LOG_ERROR_STR "error" -+#define VCHIQ_LOG_WARNING_STR "warning" -+#define VCHIQ_LOG_INFO_STR "info" -+#define VCHIQ_LOG_TRACE_STR "trace" -+ -+ -+/* Top-level debug info */ -+struct vchiq_debugfs_info { -+ /* Global 'vchiq' debugfs entry used by all instances */ -+ struct dentry *vchiq_cfg_dir; -+ -+ /* one entry per client process */ -+ struct dentry *clients; -+ -+ /* log categories */ -+ struct dentry *log_categories; -+}; -+ -+static struct vchiq_debugfs_info debugfs_info; -+ -+/* Log category debugfs entries */ -+struct vchiq_debugfs_log_entry { -+ const char *name; -+ int *plevel; -+ struct dentry *dir; -+}; -+ -+static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { -+ { "core", &vchiq_core_log_level }, -+ { "msg", &vchiq_core_msg_log_level }, -+ { "sync", &vchiq_sync_log_level }, -+ { "susp", &vchiq_susp_log_level }, -+ { "arm", &vchiq_arm_log_level }, -+}; -+static int n_log_entries = -+ sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); -+ -+ -+static struct dentry *vchiq_clients_top(void); -+static struct dentry *vchiq_debugfs_top(void); -+ -+static int debugfs_log_show(struct seq_file *f, void *offset) -+{ -+ int *levp = f->private; -+ char *log_value = NULL; -+ -+ switch (*levp) { -+ case VCHIQ_LOG_ERROR: -+ log_value = VCHIQ_LOG_ERROR_STR; -+ break; -+ case VCHIQ_LOG_WARNING: -+ log_value = VCHIQ_LOG_WARNING_STR; -+ break; -+ case VCHIQ_LOG_INFO: -+ log_value = VCHIQ_LOG_INFO_STR; -+ break; -+ case VCHIQ_LOG_TRACE: -+ log_value = VCHIQ_LOG_TRACE_STR; -+ break; -+ default: -+ break; -+ } -+ -+ seq_printf(f, "%s\n", log_value ? log_value : "(null)"); -+ -+ return 0; -+} -+ -+static int debugfs_log_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_log_show, inode->i_private); -+} -+ -+static int debugfs_log_write(struct file *file, -+ const char __user *buffer, -+ size_t count, loff_t *ppos) -+{ -+ struct seq_file *f = (struct seq_file *)file->private_data; -+ int *levp = f->private; -+ char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1]; -+ -+ memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1); -+ if (count >= DEBUGFS_WRITE_BUF_SIZE) -+ count = DEBUGFS_WRITE_BUF_SIZE; -+ -+ if (copy_from_user(kbuf, buffer, count) != 0) -+ return -EFAULT; -+ kbuf[count - 1] = 0; -+ -+ if (strncmp("error", kbuf, strlen("error")) == 0) -+ *levp = VCHIQ_LOG_ERROR; -+ else if (strncmp("warning", kbuf, strlen("warning")) == 0) -+ *levp = VCHIQ_LOG_WARNING; -+ else if (strncmp("info", kbuf, strlen("info")) == 0) -+ *levp = VCHIQ_LOG_INFO; -+ else if (strncmp("trace", kbuf, strlen("trace")) == 0) -+ *levp = VCHIQ_LOG_TRACE; ++ if (c->ch >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; + else -+ *levp = VCHIQ_LOG_DEFAULT; ++ max_size = MAX_NORMAL_TRANSFER; ++ period_len = min(period_len, max_size); + -+ *ppos += count; ++ d->frames = (buf_len-1) / period_len + 1; + -+ return count; -+} ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); + -+static const struct file_operations debugfs_log_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_log_open, -+ .write = debugfs_log_write, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } + -+/* create an entry under /vchiq/log for each log category */ -+static int vchiq_debugfs_create_log_entries(struct dentry *top) -+{ -+ struct dentry *dir; -+ size_t i; -+ int ret = 0; -+ dir = debugfs_create_dir("log", vchiq_debugfs_top()); -+ if (!dir) -+ return -ENOMEM; -+ debugfs_info.log_categories = dir; ++ /* ++ * Iterate over all frames, create a control block ++ * for each frame and link them together. ++ */ ++ for (frame = 0; frame < d->frames; frame++) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[frame]; + -+ for (i = 0; i < n_log_entries; i++) { -+ void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; -+ dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, -+ 0644, -+ debugfs_info.log_categories, -+ levp, -+ &debugfs_log_fops); -+ if (!dir) { -+ ret = -ENOMEM; -+ break; ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2835_DMA_D_INC; ++ control_block->src = dev_addr; ++ control_block->dst = buf_addr + frame * period_len; ++ } else { ++ control_block->info = BCM2835_DMA_S_INC; ++ control_block->src = buf_addr + frame * period_len; ++ control_block->dst = dev_addr; + } + -+ vchiq_debugfs_log_entries[i].dir = dir; -+ } -+ return ret; -+} ++ /* Enable interrupt */ ++ control_block->info |= BCM2835_DMA_INT_EN; + -+static int debugfs_usecount_show(struct seq_file *f, void *offset) -+{ -+ VCHIQ_INSTANCE_T instance = f->private; -+ int use_count; ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; + -+ use_count = vchiq_instance_get_use_count(instance); -+ seq_printf(f, "%d\n", use_count); ++ /* Setup DREQ channel */ ++ if (c->cfg.slave_id != 0) ++ control_block->info |= ++ BCM2835_DMA_PER_MAP(c->cfg.slave_id); + -+ return 0; -+} -+ -+static int debugfs_usecount_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_usecount_show, inode->i_private); -+} -+ -+static const struct file_operations debugfs_usecount_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_usecount_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+static int debugfs_trace_show(struct seq_file *f, void *offset) -+{ -+ VCHIQ_INSTANCE_T instance = f->private; -+ int trace; -+ -+ trace = vchiq_instance_get_trace(instance); -+ seq_printf(f, "%s\n", trace ? "Y" : "N"); -+ -+ return 0; -+} -+ -+static int debugfs_trace_open(struct inode *inode, struct file *file) -+{ -+ return single_open(file, debugfs_trace_show, inode->i_private); -+} -+ -+static int debugfs_trace_write(struct file *file, -+ const char __user *buffer, -+ size_t count, loff_t *ppos) -+{ -+ struct seq_file *f = (struct seq_file *)file->private_data; -+ VCHIQ_INSTANCE_T instance = f->private; -+ char firstchar; -+ -+ if (copy_from_user(&firstchar, buffer, 1) != 0) -+ return -EFAULT; -+ -+ switch (firstchar) { -+ case 'Y': -+ case 'y': -+ case '1': -+ vchiq_instance_set_trace(instance, 1); -+ break; -+ case 'N': -+ case 'n': -+ case '0': -+ vchiq_instance_set_trace(instance, 0); -+ break; -+ default: -+ break; -+ } -+ -+ *ppos += count; -+ -+ return count; -+} -+ -+static const struct file_operations debugfs_trace_fops = { -+ .owner = THIS_MODULE, -+ .open = debugfs_trace_open, -+ .write = debugfs_trace_write, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = single_release, -+}; -+ -+/* add an instance (process) to the debugfs entries */ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) -+{ -+ char pidstr[16]; -+ struct dentry *top, *use_count, *trace; -+ struct dentry *clients = vchiq_clients_top(); -+ -+ snprintf(pidstr, sizeof(pidstr), "%d", -+ vchiq_instance_get_pid(instance)); -+ -+ top = debugfs_create_dir(pidstr, clients); -+ if (!top) -+ goto fail_top; -+ -+ use_count = debugfs_create_file("use_count", -+ 0444, top, -+ instance, -+ &debugfs_usecount_fops); -+ if (!use_count) -+ goto fail_use_count; -+ -+ trace = debugfs_create_file("trace", -+ 0644, top, -+ instance, -+ &debugfs_trace_fops); -+ if (!trace) -+ goto fail_trace; -+ -+ vchiq_instance_get_debugfs_node(instance)->dentry = top; -+ -+ return 0; -+ -+fail_trace: -+ debugfs_remove(use_count); -+fail_use_count: -+ debugfs_remove(top); -+fail_top: -+ return -ENOMEM; -+} -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); -+ debugfs_remove_recursive(node->dentry); -+} -+ -+ -+int vchiq_debugfs_init(void) -+{ -+ BUG_ON(debugfs_info.vchiq_cfg_dir != NULL); -+ -+ debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL); -+ if (debugfs_info.vchiq_cfg_dir == NULL) -+ goto fail; -+ -+ debugfs_info.clients = debugfs_create_dir("clients", -+ vchiq_debugfs_top()); -+ if (!debugfs_info.clients) -+ goto fail; -+ -+ if (vchiq_debugfs_create_log_entries(vchiq_debugfs_top()) != 0) -+ goto fail; -+ -+ return 0; -+ -+fail: -+ vchiq_debugfs_deinit(); -+ vchiq_log_error(vchiq_arm_log_level, -+ "%s: failed to create debugfs directory", -+ __func__); -+ -+ return -ENOMEM; -+} -+ -+/* remove all the debugfs entries */ -+void vchiq_debugfs_deinit(void) -+{ -+ debugfs_remove_recursive(vchiq_debugfs_top()); -+} -+ -+static struct dentry *vchiq_clients_top(void) -+{ -+ return debugfs_info.clients; -+} -+ -+static struct dentry *vchiq_debugfs_top(void) -+{ -+ BUG_ON(debugfs_info.vchiq_cfg_dir == NULL); -+ return debugfs_info.vchiq_cfg_dir; -+} -+ -+#else /* CONFIG_DEBUG_FS */ -+ -+int vchiq_debugfs_init(void) -+{ -+ return 0; -+} -+ -+void vchiq_debugfs_deinit(void) -+{ -+} -+ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) -+{ -+ return 0; -+} -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) -+{ -+} -+ -+#endif /* CONFIG_DEBUG_FS */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h -new file mode 100644 -index 0000000..4d6a378 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h -@@ -0,0 +1,52 @@ -+/** -+ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_DEBUGFS_H -+#define VCHIQ_DEBUGFS_H -+ -+#include "vchiq_core.h" -+ -+typedef struct vchiq_debugfs_node_struct -+{ -+ struct dentry *dentry; -+} VCHIQ_DEBUGFS_NODE_T; -+ -+int vchiq_debugfs_init(void); -+ -+void vchiq_debugfs_deinit(void); -+ -+int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance); -+ -+void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance); -+ -+#endif /* VCHIQ_DEBUGFS_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion -new file mode 100644 -index 0000000..9f5b634 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion -@@ -0,0 +1,87 @@ -+#!/usr/bin/perl -w -+ -+use strict; -+ -+# -+# Generate a version from available information -+# -+ -+my $prefix = shift @ARGV; -+my $root = shift @ARGV; -+ -+ -+if ( not defined $root ) { -+ die "usage: $0 prefix root-dir\n"; -+} -+ -+if ( ! -d $root ) { -+ die "root directory $root not found\n"; -+} -+ -+my $version = "unknown"; -+my $tainted = ""; -+ -+if ( -d "$root/.git" ) { -+ # attempt to work out git version. only do so -+ # on a linux build host, as cygwin builds are -+ # already slow enough -+ -+ if ( -f "/usr/bin/git" || -f "/usr/local/bin/git" ) { -+ if (not open(F, "git --git-dir $root/.git rev-parse --verify HEAD|")) { -+ $version = "no git version"; -+ } -+ else { -+ $version = ; -+ $version =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+ $version =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ } -+ -+ if (open(G, "git --git-dir $root/.git status --porcelain|")) { -+ $tainted = ; -+ $tainted =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+ $tainted =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ if (length $tainted) { -+ $version = join ' ', $version, "(tainted)"; -+ } -+ else { -+ $version = join ' ', $version, "(clean)"; -+ } -+ } -+ } -+} -+ -+my $hostname = `hostname`; -+$hostname =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). -+$hostname =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). -+ -+ -+print STDERR "Version $version\n"; -+print < -+ -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_hostname, "$hostname" ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_version, "$version" ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_time, __TIME__ ); -+VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_date, __DATE__ ); -+ -+const char *vchiq_get_build_hostname( void ) -+{ -+ return vchiq_build_hostname; -+} -+ -+const char *vchiq_get_build_version( void ) -+{ -+ return vchiq_build_version; -+} -+ -+const char *vchiq_get_build_date( void ) -+{ -+ return vchiq_build_date; -+} -+ -+const char *vchiq_get_build_time( void ) -+{ -+ return vchiq_build_time; -+} -+EOF -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h -new file mode 100644 -index 0000000..8067bbe ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h -@@ -0,0 +1,189 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_IF_H -+#define VCHIQ_IF_H -+ -+#include "interface/vchi/vchi_mh.h" -+ -+#define VCHIQ_SERVICE_HANDLE_INVALID 0 -+ -+#define VCHIQ_SLOT_SIZE 4096 -+#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) -+#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ -+ -+#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) \ -+ (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) -+#define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) -+#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) -+ -+typedef enum { -+ VCHIQ_SERVICE_OPENED, /* service, -, - */ -+ VCHIQ_SERVICE_CLOSED, /* service, -, - */ -+ VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */ -+ VCHIQ_BULK_TRANSMIT_DONE, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */ -+ VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */ -+} VCHIQ_REASON_T; -+ -+typedef enum { -+ VCHIQ_ERROR = -1, -+ VCHIQ_SUCCESS = 0, -+ VCHIQ_RETRY = 1 -+} VCHIQ_STATUS_T; -+ -+typedef enum { -+ VCHIQ_BULK_MODE_CALLBACK, -+ VCHIQ_BULK_MODE_BLOCKING, -+ VCHIQ_BULK_MODE_NOCALLBACK, -+ VCHIQ_BULK_MODE_WAITING /* Reserved for internal use */ -+} VCHIQ_BULK_MODE_T; -+ -+typedef enum { -+ VCHIQ_SERVICE_OPTION_AUTOCLOSE, -+ VCHIQ_SERVICE_OPTION_SLOT_QUOTA, -+ VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, -+ VCHIQ_SERVICE_OPTION_SYNCHRONOUS, -+ VCHIQ_SERVICE_OPTION_TRACE -+} VCHIQ_SERVICE_OPTION_T; -+ -+typedef struct vchiq_header_struct { -+ /* The message identifier - opaque to applications. */ -+ int msgid; -+ -+ /* Size of message data. */ -+ unsigned int size; -+ -+ char data[0]; /* message */ -+} VCHIQ_HEADER_T; -+ -+typedef struct { -+ const void *data; -+ unsigned int size; -+} VCHIQ_ELEMENT_T; -+ -+typedef unsigned int VCHIQ_SERVICE_HANDLE_T; -+ -+typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, -+ VCHIQ_SERVICE_HANDLE_T, void *); -+ -+typedef struct vchiq_service_base_struct { -+ int fourcc; -+ VCHIQ_CALLBACK_T callback; -+ void *userdata; -+} VCHIQ_SERVICE_BASE_T; -+ -+typedef struct vchiq_service_params_struct { -+ int fourcc; -+ VCHIQ_CALLBACK_T callback; -+ void *userdata; -+ short version; /* Increment for non-trivial changes */ -+ short version_min; /* Update for incompatible changes */ -+} VCHIQ_SERVICE_PARAMS_T; -+ -+typedef struct vchiq_config_struct { -+ unsigned int max_msg_size; -+ unsigned int bulk_threshold; /* The message size above which it -+ is better to use a bulk transfer -+ (<= max_msg_size) */ -+ unsigned int max_outstanding_bulks; -+ unsigned int max_services; -+ short version; /* The version of VCHIQ */ -+ short version_min; /* The minimum compatible version of VCHIQ */ -+} VCHIQ_CONFIG_T; -+ -+typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; -+typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void *cb_arg); -+ -+extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); -+extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); -+extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); -+extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *pservice); -+extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *pservice); -+extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_use_service_no_resume( -+ VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); -+ -+extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, -+ const VCHIQ_ELEMENT_T *elements, unsigned int count); -+extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, -+ VCHIQ_HEADER_T *header); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, -+ const void *data, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, -+ void *data, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle( -+ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, -+ const void *offset, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle( -+ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, -+ void *offset, unsigned int size, void *userdata); -+extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, -+ const void *data, unsigned int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, -+ void *data, unsigned int size, void *userdata, -+ VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, -+ VCHI_MEM_HANDLE_T handle, const void *offset, unsigned int size, -+ void *userdata, VCHIQ_BULK_MODE_T mode); -+extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, -+ VCHI_MEM_HANDLE_T handle, void *offset, unsigned int size, -+ void *userdata, VCHIQ_BULK_MODE_T mode); -+extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); -+extern void *vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T service); -+extern int vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T service); -+extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, -+ int config_size, VCHIQ_CONFIG_T *pconfig); -+extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, -+ VCHIQ_SERVICE_OPTION_T option, int value); -+ -+extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, -+ VCHIQ_REMOTE_USE_CALLBACK_T callback, void *cb_arg); -+extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); -+ -+extern VCHIQ_STATUS_T vchiq_dump_phys_mem(VCHIQ_SERVICE_HANDLE_T service, -+ void *ptr, size_t num_bytes); -+ -+extern VCHIQ_STATUS_T vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, -+ short *peer_version); -+ -+#endif /* VCHIQ_IF_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h -new file mode 100644 -index 0000000..6137ae9 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h -@@ -0,0 +1,131 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_IOCTLS_H -+#define VCHIQ_IOCTLS_H -+ -+#include -+#include "vchiq_if.h" -+ -+#define VCHIQ_IOC_MAGIC 0xc4 -+#define VCHIQ_INVALID_HANDLE (~0) -+ -+typedef struct { -+ VCHIQ_SERVICE_PARAMS_T params; -+ int is_open; -+ int is_vchi; -+ unsigned int handle; /* OUT */ -+} VCHIQ_CREATE_SERVICE_T; -+ -+typedef struct { -+ unsigned int handle; -+ unsigned int count; -+ const VCHIQ_ELEMENT_T *elements; -+} VCHIQ_QUEUE_MESSAGE_T; -+ -+typedef struct { -+ unsigned int handle; -+ void *data; -+ unsigned int size; -+ void *userdata; -+ VCHIQ_BULK_MODE_T mode; -+} VCHIQ_QUEUE_BULK_TRANSFER_T; -+ -+typedef struct { -+ VCHIQ_REASON_T reason; -+ VCHIQ_HEADER_T *header; -+ void *service_userdata; -+ void *bulk_userdata; -+} VCHIQ_COMPLETION_DATA_T; -+ -+typedef struct { -+ unsigned int count; -+ VCHIQ_COMPLETION_DATA_T *buf; -+ unsigned int msgbufsize; -+ unsigned int msgbufcount; /* IN/OUT */ -+ void **msgbufs; -+} VCHIQ_AWAIT_COMPLETION_T; -+ -+typedef struct { -+ unsigned int handle; -+ int blocking; -+ unsigned int bufsize; -+ void *buf; -+} VCHIQ_DEQUEUE_MESSAGE_T; -+ -+typedef struct { -+ unsigned int config_size; -+ VCHIQ_CONFIG_T *pconfig; -+} VCHIQ_GET_CONFIG_T; -+ -+typedef struct { -+ unsigned int handle; -+ VCHIQ_SERVICE_OPTION_T option; -+ int value; -+} VCHIQ_SET_SERVICE_OPTION_T; -+ -+typedef struct { -+ void *virt_addr; -+ size_t num_bytes; -+} VCHIQ_DUMP_MEM_T; -+ -+#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) -+#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) -+#define VCHIQ_IOC_CREATE_SERVICE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) -+#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) -+#define VCHIQ_IOC_QUEUE_MESSAGE \ -+ _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) -+#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \ -+ _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) -+#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) -+#define VCHIQ_IOC_AWAIT_COMPLETION \ -+ _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) -+#define VCHIQ_IOC_DEQUEUE_MESSAGE \ -+ _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) -+#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) -+#define VCHIQ_IOC_GET_CONFIG \ -+ _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) -+#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) -+#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) -+#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) -+#define VCHIQ_IOC_SET_SERVICE_OPTION \ -+ _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) -+#define VCHIQ_IOC_DUMP_PHYS_MEM \ -+ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) -+#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) -+#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) -+#define VCHIQ_IOC_MAX 17 -+ -+#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -new file mode 100644 -index 0000000..be9735f ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -0,0 +1,456 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* ---- Include Files ---------------------------------------------------- */ -+ -+#include -+#include -+#include -+ -+#include "vchiq_core.h" -+#include "vchiq_arm.h" -+ -+/* ---- Public Variables ------------------------------------------------- */ -+ -+/* ---- Private Constants and Types -------------------------------------- */ -+ -+struct bulk_waiter_node { -+ struct bulk_waiter bulk_waiter; -+ int pid; -+ struct list_head list; -+}; -+ -+struct vchiq_instance_struct { -+ VCHIQ_STATE_T *state; -+ -+ int connected; -+ -+ struct list_head bulk_waiter_list; -+ struct mutex bulk_waiter_list_mutex; -+}; -+ -+static VCHIQ_STATUS_T -+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, VCHIQ_BULK_DIR_T dir); -+ -+/**************************************************************************** -+* -+* vchiq_initialise -+* -+***************************************************************************/ -+#define VCHIQ_INIT_RETRIES 10 -+VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_STATE_T *state; -+ VCHIQ_INSTANCE_T instance = NULL; -+ int i; -+ -+ vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); -+ -+ /* VideoCore may not be ready due to boot up timing. -+ It may never be ready if kernel and firmware are mismatched, so don't block forever. */ -+ for (i=0; i0) { -+ vchiq_log_warning(vchiq_core_log_level, -+ "%s: videocore initialized after %d retries\n", __func__, i); -+ } -+ -+ instance = kzalloc(sizeof(*instance), GFP_KERNEL); -+ if (!instance) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%s: error allocating vchiq instance\n", __func__); -+ goto failed; -+ } -+ -+ instance->connected = 0; -+ instance->state = state; -+ mutex_init(&instance->bulk_waiter_list_mutex); -+ INIT_LIST_HEAD(&instance->bulk_waiter_list); -+ -+ *instanceOut = instance; -+ -+ status = VCHIQ_SUCCESS; -+ -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_initialise); -+ -+/**************************************************************************** -+* -+* vchiq_shutdown -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ if (mutex_lock_interruptible(&state->mutex) != 0) -+ return VCHIQ_RETRY; -+ -+ /* Remove all services */ -+ status = vchiq_shutdown_internal(state, instance); -+ -+ mutex_unlock(&state->mutex); -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ if (status == VCHIQ_SUCCESS) { -+ struct list_head *pos, *next; -+ list_for_each_safe(pos, next, -+ &instance->bulk_waiter_list) { -+ struct bulk_waiter_node *waiter; -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ vchiq_log_info(vchiq_arm_log_level, -+ "bulk_waiter - cleaned up %x " -+ "for pid %d", -+ (unsigned int)waiter, waiter->pid); -+ kfree(waiter); -+ } -+ kfree(instance); -+ } -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_shutdown); -+ -+/**************************************************************************** -+* -+* vchiq_is_connected -+* -+***************************************************************************/ -+ -+int vchiq_is_connected(VCHIQ_INSTANCE_T instance) -+{ -+ return instance->connected; -+} -+ -+/**************************************************************************** -+* -+* vchiq_connect -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ if (mutex_lock_interruptible(&state->mutex) != 0) { -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s: call to mutex_lock failed", __func__); -+ status = VCHIQ_RETRY; -+ goto failed; -+ } -+ status = vchiq_connect_internal(state, instance); -+ -+ if (status == VCHIQ_SUCCESS) -+ instance->connected = 1; -+ -+ mutex_unlock(&state->mutex); -+ -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_connect); -+ -+/**************************************************************************** -+* -+* vchiq_add_service -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_add_service( -+ VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *phandle) -+{ -+ VCHIQ_STATUS_T status; -+ VCHIQ_STATE_T *state = instance->state; -+ VCHIQ_SERVICE_T *service = NULL; -+ int srvstate; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ -+ srvstate = vchiq_is_connected(instance) -+ ? VCHIQ_SRVSTATE_LISTENING -+ : VCHIQ_SRVSTATE_HIDDEN; -+ -+ service = vchiq_add_service_internal( -+ state, -+ params, -+ srvstate, -+ instance, -+ NULL); -+ -+ if (service) { -+ *phandle = service->handle; -+ status = VCHIQ_SUCCESS; -+ } else -+ status = VCHIQ_ERROR; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); -+ -+ return status; -+} -+EXPORT_SYMBOL(vchiq_add_service); -+ -+/**************************************************************************** -+* -+* vchiq_open_service -+* -+***************************************************************************/ -+ -+VCHIQ_STATUS_T vchiq_open_service( -+ VCHIQ_INSTANCE_T instance, -+ const VCHIQ_SERVICE_PARAMS_T *params, -+ VCHIQ_SERVICE_HANDLE_T *phandle) -+{ -+ VCHIQ_STATUS_T status = VCHIQ_ERROR; -+ VCHIQ_STATE_T *state = instance->state; -+ VCHIQ_SERVICE_T *service = NULL; -+ -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p) called", __func__, instance); -+ -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ -+ if (!vchiq_is_connected(instance)) -+ goto failed; -+ -+ service = vchiq_add_service_internal(state, -+ params, -+ VCHIQ_SRVSTATE_OPENING, -+ instance, -+ NULL); -+ -+ if (service) { -+ status = vchiq_open_service_internal(service, current->pid); -+ if (status == VCHIQ_SUCCESS) -+ *phandle = service->handle; ++ /* Length of a frame */ ++ if (frame != d->frames-1) ++ control_block->length = period_len; + else -+ vchiq_remove_service(service->handle); ++ control_block->length = buf_len - (d->frames - 1) * period_len; ++ ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ * This function is called on cyclic DMA transfers. ++ * Therefore, wrap around at number of frames. ++ */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2835_dma_cb) ++ * ((frame + 1) % d->frames); + } + -+failed: -+ vchiq_log_trace(vchiq_core_log_level, -+ "%s(%p): returning %d", __func__, instance, status); ++ c->cyclic = true; + -+ return status; ++ return vchan_tx_prep(&c->vc, &d->vd, flags); +} -+EXPORT_SYMBOL(vchiq_open_service); + -+VCHIQ_STATUS_T -+vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, -+ const void *data, unsigned int size, void *userdata) ++ ++static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( ++ struct dma_chan *chan, struct scatterlist *sgl, ++ unsigned int sg_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) +{ -+ return vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, -+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); -+} -+EXPORT_SYMBOL(vchiq_queue_bulk_transmit); ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2835_desc *d; ++ dma_addr_t dev_addr; ++ struct scatterlist *sgent; ++ unsigned int es, sync_type; ++ unsigned int i, j, splitct, max_size; + -+VCHIQ_STATUS_T -+vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, void *userdata) -+{ -+ return vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, data, size, userdata, -+ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); -+} -+EXPORT_SYMBOL(vchiq_queue_bulk_receive); ++ if (!is_slave_direction(direction)) { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; ++ } + -+VCHIQ_STATUS_T -+vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, -+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -+{ -+ VCHIQ_STATUS_T status; ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2835_DMA_S_DREQ; ++ } else { ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2835_DMA_D_DREQ; ++ } + -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ status = vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, -+ mode, VCHIQ_BULK_TRANSMIT); -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ status = vchiq_blocking_bulk_transfer(handle, -+ (void *)data, size, VCHIQ_BULK_TRANSMIT); ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2835_DMA_DATA_TYPE_S32; + break; + default: -+ return VCHIQ_ERROR; ++ return NULL; + } + -+ return status; -+} -+EXPORT_SYMBOL(vchiq_bulk_transmit); ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) ++ return NULL; + -+VCHIQ_STATUS_T -+vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) -+{ -+ VCHIQ_STATUS_T status; ++ d->dir = direction; + -+ switch (mode) { -+ case VCHIQ_BULK_MODE_NOCALLBACK: -+ case VCHIQ_BULK_MODE_CALLBACK: -+ status = vchiq_bulk_transfer(handle, -+ VCHI_MEM_HANDLE_INVALID, data, size, userdata, -+ mode, VCHIQ_BULK_RECEIVE); -+ break; -+ case VCHIQ_BULK_MODE_BLOCKING: -+ status = vchiq_blocking_bulk_transfer(handle, -+ (void *)data, size, VCHIQ_BULK_RECEIVE); -+ break; -+ default: -+ return VCHIQ_ERROR; ++ if (c->ch >= 8) /* we have a LITE channel */ ++ max_size = MAX_LITE_TRANSFER; ++ else ++ max_size = MAX_NORMAL_TRANSFER; ++ ++ /* We store the length of the SG list in d->frames ++ taking care to account for splitting up transfers ++ too large for a LITE channel */ ++ ++ d->frames = 0; ++ for_each_sg(sgl, sgent, sg_len, i) { ++ uint32_t len = sg_dma_len(sgent); ++ d->frames += 1 + len / max_size; + } + -+ return status; -+} -+EXPORT_SYMBOL(vchiq_bulk_receive); ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); + -+static VCHIQ_STATUS_T -+vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, -+ unsigned int size, VCHIQ_BULK_DIR_T dir) -+{ -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_SERVICE_T *service; -+ VCHIQ_STATUS_T status; -+ struct bulk_waiter_node *waiter = NULL; -+ struct list_head *pos; ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } + -+ service = find_service_by_handle(handle); -+ if (!service) -+ return VCHIQ_ERROR; ++ /* ++ * Iterate over all SG entries, create a control block ++ * for each frame and link them together. ++ */ + -+ instance = service->instance; ++ /* we count the number of times an SG entry had to be splitct ++ as a result of using a LITE channel */ ++ splitct = 0; + -+ unlock_service(service); ++ for_each_sg(sgl, sgent, sg_len, i) { ++ dma_addr_t addr = sg_dma_address(sgent); ++ uint32_t len = sg_dma_len(sgent); + -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_for_each(pos, &instance->bulk_waiter_list) { -+ if (list_entry(pos, struct bulk_waiter_node, -+ list)->pid == current->pid) { -+ waiter = list_entry(pos, -+ struct bulk_waiter_node, -+ list); -+ list_del(pos); -+ break; ++ for (j = 0; j < len; j += max_size) { ++ struct bcm2835_dma_cb *control_block = ++ &d->control_block_base[i+splitct]; ++ ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2835_DMA_D_INC | ++ BCM2835_DMA_D_WIDTH | BCM2835_DMA_S_DREQ; ++ control_block->src = dev_addr; ++ control_block->dst = addr + (dma_addr_t)j; ++ } else { ++ control_block->info = BCM2835_DMA_S_INC | ++ BCM2835_DMA_S_WIDTH | BCM2835_DMA_D_DREQ; ++ control_block->src = addr + (dma_addr_t)j; ++ control_block->dst = dev_addr; ++ } ++ ++ /* Common part */ ++ control_block->info |= BCM2835_DMA_WAITS(SDHCI_BCM_DMA_WAITS); ++ control_block->info |= BCM2835_DMA_WAIT_RESP; ++ ++ /* Enable */ ++ if (i == sg_len-1 && len-j <= max_size) ++ control_block->info |= BCM2835_DMA_INT_EN; ++ ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; ++ ++ /* Setup DREQ channel */ ++ if (c->dreq != 0) ++ control_block->info |= ++ BCM2835_DMA_PER_MAP(c->dreq); ++ ++ /* Length of a frame */ ++ control_block->length = min(len-j, max_size); ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ */ ++ if (i < sg_len-1 || len-j > max_size) { ++ /* next block is the next frame. */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2835_dma_cb) * (i + splitct + 1); ++ } else { ++ /* next block is empty. */ ++ control_block->next = 0; ++ } ++ ++ if (len-j > max_size) ++ splitct++; + } + } -+ mutex_unlock(&instance->bulk_waiter_list_mutex); + -+ if (waiter) { -+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; -+ if (bulk) { -+ /* This thread has an outstanding bulk transfer. */ -+ if ((bulk->data != data) || -+ (bulk->size != size)) { -+ /* This is not a retry of the previous one. -+ ** Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); ++ c->cyclic = false; ++ ++ return vchan_tx_prep(&c->vc, &d->vd, flags); ++} ++ ++static int bcm2835_dma_slave_config(struct dma_chan *chan, ++ struct dma_slave_config *cfg) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ if ((cfg->direction == DMA_DEV_TO_MEM && ++ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ (cfg->direction == DMA_MEM_TO_DEV && ++ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ !is_slave_direction(cfg->direction)) { ++ return -EINVAL; ++ } ++ ++ c->cfg = *cfg; ++ if (!c->dreq) ++ c->dreq = cfg->slave_id; ++ ++ return 0; ++} ++ ++static int bcm2835_dma_terminate_all(struct dma_chan *chan) ++{ ++ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); ++ struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); ++ unsigned long flags; ++ int timeout = 10000; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Prevent this channel being scheduled */ ++ spin_lock(&d->lock); ++ list_del_init(&c->node); ++ spin_unlock(&d->lock); ++ ++ /* ++ * Stop DMA activity: we assume the callback will not be called ++ * after bcm_dma_abort() returns (even if it does, it will see ++ * c->desc is NULL and exit.) ++ */ ++ if (c->desc) { ++ c->desc = NULL; ++ bcm2835_dma_abort(c->chan_base); ++ ++ /* Wait for stopping */ ++ while (--timeout) { ++ if (!(readl(c->chan_base + BCM2835_DMA_CS) & ++ BCM2835_DMA_ACTIVE)) ++ break; ++ ++ cpu_relax(); ++ } ++ ++ if (!timeout) ++ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); ++ } ++ ++ vchan_get_all_descriptors(&c->vc, &head); ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ vchan_dma_desc_free_list(&c->vc, &head); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_ARCH_BCM2835 ++static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) ++{ ++ struct bcm2835_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2835_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++#endif ++ ++static int bcm2708_dma_chan_init(struct bcm2835_dmadev *d, ++ void __iomem *chan_base, int chan_id, int irq) ++{ ++ struct bcm2835_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2835_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ c->chan_base = chan_base; ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++ ++ ++static void bcm2835_dma_free(struct bcm2835_dmadev *od) ++{ ++ struct bcm2835_chan *c, *next; ++ ++ list_for_each_entry_safe(c, next, &od->ddev.channels, ++ vc.chan.device_node) { ++ list_del(&c->vc.chan.device_node); ++ tasklet_kill(&c->vc.task); ++ } ++} ++ ++static const struct of_device_id bcm2835_dma_of_match[] = { ++ { .compatible = "brcm,bcm2835-dma", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); ++ ++static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, ++ struct of_dma *ofdma) ++{ ++ struct bcm2835_dmadev *d = ofdma->of_dma_data; ++ struct dma_chan *chan; ++ ++ chan = dma_get_any_slave_channel(&d->ddev); ++ if (!chan) ++ return NULL; ++ ++ /* Set DREQ from param */ ++ to_bcm2835_dma_chan(chan)->dreq = spec->args[0]; ++ ++ return chan; ++} ++ ++static int bcm2835_dma_probe(struct platform_device *pdev) ++{ ++ struct bcm2835_dmadev *od; ++#ifdef CONFIG_ARCH_BCM2835 ++ struct resource *res; ++ void __iomem *base; ++ uint32_t chans_available; ++#endif ++ int rc; ++ int i; ++ int irq; ++ ++ ++ if (!pdev->dev.dma_mask) ++ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ++ ++ /* If CONFIG_ARCH_BCM2835 is selected, device tree is used */ ++ /* hence the difference between probing */ ++ ++#ifndef CONFIG_ARCH_BCM2835 ++ ++ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_terminate_all = bcm2835_dma_terminate_all; ++ od->ddev.device_config = bcm2835_dma_slave_config; ++ od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ for (i = 0; i < 5; i++) { ++ void __iomem *chan_base; ++ int chan_id; ++ ++ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_LITE, ++ &chan_base, ++ &irq); ++ ++ if (chan_id < 0) ++ break; ++ ++ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); ++ if (rc) ++ goto err_no_dma; ++ } ++ ++ if (pdev->dev.of_node) { ++ rc = of_dma_controller_register(pdev->dev.of_node, ++ bcm2835_dma_xlate, od); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "Failed to register DMA controller\n"); ++ goto err_no_dma; ++ } ++ } ++ ++#else ++ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ od->base = base; ++ ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2835_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; ++ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; ++ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; ++ od->ddev.device_terminate_all = bcm2835_dma_terminate_all; ++ od->ddev.device_config = bcm2835_dma_slave_config; ++ od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); ++ od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ ++ /* Request DMA channel mask from device tree */ ++ if (of_property_read_u32(pdev->dev.of_node, ++ "brcm,dma-channel-mask", ++ &chans_available)) { ++ dev_err(&pdev->dev, "Failed to get channel mask\n"); ++ rc = -EINVAL; ++ goto err_no_dma; ++ } ++ ++ ++ /* ++ * Do not use the FIQ and BULK channels, ++ * because they are used by the GPU. ++ */ ++ chans_available &= ~(BCM2835_DMA_FIQ_MASK | BCM2835_DMA_BULK_MASK); ++ ++ ++ for (i = 0; i < pdev->num_resources; i++) { ++ irq = platform_get_irq(pdev, i); ++ if (irq < 0) ++ break; ++ ++ if (chans_available & (1 << i)) { ++ rc = bcm2835_dma_chan_init(od, i, irq); ++ if (rc) ++ goto err_no_dma; ++ } ++ } ++ ++ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); ++ ++ /* Device-tree DMA controller registration */ ++ rc = of_dma_controller_register(pdev->dev.of_node, ++ bcm2835_dma_xlate, od); ++ if (rc) { ++ dev_err(&pdev->dev, "Failed to register DMA controller\n"); ++ goto err_no_dma; ++ } ++#endif ++ ++ rc = dma_async_device_register(&od->ddev); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "Failed to register slave DMA engine device: %d\n", rc); ++ goto err_no_dma; ++ } ++ ++ dev_info(&pdev->dev, "Load BCM2835 DMA engine driver\n"); ++ ++ return 0; ++ ++err_no_dma: ++ bcm2835_dma_free(od); ++ return rc; ++} ++ ++static int bcm2835_dma_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_dmadev *od = platform_get_drvdata(pdev); ++ ++ dma_async_device_unregister(&od->ddev); ++ bcm2835_dma_free(od); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2835_dma_driver = { ++ .probe = bcm2835_dma_probe, ++ .remove = bcm2835_dma_remove, ++ .driver = { ++ .name = "bcm2708-dmaengine", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(bcm2835_dma_of_match), ++ }, ++}; ++ ++module_platform_driver(bcm2835_dma_driver); ++ ++MODULE_ALIAS("platform:bcm2835-dma"); ++MODULE_DESCRIPTION("BCM2835 DMA engine driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_AUTHOR("Gellert Weisz "); ++MODULE_LICENSE("GPL v2"); + +From 22939895c156c8da951a53e50165247c8f68846c Mon Sep 17 00:00:00 2001 +From: gellert +Date: Fri, 15 Aug 2014 16:35:06 +0100 +Subject: [PATCH 008/104] MMC: added alternative MMC driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mmc: Disable CMD23 transfers on all cards + +Pending wire-level investigation of these types of transfers +and associated errors on bcm2835-mmc, disable for now. Fallback of +CMD18/CMD25 transfers will be used automatically by the MMC layer. + +Reported/Tested-by: Gellert Weisz + +mmc: bcm2835-mmc: enable DT support for all architectures + +Both ARCH_BCM2835 and ARCH_BCM270x are built with OF now. +Enable Device Tree support for all architectures. + +Signed-off-by: Noralf Trønnes + +mmc: bcm2835-mmc: fix probe error handling + +Probe error handling is broken in several places. +Simplify error handling by using device managed functions. +Replace pr_{err,info} with dev_{err,info}. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/mach-bcm2708/bcm2708.c | 31 + + drivers/mmc/core/quirks.c | 4 + + drivers/mmc/host/Kconfig | 58 ++ + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/bcm2835-mmc.c | 1506 +++++++++++++++++++++++++++++++++++++++ + 5 files changed, 1600 insertions(+) + create mode 100644 drivers/mmc/host/bcm2835-mmc.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 5d8657b..d1308ed 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -399,6 +399,34 @@ static struct platform_device bcm2708_systemtimer_device = { + }, + }; + ++#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ ++static struct resource bcm2835_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2835_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2835_emmc_device = { ++ .name = "mmc-bcm2835", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), ++ .resource = bcm2835_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2835_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++#endif /* CONFIG_MMC_BCM2835 */ ++ + static struct resource bcm2708_powerman_resources[] = { + [0] = { + .start = PM_BASE, +@@ -524,6 +552,9 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_uart1_device); + bcm_register_device(&bcm2708_powerman_device); + ++#ifdef CONFIG_MMC_BCM2835 ++ bcm_register_device(&bcm2835_emmc_device); ++#endif + bcm2708_init_led(); + + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { +diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c +index dd1d1e0..f472082 100644 +--- a/drivers/mmc/core/quirks.c ++++ b/drivers/mmc/core/quirks.c +@@ -95,5 +95,9 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) + f->vendor_fixup(card, f->data); + } + } ++ /* SDHCI on BCM2708 - bug causes a certain sequence of CMD23 operations to fail. ++ * Disable this flag for all cards (fall-back to CMD25/CMD18 multi-block transfers). ++ */ ++ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; + } + EXPORT_SYMBOL(mmc_fixup_device); +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 61ac63a..b4928ec 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -4,6 +4,35 @@ + + comment "MMC/SD/SDIO Host Controller Drivers" + ++config MMC_BCM2835 ++ tristate "MMC support on BCM2835" ++ depends on MACH_BCM2708 ++ help ++ This selects the MMC Interface on BCM2835. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_DMA ++ bool "DMA support on BCM2835 Arasan controller" ++ depends on MMC_BCM2835 ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_PIO_DMA_BARRIER ++ int "Block count limit for PIO transfers" ++ depends on MMC_BCM2835 && MMC_BCM2835_DMA ++ range 0 256 ++ default 2 ++ help ++ The inclusive limit in bytes under which PIO will be used instead of DMA ++ ++ If unsure, say 2 here. ++ + config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA +@@ -328,6 +357,35 @@ config MMC_SDHCI_ST + If you have a controller with this interface, say Y or M here. + If unsure, say N. + ++config MMC_BCM2835 ++ tristate "MMC support on BCM2835" ++ depends on (MACH_BCM2708 || MACH_BCM2709) ++ help ++ This selects the MMC Interface on BCM2835. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_DMA ++ bool "DMA support on BCM2835 Arasan controller" ++ depends on MMC_BCM2835 ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ ++config MMC_BCM2835_PIO_DMA_BARRIER ++ int "Block count limit for PIO transfers" ++ depends on MMC_BCM2835 && MMC_BCM2835_DMA ++ range 0 256 ++ default 2 ++ help ++ The inclusive limit in bytes under which PIO will be used instead of DMA ++ ++ If unsure, say 2 here. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 6a7cfe0..9d41de9 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -18,6 +18,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o + obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o + obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o + obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o ++obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o +diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c +new file mode 100644 +index 0000000..28b00d3 +--- /dev/null ++++ b/drivers/mmc/host/bcm2835-mmc.c +@@ -0,0 +1,1506 @@ ++/* ++ * BCM2835 MMC host driver. ++ * ++ * Author: Gellert Weisz ++ * Copyright 2014 ++ * ++ * Based on ++ * sdhci-bcm2708.c by Broadcom ++ * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko ++ * sdhci.c and sdhci-pci.c by Pierre Ossman ++ * ++ * 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 it will be useful, but WITHOUT ++ * 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 . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sdhci.h" ++ ++ ++#define DRIVER_NAME "mmc-bcm2835" ++ ++#define DBG(f, x...) \ ++pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) ++ ++#ifndef CONFIG_MMC_BCM2835_DMA ++ #define FORCE_PIO ++#endif ++ ++ ++/* the inclusive limit in bytes under which PIO will be used instead of DMA */ ++#ifdef CONFIG_MMC_BCM2835_PIO_DMA_BARRIER ++#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_PIO_DMA_BARRIER ++#else ++#define PIO_DMA_BARRIER 00 ++#endif ++ ++#define MIN_FREQ 400000 ++#define TIMEOUT_VAL 0xE ++#define BCM2835_SDHCI_WRITE_DELAY(f) (((2 * 1000000) / f) + 1) ++ ++#ifndef BCM2708_PERI_BASE ++ #define BCM2708_PERI_BASE 0x20000000 ++#endif ++ ++/* FIXME: Needs IOMMU support */ ++#define BCM2835_VCMMU_SHIFT (0x7E000000 - BCM2708_PERI_BASE) ++ ++ ++struct bcm2835_host { ++ spinlock_t lock; ++ ++ void __iomem *ioaddr; ++ u32 phys_addr; ++ ++ struct mmc_host *mmc; ++ ++ u32 timeout; ++ ++ int clock; /* Current clock speed */ ++ u8 pwr; /* Current voltage */ ++ ++ unsigned int max_clk; /* Max possible freq */ ++ unsigned int timeout_clk; /* Timeout freq (KHz) */ ++ unsigned int clk_mul; /* Clock Muliplier value */ ++ ++ struct tasklet_struct finish_tasklet; /* Tasklet structures */ ++ ++ struct timer_list timer; /* Timer for timeouts */ ++ ++ struct sg_mapping_iter sg_miter; /* SG state for PIO */ ++ unsigned int blocks; /* remaining PIO blocks */ ++ ++ int irq; /* Device IRQ */ ++ ++ ++ u32 ier; /* cached registers */ ++ ++ struct mmc_request *mrq; /* Current request */ ++ struct mmc_command *cmd; /* Current command */ ++ struct mmc_data *data; /* Current data request */ ++ unsigned int data_early:1; /* Data finished before cmd */ ++ ++ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ ++ ++ u32 thread_isr; ++ ++ u32 shadow; ++ ++ /*DMA part*/ ++ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ ++ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ ++ struct dma_async_tx_descriptor *tx_desc; /* descriptor */ ++ ++ bool have_dma; ++ bool use_dma; ++ /*end of DMA part*/ ++ ++ int max_delay; /* maximum length of time spent waiting */ ++ ++ int flags; /* Host attributes */ ++#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ ++#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ ++#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ ++#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ ++#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ ++}; ++ ++ ++static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); ++} ++ ++static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg) ++{ ++ writel(val, host->ioaddr + reg); ++} ++ ++static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) ++{ ++ return readl(host->ioaddr + reg); ++} ++ ++static inline void bcm2835_mmc_writew(struct bcm2835_host *host, u16 val, int reg) ++{ ++ u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow : ++ bcm2835_mmc_readl(host, reg & ~3); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 mask = 0xffff << word_shift; ++ u32 newval = (oldval & ~mask) | (val << word_shift); ++ ++ if (reg == SDHCI_TRANSFER_MODE) ++ host->shadow = newval; ++ else ++ bcm2835_mmc_writel(host, newval, reg & ~3); ++ ++} ++ ++static inline void bcm2835_mmc_writeb(struct bcm2835_host *host, u8 val, int reg) ++{ ++ u32 oldval = bcm2835_mmc_readl(host, reg & ~3); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 mask = 0xff << byte_shift; ++ u32 newval = (oldval & ~mask) | (val << byte_shift); ++ ++ bcm2835_mmc_writel(host, newval, reg & ~3); ++} ++ ++ ++static inline u16 bcm2835_mmc_readw(struct bcm2835_host *host, int reg) ++{ ++ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); ++ u32 word_num = (reg >> 1) & 1; ++ u32 word_shift = word_num * 16; ++ u32 word = (val >> word_shift) & 0xffff; ++ ++ return word; ++} ++ ++static inline u8 bcm2835_mmc_readb(struct bcm2835_host *host, int reg) ++{ ++ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); ++ u32 byte_num = reg & 3; ++ u32 byte_shift = byte_num * 8; ++ u32 byte = (val >> byte_shift) & 0xff; ++ ++ return byte; ++} ++ ++static void bcm2835_mmc_unsignal_irqs(struct bcm2835_host *host, u32 clear) ++{ ++ u32 ier; ++ ++ ier = bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE); ++ ier &= ~clear; ++ /* change which requests generate IRQs - makes no difference to ++ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ ++ bcm2835_mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++ ++static void bcm2835_mmc_dumpregs(struct bcm2835_host *host) ++{ ++ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", ++ mmc_hostname(host->mmc)); ++ ++ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_DMA_ADDRESS), ++ bcm2835_mmc_readw(host, SDHCI_HOST_VERSION)); ++ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_BLOCK_SIZE), ++ bcm2835_mmc_readw(host, SDHCI_BLOCK_COUNT)); ++ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_ARGUMENT), ++ bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE)); ++ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE), ++ bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL)); ++ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_POWER_CONTROL), ++ bcm2835_mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL)); ++ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_WAKE_UP_CONTROL), ++ bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)); ++ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", ++ bcm2835_mmc_readb(host, SDHCI_TIMEOUT_CONTROL), ++ bcm2835_mmc_readl(host, SDHCI_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_INT_ENABLE), ++ bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE)); ++ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_ACMD12_ERR), ++ bcm2835_mmc_readw(host, SDHCI_SLOT_INT_STATUS)); ++ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", ++ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES), ++ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES_1)); ++ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_COMMAND), ++ bcm2835_mmc_readl(host, SDHCI_MAX_CURRENT)); ++ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", ++ bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2)); ++ ++ pr_debug(DRIVER_NAME ": ===========================================\n"); ++} ++ ++ ++static void bcm2835_mmc_reset(struct bcm2835_host *host, u8 mask) ++{ ++ unsigned long timeout; ++ ++ bcm2835_mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET); ++ ++ if (mask & SDHCI_RESET_ALL) ++ host->clock = 0; ++ ++ /* Wait max 100 ms */ ++ timeout = 100; ++ ++ /* hw clears the bit when it's done */ ++ while (bcm2835_mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Reset 0x%x never completed.\n", ++ mmc_hostname(host->mmc), (int)mask); ++ bcm2835_mmc_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (100-timeout > 10 && 100-timeout > host->max_delay) { ++ host->max_delay = 100-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++} ++ ++static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); ++ ++static void bcm2835_mmc_init(struct bcm2835_host *host, int soft) ++{ ++ if (soft) ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); ++ else ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | ++ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | ++ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | ++ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | ++ SDHCI_INT_RESPONSE; ++ ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ ++ if (soft) { ++ /* force clock reconfiguration */ ++ host->clock = 0; ++ bcm2835_mmc_set_ios(host->mmc, &host->mmc->ios); ++ } ++} ++ ++ ++ ++static void bcm2835_mmc_finish_data(struct bcm2835_host *host); ++ ++static void bcm2835_mmc_dma_complete(void *param) ++{ ++ struct bcm2835_host *host = param; ++ struct dma_chan *dma_chan; ++ unsigned long flags; ++ u32 dir_data; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { ++ /* otherwise handled in SDHCI IRQ */ ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_mmc_finish_data(host); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_bcm2835_mmc_read_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ ++ u32 uninitialized_var(scratch); ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ if (chunk == 0) { ++ scratch = bcm2835_mmc_readl(host, SDHCI_BUFFER); ++ chunk = 4; ++ } ++ ++ *buf = scratch & 0xFF; ++ ++ buf++; ++ scratch >>= 8; ++ chunk--; ++ len--; ++ } ++ } ++ ++ sg_miter_stop(&host->sg_miter); ++ ++ local_irq_restore(flags); ++} ++ ++static void bcm2835_bcm2835_mmc_write_block_pio(struct bcm2835_host *host) ++{ ++ unsigned long flags; ++ size_t blksize, len, chunk; ++ u32 scratch; ++ u8 *buf; ++ ++ blksize = host->data->blksz; ++ chunk = 0; ++ chunk = 0; ++ scratch = 0; ++ ++ local_irq_save(flags); ++ ++ while (blksize) { ++ if (!sg_miter_next(&host->sg_miter)) ++ BUG(); ++ ++ len = min(host->sg_miter.length, blksize); ++ ++ blksize -= len; ++ host->sg_miter.consumed = len; ++ ++ buf = host->sg_miter.addr; ++ ++ while (len) { ++ scratch |= (u32)*buf << (chunk * 8); ++ ++ buf++; ++ chunk++; ++ len--; ++ ++ if ((chunk == 4) || ((len == 0) && (blksize == 0))) { ++ mmc_raw_writel(host, scratch, SDHCI_BUFFER); ++ chunk = 0; ++ scratch = 0; + } + } + } + -+ if (!waiter) { -+ waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); -+ if (!waiter) { -+ vchiq_log_error(vchiq_core_log_level, -+ "%s - out of memory", __func__); -+ return VCHIQ_ERROR; -+ } -+ } ++ sg_miter_stop(&host->sg_miter); + -+ status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, -+ data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, -+ dir); -+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || -+ !waiter->bulk_waiter.bulk) { -+ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; -+ if (bulk) { -+ /* Cancel the signal when the transfer -+ ** completes. */ -+ spin_lock(&bulk_waiter_spinlock); -+ bulk->userdata = NULL; -+ spin_unlock(&bulk_waiter_spinlock); -+ } -+ kfree(waiter); ++ local_irq_restore(flags); ++} ++ ++ ++static void bcm2835_mmc_transfer_pio(struct bcm2835_host *host) ++{ ++ u32 mask; ++ ++ BUG_ON(!host->data); ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) ++ mask = SDHCI_DATA_AVAILABLE; ++ else ++ mask = SDHCI_SPACE_AVAILABLE; ++ ++ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ ++ if (host->data->flags & MMC_DATA_READ) ++ bcm2835_bcm2835_mmc_read_block_pio(host); ++ else ++ bcm2835_bcm2835_mmc_write_block_pio(host); ++ ++ host->blocks--; ++ ++ /* QUIRK used in sdhci.c removes the 'if' */ ++ /* but it seems this is unnecessary */ ++ if (host->blocks == 0) ++ break; ++ ++ ++ } ++} ++ ++ ++static void bcm2835_mmc_transfer_dma(struct bcm2835_host *host) ++{ ++ u32 len, dir_data, dir_slave; ++ struct dma_async_tx_descriptor *desc = NULL; ++ struct dma_chan *dma_chan; ++ ++ ++ WARN_ON(!host->data); ++ ++ if (!host->data) ++ return; ++ ++ if (host->blocks == 0) ++ return; ++ ++ if (host->data->flags & MMC_DATA_READ) { ++ dma_chan = host->dma_chan_rx; ++ dir_data = DMA_FROM_DEVICE; ++ dir_slave = DMA_DEV_TO_MEM; + } else { -+ waiter->pid = current->pid; -+ mutex_lock(&instance->bulk_waiter_list_mutex); -+ list_add(&waiter->list, &instance->bulk_waiter_list); -+ mutex_unlock(&instance->bulk_waiter_list_mutex); -+ vchiq_log_info(vchiq_arm_log_level, -+ "saved bulk_waiter %x for pid %d", -+ (unsigned int)waiter, current->pid); ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dir_slave = DMA_MEM_TO_DEV; ++ } ++ ++ BUG_ON(!dma_chan->device); ++ BUG_ON(!dma_chan->device->dev); ++ BUG_ON(!host->data->sg); ++ ++ len = dma_map_sg(dma_chan->device->dev, host->data->sg, ++ host->data->sg_len, dir_data); ++ if (len > 0) { ++ desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg, ++ len, dir_slave, ++ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); ++ } else { ++ dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); ++ } ++ if (desc) { ++ bcm2835_mmc_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ host->tx_desc = desc; ++ desc->callback = bcm2835_mmc_dma_complete; ++ desc->callback_param = host; ++ dmaengine_submit(desc); ++ dma_async_issue_pending(dma_chan); + } + -+ return status; +} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h -new file mode 100644 -index 0000000..d02e776 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h -@@ -0,0 +1,71 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ + -+#ifndef VCHIQ_MEMDRV_H -+#define VCHIQ_MEMDRV_H + -+/* ---- Include Files ----------------------------------------------------- */ + -+#include -+#include "vchiq_if.h" ++static void bcm2835_mmc_set_transfer_irqs(struct bcm2835_host *host) ++{ ++ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; ++ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; + -+/* ---- Constants and Types ---------------------------------------------- */ ++ if (host->use_dma) ++ host->ier = (host->ier & ~pio_irqs) | dma_irqs; ++ else ++ host->ier = (host->ier & ~dma_irqs) | pio_irqs; + -+typedef struct { -+ void *armSharedMemVirt; -+ dma_addr_t armSharedMemPhys; -+ size_t armSharedMemSize; ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++} + -+ void *vcSharedMemVirt; -+ dma_addr_t vcSharedMemPhys; -+ size_t vcSharedMemSize; -+} VCHIQ_SHARED_MEM_INFO_T; + -+/* ---- Variable Externs ------------------------------------------------- */ ++static void bcm2835_mmc_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ u8 count; ++ struct mmc_data *data = cmd->data; + -+/* ---- Function Prototypes ---------------------------------------------- */ ++ WARN_ON(host->data); + -+void vchiq_get_shared_mem_info(VCHIQ_SHARED_MEM_INFO_T *info); ++ if (data || (cmd->flags & MMC_RSP_BUSY)) { ++ count = TIMEOUT_VAL; ++ bcm2835_mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL); ++ } + -+VCHIQ_STATUS_T vchiq_memdrv_initialise(void); ++ if (!data) ++ return; + -+VCHIQ_STATUS_T vchiq_userdrv_create_instance( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ /* Sanity checks */ ++ BUG_ON(data->blksz * data->blocks > 524288); ++ BUG_ON(data->blksz > host->mmc->max_blk_size); ++ BUG_ON(data->blocks > 65535); + -+VCHIQ_STATUS_T vchiq_userdrv_suspend( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ host->data = data; ++ host->data_early = 0; ++ host->data->bytes_xfered = 0; + -+VCHIQ_STATUS_T vchiq_userdrv_resume( -+ const VCHIQ_PLATFORM_DATA_T * platform_data); + ++ if (!(host->flags & SDHCI_REQ_USE_DMA)) { ++ int flags; ++ ++ flags = SG_MITER_ATOMIC; ++ if (host->data->flags & MMC_DATA_READ) ++ flags |= SG_MITER_TO_SG; ++ else ++ flags |= SG_MITER_FROM_SG; ++ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); ++ host->blocks = data->blocks; ++ } ++ ++ host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER; ++ ++ bcm2835_mmc_set_transfer_irqs(host); ++ ++ /* Set the DMA boundary value and block size */ ++ bcm2835_mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, ++ data->blksz), SDHCI_BLOCK_SIZE); ++ bcm2835_mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT); ++ ++ BUG_ON(!host->data); ++} ++ ++static void bcm2835_mmc_set_transfer_mode(struct bcm2835_host *host, ++ struct mmc_command *cmd) ++{ ++ u16 mode; ++ struct mmc_data *data = cmd->data; ++ ++ if (data == NULL) { ++ /* clear Auto CMD settings for no data CMDs */ ++ mode = bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE); ++ bcm2835_mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | ++ SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); ++ return; ++ } ++ ++ WARN_ON(!host->data); ++ ++ mode = SDHCI_TRNS_BLK_CNT_EN; ++ ++ if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) { ++ mode |= SDHCI_TRNS_MULTI; ++ ++ /* ++ * If we are sending CMD23, CMD12 never gets sent ++ * on successful completion (so no Auto-CMD12). ++ */ ++ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) ++ mode |= SDHCI_TRNS_AUTO_CMD12; ++ else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { ++ mode |= SDHCI_TRNS_AUTO_CMD23; ++ bcm2835_mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); ++ } ++ } ++ ++ if (data->flags & MMC_DATA_READ) ++ mode |= SDHCI_TRNS_READ; ++ if (host->flags & SDHCI_REQ_USE_DMA) ++ mode |= SDHCI_TRNS_DMA; ++ ++ bcm2835_mmc_writew(host, mode, SDHCI_TRANSFER_MODE); ++} ++ ++void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd) ++{ ++ int flags; ++ u32 mask; ++ unsigned long timeout; ++ ++ WARN_ON(host->cmd); ++ ++ /* Wait max 10 ms */ ++ timeout = 1000; ++ ++ mask = SDHCI_CMD_INHIBIT; ++ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) ++ mask |= SDHCI_DATA_INHIBIT; ++ ++ /* We shouldn't wait for data inihibit for stop commands, even ++ though they might use busy signaling */ ++ if (host->mrq->data && (cmd == host->mrq->data->stop)) ++ mask &= ~SDHCI_DATA_INHIBIT; ++ ++ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ if (timeout == 0) { ++ pr_err("%s: Controller never released inhibit bit(s).\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ cmd->error = -EIO; ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ timeout--; ++ udelay(10); ++ } ++ ++ if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) { ++ host->max_delay = (1000-timeout)/100; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ timeout = jiffies; ++#ifdef CONFIG_ARCH_BCM2835 ++ if (!cmd->data && cmd->busy_timeout > 9000) ++ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; ++ else +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h -new file mode 100644 -index 0000000..54a3ece ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h -@@ -0,0 +1,58 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ ++ timeout += 10 * HZ; ++ mod_timer(&host->timer, timeout); + -+#ifndef VCHIQ_PAGELIST_H -+#define VCHIQ_PAGELIST_H ++ host->cmd = cmd; + -+#ifndef PAGE_SIZE -+#define PAGE_SIZE 4096 -+#endif -+#define CACHE_LINE_SIZE 32 -+#define PAGELIST_WRITE 0 -+#define PAGELIST_READ 1 -+#define PAGELIST_READ_WITH_FRAGMENTS 2 ++ bcm2835_mmc_prepare_data(host, cmd); + -+typedef struct pagelist_struct { -+ unsigned long length; -+ unsigned short type; -+ unsigned short offset; -+ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following -+ pages at consecutive addresses. */ -+} PAGELIST_T; ++ bcm2835_mmc_writel(host, cmd->arg, SDHCI_ARGUMENT); + -+typedef struct fragments_struct { -+ char headbuf[CACHE_LINE_SIZE]; -+ char tailbuf[CACHE_LINE_SIZE]; -+} FRAGMENTS_T; ++ bcm2835_mmc_set_transfer_mode(host, cmd); + -+#endif /* VCHIQ_PAGELIST_H */ -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -new file mode 100644 -index 0000000..72eacdaf ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -0,0 +1,853 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include -+#include -+ -+#include "interface/vchi/vchi.h" -+#include "vchiq.h" -+#include "vchiq_core.h" -+ -+#include "vchiq_util.h" -+ -+#include -+ -+#define vchiq_status_to_vchi(status) ((int32_t)status) -+ -+typedef struct { -+ VCHIQ_SERVICE_HANDLE_T handle; -+ -+ VCHIU_QUEUE_T queue; -+ -+ VCHI_CALLBACK_T callback; -+ void *callback_param; -+} SHIM_SERVICE_T; -+ -+/* ---------------------------------------------------------------------- -+ * return pointer to the mphi message driver function table -+ * -------------------------------------------------------------------- */ -+const VCHI_MESSAGE_DRIVER_T * -+vchi_mphi_message_driver_func_table(void) -+{ -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------------- -+ * return a pointer to the 'single' connection driver fops -+ * -------------------------------------------------------------------- */ -+const VCHI_CONNECTION_API_T * -+single_get_func_table(void) -+{ -+ return NULL; -+} -+ -+VCHI_CONNECTION_T *vchi_create_connection( -+ const VCHI_CONNECTION_API_T *function_table, -+ const VCHI_MESSAGE_DRIVER_T *low_level) -+{ -+ (void)function_table; -+ (void)low_level; -+ return NULL; -+} -+ -+/*********************************************************** -+ * Name: vchi_msg_peek -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle, -+ * void **data, -+ * uint32_t *msg_size, -+ -+ -+ * VCHI_FLAGS_T flags -+ * -+ * Description: Routine to return a pointer to the current message (to allow in -+ * place processing). The message can be removed using -+ * vchi_msg_remove when you're finished -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_peek(VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_peek(&service->queue); -+ -+ *data = header->data; -+ *msg_size = header->size; -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_peek); -+ -+/*********************************************************** -+ * Name: vchi_msg_remove -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle, -+ * -+ * Description: Routine to remove a message (after it has been read with -+ * vchi_msg_peek) -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_remove(VCHI_SERVICE_HANDLE_T handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ vchiq_release_message(service->handle, header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_remove); -+ -+/*********************************************************** -+ * Name: vchi_msg_queue -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * const void *data, -+ * uint32_t data_size, -+ * VCHI_FLAGS_T flags, -+ * void *msg_handle, -+ * -+ * Description: Thin wrapper to queue a message onto a connection -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, -+ const void *data, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *msg_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_ELEMENT_T element = {data, data_size}; -+ VCHIQ_STATUS_T status; -+ -+ (void)msg_handle; -+ -+ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); -+ -+ status = vchiq_queue_message(service->handle, &element, 1); -+ -+ /* vchiq_queue_message() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_queue_message(service->handle, &element, 1); ++ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { ++ pr_err("%s: Unsupported response type!\n", ++ mmc_hostname(host->mmc)); ++ cmd->error = -EINVAL; ++ tasklet_schedule(&host->finish_tasklet); ++ return; + } + -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_msg_queue); ++ if (!(cmd->flags & MMC_RSP_PRESENT)) ++ flags = SDHCI_CMD_RESP_NONE; ++ else if (cmd->flags & MMC_RSP_136) ++ flags = SDHCI_CMD_RESP_LONG; ++ else if (cmd->flags & MMC_RSP_BUSY) ++ flags = SDHCI_CMD_RESP_SHORT_BUSY; ++ else ++ flags = SDHCI_CMD_RESP_SHORT; + -+/*********************************************************** -+ * Name: vchi_bulk_queue_receive -+ * -+ * Arguments: VCHI_BULK_HANDLE_T handle, -+ * void *data_dst, -+ * const uint32_t data_size, -+ * VCHI_FLAGS_T flags -+ * void *bulk_handle -+ * -+ * Description: Routine to setup a rcv buffer -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_bulk_queue_receive(VCHI_SERVICE_HANDLE_T handle, -+ void *data_dst, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_BULK_MODE_T mode; -+ VCHIQ_STATUS_T status; ++ if (cmd->flags & MMC_RSP_CRC) ++ flags |= SDHCI_CMD_CRC; ++ if (cmd->flags & MMC_RSP_OPCODE) ++ flags |= SDHCI_CMD_INDEX; + -+ switch ((int)flags) { -+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE -+ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ WARN_ON(!service->callback); -+ mode = VCHIQ_BULK_MODE_CALLBACK; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: -+ mode = VCHIQ_BULK_MODE_BLOCKING; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ case VCHI_FLAGS_NONE: -+ mode = VCHIQ_BULK_MODE_NOCALLBACK; -+ break; -+ default: -+ WARN(1, "unsupported message\n"); -+ return vchiq_status_to_vchi(VCHIQ_ERROR); -+ } ++ if (cmd->data) ++ flags |= SDHCI_CMD_DATA; + -+ status = vchiq_bulk_receive(service->handle, data_dst, data_size, -+ bulk_handle, mode); -+ -+ /* vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_bulk_receive(service->handle, data_dst, -+ data_size, bulk_handle, mode); -+ } -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_bulk_queue_receive); -+ -+/*********************************************************** -+ * Name: vchi_bulk_queue_transmit -+ * -+ * Arguments: VCHI_BULK_HANDLE_T handle, -+ * const void *data_src, -+ * uint32_t data_size, -+ * VCHI_FLAGS_T flags, -+ * void *bulk_handle -+ * -+ * Description: Routine to transmit some data -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_bulk_queue_transmit(VCHI_SERVICE_HANDLE_T handle, -+ const void *data_src, -+ uint32_t data_size, -+ VCHI_FLAGS_T flags, -+ void *bulk_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_BULK_MODE_T mode; -+ VCHIQ_STATUS_T status; -+ -+ switch ((int)flags) { -+ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE -+ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ WARN_ON(!service->callback); -+ mode = VCHIQ_BULK_MODE_CALLBACK; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: -+ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: -+ mode = VCHIQ_BULK_MODE_BLOCKING; -+ break; -+ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: -+ case VCHI_FLAGS_NONE: -+ mode = VCHIQ_BULK_MODE_NOCALLBACK; -+ break; -+ default: -+ WARN(1, "unsupported message\n"); -+ return vchiq_status_to_vchi(VCHIQ_ERROR); -+ } -+ -+ status = vchiq_bulk_transmit(service->handle, data_src, data_size, -+ bulk_handle, mode); -+ -+ /* vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to -+ ** implement a retry mechanism since this function is supposed -+ ** to block until queued -+ */ -+ while (status == VCHIQ_RETRY) { -+ msleep(1); -+ status = vchiq_bulk_transmit(service->handle, data_src, -+ data_size, bulk_handle, mode); -+ } -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_bulk_queue_transmit); -+ -+/*********************************************************** -+ * Name: vchi_msg_dequeue -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * void *data, -+ * uint32_t max_data_size_to_read, -+ * uint32_t *actual_msg_size -+ * VCHI_FLAGS_T flags -+ * -+ * Description: Routine to dequeue a message into the supplied buffer -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_dequeue(VCHI_SERVICE_HANDLE_T handle, -+ void *data, -+ uint32_t max_data_size_to_read, -+ uint32_t *actual_msg_size, -+ VCHI_FLAGS_T flags) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ memcpy(data, header->data, header->size < max_data_size_to_read ? -+ header->size : max_data_size_to_read); -+ -+ *actual_msg_size = header->size; -+ -+ vchiq_release_message(service->handle, header); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_dequeue); -+ -+/*********************************************************** -+ * Name: vchi_msg_queuev -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * VCHI_MSG_VECTOR_T *vector, -+ * uint32_t count, -+ * VCHI_FLAGS_T flags, -+ * void *msg_handle -+ * -+ * Description: Thin wrapper to queue a message onto a connection -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+ -+vchiq_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); -+vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == -+ offsetof(VCHIQ_ELEMENT_T, data)); -+vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == -+ offsetof(VCHIQ_ELEMENT_T, size)); -+ -+int32_t vchi_msg_queuev(VCHI_SERVICE_HANDLE_T handle, -+ VCHI_MSG_VECTOR_T *vector, -+ uint32_t count, -+ VCHI_FLAGS_T flags, -+ void *msg_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ -+ (void)msg_handle; -+ -+ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); -+ -+ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, -+ (const VCHIQ_ELEMENT_T *)vector, count)); -+} -+EXPORT_SYMBOL(vchi_msg_queuev); -+ -+/*********************************************************** -+ * Name: vchi_held_msg_release -+ * -+ * Arguments: VCHI_HELD_MSG_T *message -+ * -+ * Description: Routine to release a held message (after it has been read with -+ * vchi_msg_hold) -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) -+{ -+ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, -+ (VCHIQ_HEADER_T *)message->message); -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_held_msg_release); -+ -+/*********************************************************** -+ * Name: vchi_msg_hold -+ * -+ * Arguments: VCHI_SERVICE_HANDLE_T handle, -+ * void **data, -+ * uint32_t *msg_size, -+ * VCHI_FLAGS_T flags, -+ * VCHI_HELD_MSG_T *message_handle -+ * -+ * Description: Routine to return a pointer to the current message (to allow -+ * in place processing). The message is dequeued - don't forget -+ * to release the message using vchi_held_msg_release when you're -+ * finished. -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, -+ void **data, -+ uint32_t *msg_size, -+ VCHI_FLAGS_T flags, -+ VCHI_HELD_MSG_T *message_handle) -+{ -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_HEADER_T *header; -+ -+ WARN_ON((flags != VCHI_FLAGS_NONE) && -+ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); -+ -+ if (flags == VCHI_FLAGS_NONE) -+ if (vchiu_queue_is_empty(&service->queue)) -+ return -1; -+ -+ header = vchiu_queue_pop(&service->queue); -+ -+ *data = header->data; -+ *msg_size = header->size; -+ -+ message_handle->service = -+ (struct opaque_vchi_service_t *)service->handle; -+ message_handle->message = header; -+ -+ return 0; -+} -+EXPORT_SYMBOL(vchi_msg_hold); -+ -+/*********************************************************** -+ * Name: vchi_initialise -+ * -+ * Arguments: VCHI_INSTANCE_T *instance_handle -+ * -+ * Description: Initialises the hardware but does not transmit anything -+ * When run as a Host App this will be called twice hence the need -+ * to malloc the state information -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+ -+int32_t vchi_initialise(VCHI_INSTANCE_T *instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance; -+ VCHIQ_STATUS_T status; -+ -+ status = vchiq_initialise(&instance); -+ -+ *instance_handle = (VCHI_INSTANCE_T)instance; -+ -+ return vchiq_status_to_vchi(status); -+} -+EXPORT_SYMBOL(vchi_initialise); -+ -+/*********************************************************** -+ * Name: vchi_connect -+ * -+ * Arguments: VCHI_CONNECTION_T **connections -+ * const uint32_t num_connections -+ * VCHI_INSTANCE_T instance_handle) -+ * -+ * Description: Starts the command service on each connection, -+ * causing INIT messages to be pinged back and forth -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+int32_t vchi_connect(VCHI_CONNECTION_T **connections, -+ const uint32_t num_connections, -+ VCHI_INSTANCE_T instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ -+ (void)connections; -+ (void)num_connections; -+ -+ return vchiq_connect(instance); -+} -+EXPORT_SYMBOL(vchi_connect); -+ -+ -+/*********************************************************** -+ * Name: vchi_disconnect -+ * -+ * Arguments: VCHI_INSTANCE_T instance_handle -+ * -+ * Description: Stops the command service on each connection, -+ * causing DE-INIT messages to be pinged back and forth -+ * -+ * Returns: 0 if successful, failure otherwise -+ * -+ ***********************************************************/ -+int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) -+{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ return vchiq_status_to_vchi(vchiq_shutdown(instance)); -+} -+EXPORT_SYMBOL(vchi_disconnect); -+ -+ -+/*********************************************************** -+ * Name: vchi_service_open -+ * Name: vchi_service_create -+ * -+ * Arguments: VCHI_INSTANCE_T *instance_handle -+ * SERVICE_CREATION_T *setup, -+ * VCHI_SERVICE_HANDLE_T *handle -+ * -+ * Description: Routine to open a service -+ * -+ * Returns: int32_t - success == 0 -+ * -+ ***********************************************************/ -+ -+static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, -+ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) -+{ -+ SHIM_SERVICE_T *service = -+ (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); -+ -+ if (!service->callback) -+ goto release; -+ -+ switch (reason) { -+ case VCHIQ_MESSAGE_AVAILABLE: -+ vchiu_queue_push(&service->queue, header); -+ -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_MSG_AVAILABLE, NULL); -+ -+ goto done; -+ break; -+ -+ case VCHIQ_BULK_TRANSMIT_DONE: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_SENT, bulk_user); -+ break; -+ -+ case VCHIQ_BULK_RECEIVE_DONE: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_RECEIVED, bulk_user); -+ break; -+ -+ case VCHIQ_SERVICE_CLOSED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_SERVICE_CLOSED, NULL); -+ break; -+ -+ case VCHIQ_SERVICE_OPENED: -+ /* No equivalent VCHI reason */ -+ break; -+ -+ case VCHIQ_BULK_TRANSMIT_ABORTED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, -+ bulk_user); -+ break; -+ -+ case VCHIQ_BULK_RECEIVE_ABORTED: -+ service->callback(service->callback_param, -+ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, -+ bulk_user); -+ break; -+ -+ default: -+ WARN(1, "not supported\n"); -+ break; -+ } -+ -+release: -+ vchiq_release_message(service->handle, header); -+done: -+ return VCHIQ_SUCCESS; ++ bcm2835_mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); +} + -+static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, -+ SERVICE_CREATION_T *setup) ++ ++static void bcm2835_mmc_finish_data(struct bcm2835_host *host) +{ -+ SHIM_SERVICE_T *service = kzalloc(sizeof(SHIM_SERVICE_T), GFP_KERNEL); ++ struct mmc_data *data; + -+ (void)instance; ++ BUG_ON(!host->data); + -+ if (service) { -+ if (vchiu_queue_init(&service->queue, 64)) { -+ service->callback = setup->callback; -+ service->callback_param = setup->callback_param; ++ data = host->data; ++ host->data = NULL; ++ ++ if (data->error) ++ data->bytes_xfered = 0; ++ else ++ data->bytes_xfered = data->blksz * data->blocks; ++ ++ /* ++ * Need to send CMD12 if - ++ * a) open-ended multiblock transfer (no CMD23) ++ * b) error in multiblock transfer ++ */ ++ if (data->stop && ++ (data->error || ++ !host->mrq->sbc)) { ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (data->error) { ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); ++ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ bcm2835_mmc_send_command(host, data->stop); ++ } else ++ tasklet_schedule(&host->finish_tasklet); ++} ++ ++static void bcm2835_mmc_finish_command(struct bcm2835_host *host) ++{ ++ int i; ++ ++ BUG_ON(host->cmd == NULL); ++ ++ if (host->cmd->flags & MMC_RSP_PRESENT) { ++ if (host->cmd->flags & MMC_RSP_136) { ++ /* CRC is stripped so we need to do some shifting. */ ++ for (i = 0; i < 4; i++) { ++ host->cmd->resp[i] = bcm2835_mmc_readl(host, ++ SDHCI_RESPONSE + (3-i)*4) << 8; ++ if (i != 3) ++ host->cmd->resp[i] |= ++ bcm2835_mmc_readb(host, ++ SDHCI_RESPONSE + (3-i)*4-1); ++ } + } else { -+ kfree(service); -+ service = NULL; ++ host->cmd->resp[0] = bcm2835_mmc_readl(host, SDHCI_RESPONSE); + } + } + -+ return service; -+} ++ host->cmd->error = 0; + -+static void service_free(SHIM_SERVICE_T *service) -+{ -+ if (service) { -+ vchiu_queue_delete(&service->queue); -+ kfree(service); ++ /* Finished CMD23, now send actual command. */ ++ if (host->cmd == host->mrq->sbc) { ++ host->cmd = NULL; ++ bcm2835_mmc_send_command(host, host->mrq->cmd); ++ ++ if (host->mrq->cmd->data && host->use_dma) { ++ /* DMA transfer starts now, PIO starts after interrupt */ ++ bcm2835_mmc_transfer_dma(host); ++ } ++ } else { ++ ++ /* Processed actual command. */ ++ if (host->data && host->data_early) ++ bcm2835_mmc_finish_data(host); ++ ++ if (!host->cmd->data) ++ tasklet_schedule(&host->finish_tasklet); ++ ++ host->cmd = NULL; + } +} + -+int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle) ++ ++static void bcm2835_mmc_timeout_timer(unsigned long data) +{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ if (service) { -+ VCHIQ_SERVICE_PARAMS_T params; -+ VCHIQ_STATUS_T status; ++ struct bcm2835_host *host; ++ unsigned long flags; + -+ memset(¶ms, 0, sizeof(params)); -+ params.fourcc = setup->service_id; -+ params.callback = shim_callback; -+ params.userdata = service; -+ params.version = setup->version.version; -+ params.version_min = setup->version.version_min; ++ host = (struct bcm2835_host *)data; + -+ status = vchiq_open_service(instance, ¶ms, -+ &service->handle); -+ if (status != VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host->mrq) { ++ pr_err("%s: Timeout waiting for hardware interrupt.\n", ++ mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ ++ if (host->data) { ++ host->data->error = -ETIMEDOUT; ++ bcm2835_mmc_finish_data(host); ++ } else { ++ if (host->cmd) ++ host->cmd->error = -ETIMEDOUT; ++ else ++ host->mrq->cmd->error = -ETIMEDOUT; ++ ++ tasklet_schedule(&host->finish_tasklet); + } + } + -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ -+ return (service != NULL) ? 0 : -1; ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); +} -+EXPORT_SYMBOL(vchi_service_open); + -+int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, -+ SERVICE_CREATION_T *setup, -+ VCHI_SERVICE_HANDLE_T *handle) ++ ++static void bcm2835_mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) +{ -+ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; -+ SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ if (service) { -+ VCHIQ_SERVICE_PARAMS_T params; -+ VCHIQ_STATUS_T status; ++ if (!(host->flags & SDHCI_DEVICE_DEAD)) { ++ if (enable) ++ host->ier |= SDHCI_INT_CARD_INT; ++ else ++ host->ier &= ~SDHCI_INT_CARD_INT; + -+ memset(¶ms, 0, sizeof(params)); -+ params.fourcc = setup->service_id; -+ params.callback = shim_callback; -+ params.userdata = service; -+ params.version = setup->version.version; -+ params.version_min = setup->version.version_min; -+ status = vchiq_add_service(instance, ¶ms, &service->handle); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); ++ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); ++ mmiowb(); ++ } ++} + -+ if (status != VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; -+ } ++static void bcm2835_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (enable) ++ host->flags |= SDHCI_SDIO_IRQ_ENABLED; ++ else ++ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; ++ ++ bcm2835_mmc_enable_sdio_irq_nolock(host, enable); ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static void bcm2835_mmc_cmd_irq(struct bcm2835_host *host, u32 intmask) ++{ ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->cmd) { ++ pr_err("%s: Got command interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_mmc_dumpregs(host); ++ return; + } + -+ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ if (intmask & SDHCI_INT_TIMEOUT) ++ host->cmd->error = -ETIMEDOUT; ++ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | ++ SDHCI_INT_INDEX)) { ++ host->cmd->error = -EILSEQ; ++ } ++ ++ if (host->cmd->error) { ++ tasklet_schedule(&host->finish_tasklet); ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_RESPONSE) ++ bcm2835_mmc_finish_command(host); + -+ return (service != NULL) ? 0 : -1; +} -+EXPORT_SYMBOL(vchi_service_create); + -+int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) ++static void bcm2835_mmc_data_irq(struct bcm2835_host *host, u32 intmask) +{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) { -+ VCHIQ_STATUS_T status = vchiq_close_service(service->handle); -+ if (status == VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ struct dma_chan *dma_chan; ++ u32 dir_data; ++ ++ BUG_ON(intmask == 0); ++ ++ if (!host->data) { ++ /* ++ * The "data complete" interrupt is also used to ++ * indicate that a busy state has ended. See comment ++ * above in sdhci_cmd_irq(). ++ */ ++ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { ++ if (intmask & SDHCI_INT_DATA_END) { ++ bcm2835_mmc_finish_command(host); ++ return; ++ } + } + -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_close); ++ pr_debug("%s: Got data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ bcm2835_mmc_dumpregs(host); + -+int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) { -+ VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); -+ if (status == VCHIQ_SUCCESS) { -+ service_free(service); -+ service = NULL; ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_DATA_TIMEOUT) ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & SDHCI_INT_DATA_END_BIT) ++ host->data->error = -EILSEQ; ++ else if ((intmask & SDHCI_INT_DATA_CRC) && ++ SDHCI_GET_CMD(bcm2835_mmc_readw(host, SDHCI_COMMAND)) ++ != MMC_BUS_TEST_R) ++ host->data->error = -EILSEQ; ++ ++ if (host->use_dma) { ++ if (host->data->flags & MMC_DATA_WRITE) { ++ /* IRQ handled here */ ++ ++ dma_chan = host->dma_chan_tx; ++ dir_data = DMA_TO_DEVICE; ++ dma_unmap_sg(dma_chan->device->dev, ++ host->data->sg, host->data->sg_len, ++ dir_data); ++ ++ bcm2835_mmc_finish_data(host); + } + -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_destroy); ++ } else { ++ if (host->data->error) ++ bcm2835_mmc_finish_data(host); ++ else { ++ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) ++ bcm2835_mmc_transfer_pio(host); + -+int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, -+ VCHI_SERVICE_OPTION_T option, -+ int value) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ VCHIQ_SERVICE_OPTION_T vchiq_option; -+ switch (option) { -+ case VCHI_SERVICE_OPTION_TRACE: -+ vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; -+ break; -+ default: -+ service = NULL; -+ break; -+ } -+ if (service) { -+ VCHIQ_STATUS_T status = -+ vchiq_set_service_option(service->handle, -+ vchiq_option, -+ value); -+ -+ ret = vchiq_status_to_vchi(status); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_set_option); -+ -+int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if(service) -+ { -+ VCHIQ_STATUS_T status = vchiq_get_peer_version(service->handle, peer_version); -+ ret = vchiq_status_to_vchi( status ); -+ } -+ return ret; -+} -+EXPORT_SYMBOL(vchi_get_peer_version); -+ -+/* ---------------------------------------------------------------------- -+ * read a uint32_t from buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+uint32_t -+vchi_readbuf_uint32(const void *_ptr) -+{ -+ const unsigned char *ptr = _ptr; -+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); -+} -+ -+/* ---------------------------------------------------------------------- -+ * write a uint32_t to buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+void -+vchi_writebuf_uint32(void *_ptr, uint32_t value) -+{ -+ unsigned char *ptr = _ptr; -+ ptr[0] = (unsigned char)((value >> 0) & 0xFF); -+ ptr[1] = (unsigned char)((value >> 8) & 0xFF); -+ ptr[2] = (unsigned char)((value >> 16) & 0xFF); -+ ptr[3] = (unsigned char)((value >> 24) & 0xFF); -+} -+ -+/* ---------------------------------------------------------------------- -+ * read a uint16_t from buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+uint16_t -+vchi_readbuf_uint16(const void *_ptr) -+{ -+ const unsigned char *ptr = _ptr; -+ return ptr[0] | (ptr[1] << 8); -+} -+ -+/* ---------------------------------------------------------------------- -+ * write a uint16_t into the buffer. -+ * network format is defined to be little endian -+ * -------------------------------------------------------------------- */ -+void -+vchi_writebuf_uint16(void *_ptr, uint16_t value) -+{ -+ unsigned char *ptr = _ptr; -+ ptr[0] = (value >> 0) & 0xFF; -+ ptr[1] = (value >> 8) & 0xFF; -+} -+ -+/*********************************************************** -+ * Name: vchi_service_use -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle -+ * -+ * Description: Routine to increment refcount on a service -+ * -+ * Returns: void -+ * -+ ***********************************************************/ -+int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) -+ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_use); -+ -+/*********************************************************** -+ * Name: vchi_service_release -+ * -+ * Arguments: const VCHI_SERVICE_HANDLE_T handle -+ * -+ * Description: Routine to decrement refcount on a service -+ * -+ * Returns: void -+ * -+ ***********************************************************/ -+int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) -+{ -+ int32_t ret = -1; -+ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; -+ if (service) -+ ret = vchiq_status_to_vchi( -+ vchiq_release_service(service->handle)); -+ return ret; -+} -+EXPORT_SYMBOL(vchi_service_release); -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -new file mode 100644 -index 0000000..c2eefef ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -@@ -0,0 +1,151 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "vchiq_util.h" -+ -+static inline int is_pow2(int i) -+{ -+ return i && !(i & (i - 1)); -+} -+ -+int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) -+{ -+ WARN_ON(!is_pow2(size)); -+ -+ queue->size = size; -+ queue->read = 0; -+ queue->write = 0; -+ -+ sema_init(&queue->pop, 0); -+ sema_init(&queue->push, 0); -+ -+ queue->storage = kzalloc(size * sizeof(VCHIQ_HEADER_T *), GFP_KERNEL); -+ if (queue->storage == NULL) { -+ vchiu_queue_delete(queue); -+ return 0; -+ } -+ return 1; -+} -+ -+void vchiu_queue_delete(VCHIU_QUEUE_T *queue) -+{ -+ if (queue->storage != NULL) -+ kfree(queue->storage); -+} -+ -+int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) -+{ -+ return queue->read == queue->write; -+} -+ -+int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) -+{ -+ return queue->write == queue->read + queue->size; -+} -+ -+void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) -+{ -+ while (queue->write == queue->read + queue->size) { -+ if (down_interruptible(&queue->pop) != 0) { -+ flush_signals(current); ++ if (intmask & SDHCI_INT_DATA_END) { ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ host->data_early = 1; ++ } else { ++ bcm2835_mmc_finish_data(host); ++ } ++ } + } + } -+ -+ /* -+ * Write to queue->storage must be visible after read from -+ * queue->read -+ */ -+ smp_mb(); -+ -+ queue->storage[queue->write & (queue->size - 1)] = header; -+ -+ /* -+ * Write to queue->storage must be visible before write to -+ * queue->write -+ */ -+ smp_wmb(); -+ -+ queue->write++; -+ -+ up(&queue->push); +} + -+VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) ++ ++static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) +{ -+ while (queue->write == queue->read) { -+ if (down_interruptible(&queue->push) != 0) { -+ flush_signals(current); -+ } -+ } -+ -+ up(&queue->push); // We haven't removed anything from the queue. -+ -+ /* -+ * Read from queue->storage must be visible after read from -+ * queue->write -+ */ -+ smp_rmb(); -+ -+ return queue->storage[queue->read & (queue->size - 1)]; -+} -+ -+VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) -+{ -+ VCHIQ_HEADER_T *header; -+ -+ while (queue->write == queue->read) { -+ if (down_interruptible(&queue->push) != 0) { -+ flush_signals(current); -+ } -+ } -+ -+ /* -+ * Read from queue->storage must be visible after read from -+ * queue->write -+ */ -+ smp_rmb(); -+ -+ header = queue->storage[queue->read & (queue->size - 1)]; -+ -+ /* -+ * Read from queue->storage must be visible before write to -+ * queue->read -+ */ -+ smp_mb(); -+ -+ queue->read++; -+ -+ up(&queue->pop); -+ -+ return header; -+} -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h -new file mode 100644 -index 0000000..f4d0b66 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h -@@ -0,0 +1,81 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_UTIL_H -+#define VCHIQ_UTIL_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include /* for time_t */ -+#include -+#include -+ -+#include "vchiq_if.h" -+ -+typedef struct { -+ int size; -+ int read; -+ int write; -+ -+ struct semaphore pop; -+ struct semaphore push; -+ -+ VCHIQ_HEADER_T **storage; -+} VCHIU_QUEUE_T; -+ -+extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); -+extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); -+ -+extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); -+extern int vchiu_queue_is_full(VCHIU_QUEUE_T *queue); -+ -+extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); -+ -+extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); -+extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); -+ ++ irqreturn_t result = IRQ_NONE; ++ struct bcm2835_host *host = dev_id; ++ u32 intmask, mask, unexpected = 0; ++ int max_loops = 16; ++#ifndef CONFIG_ARCH_BCM2835 ++ int cardint = 0; +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -new file mode 100644 -index 0000000..b6bfa21 ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -@@ -0,0 +1,59 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+#include "vchiq_build_info.h" -+#include + -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_hostname, "dc4-arm-01" ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)" ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_time, __TIME__ ); -+VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_date, __DATE__ ); ++ spin_lock(&host->lock); + -+const char *vchiq_get_build_hostname( void ) -+{ -+ return vchiq_build_hostname; -+} ++ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); + -+const char *vchiq_get_build_version( void ) -+{ -+ return vchiq_build_version; -+} ++ if (!intmask || intmask == 0xffffffff) { ++ result = IRQ_NONE; ++ goto out; ++ } + -+const char *vchiq_get_build_date( void ) -+{ -+ return vchiq_build_date; -+} -+ -+const char *vchiq_get_build_time( void ) -+{ -+ return vchiq_build_time; -+} - -From 2e067b9b144d119a1b0b930dae88d2f1b29dd497 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 12 May 2014 15:12:02 +0100 -Subject: [PATCH 012/172] vchiq: Avoid high load when blocked and unkillable - -vchiq: Include SIGSTOP and SIGCONT in list of signals not-masked by vchiq to allow gdb to work ---- - .../interface/vchiq_arm/vchiq_2835_arm.c | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 1 + - .../interface/vchiq_arm/vchiq_connected.c | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 1 + - .../interface/vchiq_arm/vchiq_kern_lib.c | 1 + - .../interface/vchiq_arm/vchiq_killable.h | 69 ++++++++++++++++++++++ - .../vc04_services/interface/vchiq_arm/vchiq_util.c | 1 + - 7 files changed, 75 insertions(+) - create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -index b3bdaa2..7e7b09f 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c -@@ -56,6 +56,7 @@ - #include "vchiq_arm.h" - #include "vchiq_2835.h" - #include "vchiq_connected.h" -+#include "vchiq_killable.h" - - #define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -index 2596818..3e328ab 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -50,6 +50,7 @@ - #include "vchiq_ioctl.h" - #include "vchiq_arm.h" - #include "vchiq_debugfs.h" -+#include "vchiq_killable.h" - - #define DEVICE_NAME "vchiq" - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -index 65f4b52..5efc62f 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c -@@ -33,6 +33,7 @@ - - #include "vchiq_connected.h" - #include "vchiq_core.h" -+#include "vchiq_killable.h" - #include - #include - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -index f962027..14a4155 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -32,6 +32,7 @@ - */ - - #include "vchiq_core.h" -+#include "vchiq_killable.h" - - #define VCHIQ_SLOT_HANDLER_STACK 8192 - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -index be9735f..5a4182e 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -39,6 +39,7 @@ - - #include "vchiq_core.h" - #include "vchiq_arm.h" -+#include "vchiq_killable.h" - - /* ---- Public Variables ------------------------------------------------- */ - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h -new file mode 100644 -index 0000000..335446e ---- /dev/null -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h -@@ -0,0 +1,69 @@ -+/** -+ * Copyright (c) 2010-2012 Broadcom. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#ifndef VCHIQ_KILLABLE_H -+#define VCHIQ_KILLABLE_H -+ -+#include -+#include -+ -+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT)) -+ -+static inline int __must_check down_interruptible_killable(struct semaphore *sem) -+{ -+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ -+ int ret; -+ sigset_t blocked, oldset; -+ siginitsetinv(&blocked, SHUTDOWN_SIGS); -+ sigprocmask(SIG_SETMASK, &blocked, &oldset); -+ ret = down_interruptible(sem); -+ sigprocmask(SIG_SETMASK, &oldset, NULL); -+ return ret; -+} -+#define down_interruptible down_interruptible_killable ++ do { ++ /* Clear selected interrupts. */ ++ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_BUS_POWER); ++ bcm2835_mmc_writel(host, mask, SDHCI_INT_STATUS); + + -+static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock) -+{ -+ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ -+ int ret; -+ sigset_t blocked, oldset; -+ siginitsetinv(&blocked, SHUTDOWN_SIGS); -+ sigprocmask(SIG_SETMASK, &blocked, &oldset); -+ ret = mutex_lock_interruptible(lock); -+ sigprocmask(SIG_SETMASK, &oldset, NULL); -+ return ret; -+} -+#define mutex_lock_interruptible mutex_lock_interruptible_killable ++ if (intmask & SDHCI_INT_CMD_MASK) ++ bcm2835_mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + ++ if (intmask & SDHCI_INT_DATA_MASK) ++ bcm2835_mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK); ++ ++ if (intmask & SDHCI_INT_BUS_POWER) ++ pr_err("%s: Card is consuming too much power!\n", ++ mmc_hostname(host->mmc)); ++ ++ if (intmask & SDHCI_INT_CARD_INT) { ++#ifndef CONFIG_ARCH_BCM2835 ++ cardint = 1; ++#else ++ bcm2835_mmc_enable_sdio_irq_nolock(host, false); ++ host->thread_isr |= SDHCI_INT_CARD_INT; ++ result = IRQ_WAKE_THREAD; +#endif -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -index c2eefef..05e7979 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c -@@ -32,6 +32,7 @@ - */ - - #include "vchiq_util.h" -+#include "vchiq_killable.h" - - static inline int is_pow2(int i) - { ++ } ++ ++ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | ++ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | ++ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | ++ SDHCI_INT_CARD_INT); ++ ++ if (intmask) { ++ unexpected |= intmask; ++ bcm2835_mmc_writel(host, intmask, SDHCI_INT_STATUS); ++ } ++ ++ if (result == IRQ_NONE) ++ result = IRQ_HANDLED; ++ ++ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); ++ } while (intmask && --max_loops); ++out: ++ spin_unlock(&host->lock); ++ ++ if (unexpected) { ++ pr_err("%s: Unexpected interrupt 0x%08x.\n", ++ mmc_hostname(host->mmc), unexpected); ++ bcm2835_mmc_dumpregs(host); ++ } ++ ++#ifndef CONFIG_ARCH_BCM2835 ++ if (cardint) ++ mmc_signal_sdio_irq(host->mmc); ++#endif ++ ++ return result; ++} ++ ++#ifdef CONFIG_ARCH_BCM2835 ++static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) ++{ ++ struct bcm2835_host *host = dev_id; ++ unsigned long flags; ++ u32 isr; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ isr = host->thread_isr; ++ host->thread_isr = 0; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (isr & SDHCI_INT_CARD_INT) { ++ sdio_run_irqs(host->mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) ++ bcm2835_mmc_enable_sdio_irq_nolock(host, true); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ return isr ? IRQ_HANDLED : IRQ_NONE; ++} ++#endif ++ ++ ++ ++void bcm2835_mmc_set_clock(struct bcm2835_host *host, unsigned int clock) ++{ ++ int div = 0; /* Initialized for compiler warning */ ++ int real_div = div, clk_mul = 1; ++ u16 clk = 0; ++ unsigned long timeout; ++ ++ ++ host->mmc->actual_clock = 0; ++ ++ bcm2835_mmc_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ return; ++ ++ /* Version 3.00 divisors must be a multiple of 2. */ ++ if (host->max_clk <= clock) ++ div = 1; ++ else { ++ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; ++ div += 2) { ++ if ((host->max_clk / div) <= clock) ++ break; ++ } ++ } ++ ++ real_div = div; ++ div >>= 1; ++ ++ if (real_div) ++ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; ++ ++ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; ++ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) ++ << SDHCI_DIVIDER_HI_SHIFT; ++ clk |= SDHCI_CLOCK_INT_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Wait max 20 ms */ ++ timeout = 20; ++ while (!((clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ pr_err("%s: Internal clock never " ++ "stabilised.\n", mmc_hostname(host->mmc)); ++ bcm2835_mmc_dumpregs(host); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ if (20-timeout > 10 && 20-timeout > host->max_delay) { ++ host->max_delay = 20-timeout; ++ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); ++ } ++ ++ clk |= SDHCI_CLOCK_CARD_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++} ++ ++static void bcm2835_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ ++ host = mmc_priv(mmc); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) ++ bcm2835_mmc_send_command(host, mrq->sbc); ++ else ++ bcm2835_mmc_send_command(host, mrq->cmd); ++ ++ mmiowb(); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (!(mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) && mrq->cmd->data && host->use_dma) { ++ /* DMA transfer starts now, PIO starts after interrupt */ ++ bcm2835_mmc_transfer_dma(host); ++ } ++} ++ ++ ++static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct bcm2835_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ u8 ctrl; ++ u16 clk, ctrl_2; ++ ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (!ios->clock || ios->clock != host->clock) { ++ bcm2835_mmc_set_clock(host, ios->clock); ++ host->clock = ios->clock; ++ } ++ ++ if (host->pwr != SDHCI_POWER_330) { ++ host->pwr = SDHCI_POWER_330; ++ bcm2835_mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL); ++ } ++ ++ ctrl = bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL); ++ ++ /* set bus width */ ++ ctrl &= ~SDHCI_CTRL_8BITBUS; ++ if (ios->bus_width == MMC_BUS_WIDTH_4) ++ ctrl |= SDHCI_CTRL_4BITBUS; ++ else ++ ctrl &= ~SDHCI_CTRL_4BITBUS; ++ ++ ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */ ++ ++ ++ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ /* ++ * We only need to set Driver Strength if the ++ * preset value enable is not set. ++ */ ++ ctrl_2 = bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2); ++ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; ++ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; ++ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ++ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; ++ ++ bcm2835_mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); ++ ++ /* Reset SD Clock Enable */ ++ clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL); ++ clk &= ~SDHCI_CLOCK_CARD_EN; ++ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ /* Re-enable SD Clock */ ++ bcm2835_mmc_set_clock(host, host->clock); ++ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++static struct mmc_host_ops bcm2835_ops = { ++ .request = bcm2835_mmc_request, ++ .set_ios = bcm2835_mmc_set_ios, ++ .enable_sdio_irq = bcm2835_mmc_enable_sdio_irq, ++}; ++ ++ ++static void bcm2835_mmc_tasklet_finish(unsigned long param) ++{ ++ struct bcm2835_host *host; ++ unsigned long flags; ++ struct mmc_request *mrq; ++ ++ host = (struct bcm2835_host *)param; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ /* ++ * If this tasklet gets rescheduled while running, it will ++ * be run again afterwards but without any active request. ++ */ ++ if (!host->mrq) { ++ spin_unlock_irqrestore(&host->lock, flags); ++ return; ++ } ++ ++ del_timer(&host->timer); ++ ++ mrq = host->mrq; ++ ++ /* ++ * The controller needs a reset of internal state machines ++ * upon error conditions. ++ */ ++ if (!(host->flags & SDHCI_DEVICE_DEAD) && ++ ((mrq->cmd && mrq->cmd->error) || ++ (mrq->data && (mrq->data->error || ++ (mrq->data->stop && mrq->data->stop->error))))) { ++ ++ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); ++ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); ++ } ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmiowb(); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++ ++int bcm2835_mmc_add_host(struct bcm2835_host *host) ++{ ++ struct mmc_host *mmc = host->mmc; ++ struct device *dev = mmc->parent; ++#ifndef FORCE_PIO ++ struct dma_slave_config cfg; ++#endif ++ int ret; ++ ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ host->clk_mul = 0; ++ ++ mmc->f_max = host->max_clk; ++ mmc->f_max = host->max_clk; ++ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; ++ ++ /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ ++ host->timeout_clk = mmc->f_max / 1000; ++#ifdef CONFIG_ARCH_BCM2835 ++ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; ++#endif ++ /* host controller capabilities */ ++ mmc->caps = MMC_CAP_CMD23 | MMC_CAP_ERASE | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | ++ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_4_BIT_DATA; ++ ++ host->flags = SDHCI_AUTO_CMD23; ++ ++ spin_lock_init(&host->lock); ++ ++#ifdef FORCE_PIO ++ dev_info(dev, "Forcing PIO mode\n"); ++ host->have_dma = false; ++#else ++ if (IS_ERR_OR_NULL(host->dma_chan_tx) || ++ IS_ERR_OR_NULL(host->dma_chan_rx)) { ++ dev_err(dev, "%s: Unable to initialise DMA channels. Falling back to PIO\n", ++ DRIVER_NAME); ++ host->have_dma = false; ++ } else { ++ dev_info(dev, "DMA channels allocated"); ++ host->have_dma = true; ++ ++ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ cfg.slave_id = 11; /* DREQ channel */ ++ ++ cfg.direction = DMA_MEM_TO_DEV; ++ cfg.src_addr = 0; ++ cfg.dst_addr = host->phys_addr + SDHCI_BUFFER; ++ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); ++ ++ cfg.direction = DMA_DEV_TO_MEM; ++ cfg.src_addr = host->phys_addr + SDHCI_BUFFER; ++ cfg.dst_addr = 0; ++ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); ++ } ++#endif ++ mmc->max_segs = 128; ++ mmc->max_req_size = 524288; ++ mmc->max_seg_size = mmc->max_req_size; ++ mmc->max_blk_size = 512; ++ mmc->max_blk_count = 65535; ++ ++ /* report supported voltage ranges */ ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ tasklet_init(&host->finish_tasklet, ++ bcm2835_mmc_tasklet_finish, (unsigned long)host); ++ ++ setup_timer(&host->timer, bcm2835_mmc_timeout_timer, (unsigned long)host); ++ init_waitqueue_head(&host->buf_ready_int); ++ ++ bcm2835_mmc_init(host, 0); ++#ifndef CONFIG_ARCH_BCM2835 ++ ret = devm_request_irq(dev, host->irq, bcm2835_mmc_irq, 0, ++ mmc_hostname(mmc), host); ++#else ++ ret = devm_request_threaded_irq(dev, host->irq, bcm2835_mmc_irq, ++ bcm2835_mmc_thread_irq, IRQF_SHARED, ++ mmc_hostname(mmc), host); ++#endif ++ if (ret) { ++ dev_err(dev, "Failed to request IRQ %d: %d\n", host->irq, ret); ++ goto untasklet; ++ } ++ ++ mmiowb(); ++ mmc_add_host(mmc); ++ ++ return 0; ++ ++untasklet: ++ tasklet_kill(&host->finish_tasklet); ++ ++ return ret; ++} ++ ++static int bcm2835_mmc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *node = dev->of_node; ++ struct clk *clk; ++ struct resource *iomem; ++ struct bcm2835_host *host; ++ struct mmc_host *mmc; ++ int ret; ++ ++ mmc = mmc_alloc_host(sizeof(*host), dev); ++ if (!mmc) ++ return -ENOMEM; ++ ++ mmc->ops = &bcm2835_ops; ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->timeout = msecs_to_jiffies(1000); ++ spin_lock_init(&host->lock); ++ ++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ host->ioaddr = devm_ioremap_resource(dev, iomem); ++ if (IS_ERR(host->ioaddr)) { ++ ret = PTR_ERR(host->ioaddr); ++ goto err; ++ } ++ ++ host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; ++ ++#ifndef FORCE_PIO ++ if (node) { ++ host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); ++ host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); ++ } else { ++ dma_cap_mask_t mask; ++ ++ dma_cap_zero(mask); ++ /* we don't care about the channel, any would work */ ++ dma_cap_set(DMA_SLAVE, mask); ++ host->dma_chan_tx = dma_request_channel(mask, NULL, NULL); ++ host->dma_chan_rx = dma_request_channel(mask, NULL, NULL); ++ } ++#endif ++ clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "could not get clk\n"); ++ ret = PTR_ERR(clk); ++ goto err; ++ } ++ ++ host->max_clk = clk_get_rate(clk); ++ ++ host->irq = platform_get_irq(pdev, 0); ++ if (host->irq <= 0) { ++ dev_err(dev, "get IRQ failed\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (node) ++ mmc_of_parse(mmc); ++ else ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ ++ ret = bcm2835_mmc_add_host(host); ++ if (ret) ++ goto err; ++ ++ platform_set_drvdata(pdev, host); ++ ++ return 0; ++err: ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++static int bcm2835_mmc_remove(struct platform_device *pdev) ++{ ++ struct bcm2835_host *host = platform_get_drvdata(pdev); ++ unsigned long flags; ++ int dead; ++ u32 scratch; ++ ++ dead = 0; ++ scratch = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); ++ if (scratch == (u32)-1) ++ dead = 1; ++ ++ ++ if (dead) { ++ spin_lock_irqsave(&host->lock, flags); ++ ++ host->flags |= SDHCI_DEVICE_DEAD; ++ ++ if (host->mrq) { ++ pr_err("%s: Controller removed during " ++ " transfer!\n", mmc_hostname(host->mmc)); ++ ++ host->mrq->cmd->error = -ENOMEDIUM; ++ tasklet_schedule(&host->finish_tasklet); ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ ++ mmc_remove_host(host->mmc); ++ ++ if (!dead) ++ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); ++ ++ free_irq(host->irq, host); ++ ++ del_timer_sync(&host->timer); ++ ++ tasklet_kill(&host->finish_tasklet); ++ ++ mmc_free_host(host->mmc); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++ ++static const struct of_device_id bcm2835_mmc_match[] = { ++ { .compatible = "brcm,bcm2835-mmc" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, bcm2835_mmc_match); ++ ++ ++ ++static struct platform_driver bcm2835_mmc_driver = { ++ .probe = bcm2835_mmc_probe, ++ .remove = bcm2835_mmc_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2835_mmc_match, ++ }, ++}; ++module_platform_driver(bcm2835_mmc_driver); ++ ++MODULE_ALIAS("platform:mmc-bcm2835"); ++MODULE_DESCRIPTION("BCM2835 SDHCI driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Gellert Weisz"); -From 38a6237db4d225352e14513dde856a7767e51c76 Mon Sep 17 00:00:00 2001 +From 3c364191bcd2eb70a61271316a7348e7f471caea Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:31:47 +0100 -Subject: [PATCH 013/172] cma: Add vc_cma driver to enable use of CMA +Subject: [PATCH 009/104] cma: Add vc_cma driver to enable use of CMA Signed-off-by: popcornmix + +vc_cma: Make the vc_cma area the default contiguous DMA area --- drivers/char/Kconfig | 2 + drivers/char/Makefile | 1 + drivers/char/broadcom/Kconfig | 15 + drivers/char/broadcom/Makefile | 1 + drivers/char/broadcom/vc_cma/Makefile | 14 + - drivers/char/broadcom/vc_cma/vc_cma.c | 1143 +++++++++++++++++++++++++++++++++ - drivers/misc/Makefile | 2 +- + drivers/char/broadcom/vc_cma/vc_cma.c | 1193 +++++++++++++++++++++++++++++++++ include/linux/broadcom/vc_cma.h | 29 + - 8 files changed, 1206 insertions(+), 1 deletion(-) + 7 files changed, 1255 insertions(+) create mode 100644 drivers/char/broadcom/Kconfig create mode 100644 drivers/char/broadcom/Makefile create mode 100644 drivers/char/broadcom/vc_cma/Makefile @@ -80312,10 +80603,10 @@ Signed-off-by: popcornmix create mode 100644 include/linux/broadcom/vc_cma.h diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig -index efefd12..aebd7ca 100644 +index a4af8221..59e4d63 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig -@@ -581,6 +581,8 @@ config DEVPORT +@@ -590,6 +590,8 @@ config DEVPORT source "drivers/s390/char/Kconfig" @@ -80383,10 +80674,10 @@ index 0000000..6a94de4 +vc-cma-objs := vc_cma.o diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c new file mode 100644 -index 0000000..a635f9f +index 0000000..07b31a7 --- /dev/null +++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -0,0 +1,1143 @@ +@@ -0,0 +1,1193 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * @@ -80447,6 +80738,8 @@ index 0000000..a635f9f +#define LOG_DBG(fmt, ...) \ + if (vc_cma_debug) \ + printk(KERN_INFO fmt "\n", ##__VA_ARGS__) ++#define LOG_INFO(fmt, ...) \ ++ printk(KERN_INFO fmt "\n", ##__VA_ARGS__) +#define LOG_ERR(fmt, ...) \ + printk(KERN_ERR fmt "\n", ##__VA_ARGS__) + @@ -80509,6 +80802,10 @@ index 0000000..a635f9f +unsigned int vc_cma_chunks_used; +unsigned int vc_cma_chunks_reserved; + ++ ++void *vc_cma_dma_alloc; ++unsigned int vc_cma_dma_size; ++ +static int in_loud_error; + +unsigned int vc_cma_reserve_total; @@ -80587,8 +80884,17 @@ index 0000000..a635f9f + * size from the end of memory + */ + if (vc_cma_size) { -+ if (dma_declare_contiguous(NULL /*&vc_cma_device.dev*/, vc_cma_size, ++ if (dma_declare_contiguous(&vc_cma_device.dev, vc_cma_size, + vc_cma_base, 0) == 0) { ++ if (!dev_get_cma_area(NULL)) { ++ /* There is no default CMA area - make this ++ the default */ ++ struct cma *vc_cma_area = dev_get_cma_area( ++ &vc_cma_device.dev); ++ dma_contiguous_set_default(vc_cma_area); ++ LOG_INFO("vc_cma_reserve - using vc_cma as " ++ "the default contiguous DMA area"); ++ } + } else { + LOG_ERR("vc_cma: dma_declare_contiguous(%x,%x) failed", + vc_cma_size, (unsigned int)vc_cma_base); @@ -80698,6 +81004,9 @@ index 0000000..a635f9f + seq_printf(m, " PID %5d: %d bytes\n", user->pid, + user->reserve); + } ++ seq_printf(m, " dma_alloc : %p (%d pages)\n", ++ vc_cma_dma_alloc ? page_address(vc_cma_dma_alloc) : 0, ++ vc_cma_dma_size); + + seq_printf(m, "\n"); + @@ -80737,31 +81046,33 @@ index 0000000..a635f9f +#define FREE_STR "free" +#define DEBUG_STR "debug" +#define RESERVE_STR "reserve" ++#define DMA_ALLOC_STR "dma_alloc" ++#define DMA_FREE_STR "dma_free" + if (strncmp(input_str, ALLOC_STR, strlen(ALLOC_STR)) == 0) { -+ int size; ++ int alloc_size; + char *p = input_str + strlen(ALLOC_STR); + + while (*p == ' ') + p++; -+ size = memparse(p, NULL); -+ LOG_ERR("/proc/vc-cma: alloc %d", size); -+ if (size) ++ alloc_size = memparse(p, NULL); ++ LOG_INFO("/proc/vc-cma: alloc %d", alloc_size); ++ if (alloc_size) + send_vc_msg(VC_CMA_MSG_REQUEST_FREE, -+ size / VC_CMA_CHUNK_SIZE, 0); ++ alloc_size / VC_CMA_CHUNK_SIZE, 0); + else + LOG_ERR("invalid size '%s'", p); + rc = size; + } else if (strncmp(input_str, FREE_STR, strlen(FREE_STR)) == 0) { -+ int size; ++ int alloc_size; + char *p = input_str + strlen(FREE_STR); + + while (*p == ' ') + p++; -+ size = memparse(p, NULL); -+ LOG_ERR("/proc/vc-cma: free %d", size); -+ if (size) ++ alloc_size = memparse(p, NULL); ++ LOG_INFO("/proc/vc-cma: free %d", alloc_size); ++ if (alloc_size) + send_vc_msg(VC_CMA_MSG_REQUEST_ALLOC, -+ size / VC_CMA_CHUNK_SIZE, 0); ++ alloc_size / VC_CMA_CHUNK_SIZE, 0); + else + LOG_ERR("invalid size '%s'", p); + rc = size; @@ -80773,18 +81084,46 @@ index 0000000..a635f9f + vc_cma_debug = 1; + else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) + vc_cma_debug = 0; -+ LOG_ERR("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); ++ LOG_INFO("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); + rc = size; + } else if (strncmp(input_str, RESERVE_STR, strlen(RESERVE_STR)) == 0) { -+ int size; ++ int alloc_size; + int reserved; + char *p = input_str + strlen(RESERVE_STR); + while (*p == ' ') + p++; -+ size = memparse(p, NULL); ++ alloc_size = memparse(p, NULL); + -+ reserved = vc_cma_set_reserve(size, current->tgid); ++ reserved = vc_cma_set_reserve(alloc_size, current->tgid); + rc = (reserved >= 0) ? size : reserved; ++ } else if (strncmp(input_str, DMA_ALLOC_STR, strlen(DMA_ALLOC_STR)) == 0) { ++ int alloc_size; ++ char *p = input_str + strlen(DMA_ALLOC_STR); ++ while (*p == ' ') ++ p++; ++ alloc_size = memparse(p, NULL); ++ ++ if (vc_cma_dma_alloc) { ++ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, ++ vc_cma_dma_size); ++ vc_cma_dma_alloc = NULL; ++ vc_cma_dma_size = 0; ++ } ++ vc_cma_dma_alloc = dma_alloc_from_contiguous(NULL, alloc_size, 0); ++ vc_cma_dma_size = (vc_cma_dma_alloc ? alloc_size : 0); ++ if (vc_cma_dma_alloc) ++ LOG_INFO("dma_alloc(%d pages) -> %p", alloc_size, page_address(vc_cma_dma_alloc)); ++ else ++ LOG_ERR("dma_alloc(%d pages) failed", alloc_size); ++ rc = size; ++ } else if (strncmp(input_str, DMA_FREE_STR, strlen(DMA_FREE_STR)) == 0) { ++ if (vc_cma_dma_alloc) { ++ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, ++ vc_cma_dma_size); ++ vc_cma_dma_alloc = NULL; ++ vc_cma_dma_size = 0; ++ } ++ rc = size; + } + +out: @@ -80915,7 +81254,7 @@ index 0000000..a635f9f + uint8_t *chunk_addr; + size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; + -+ chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, + PAGES_PER_CHUNK, + VC_CMA_CHUNK_ORDER); + if (!chunk) @@ -80931,13 +81270,14 @@ index 0000000..a635f9f + BUG_ON(((page_to_phys(chunk) - vc_cma_base) % + VC_CMA_CHUNK_SIZE) != 0); + if (chunk_num >= vc_cma_chunks) { ++ phys_addr_t _pa = vc_cma_base + vc_cma_size - 1; + LOG_ERR("%s: ===============================", + __func__); -+ LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " ++ LOG_ERR("%s: chunk phys %x, vc_cma %pa-%pa - " + "bad SPARSEMEM configuration?", + __func__, (unsigned int)page_to_phys(chunk), -+ vc_cma_base, vc_cma_base + vc_cma_size - 1); -+ LOG_ERR("%s: dev->cma_area = %p\n", __func__, ++ &vc_cma_base, &_pa); ++ LOG_ERR("%s: dev->cma_area = %p", __func__, + (void*)0/*vc_cma_device.dev.cma_area*/); + LOG_ERR("%s: ===============================", + __func__); @@ -81061,13 +81401,14 @@ index 0000000..a635f9f + } + + if (!dma_release_from_contiguous -+ (NULL /*&vc_cma_device.dev*/, page, ++ (&vc_cma_device.dev, page, + PAGES_PER_CHUNK)) { ++ phys_addr_t _pa = page_to_phys(page); + LOG_ERR + ("CMA_MSG_FREE - failed to " -+ "release chunk %d (phys %x, " ++ "release chunk %d (phys %pa, " + "page %x)", chunk_num, -+ page_to_phys(page), ++ &_pa, + (unsigned int)page); + } + vc_cma_chunks_used--; @@ -81412,12 +81753,12 @@ index 0000000..a635f9f + if (!check_cma_config()) + goto out_release; + -+ printk(KERN_INFO "vc-cma: Videocore CMA driver\n"); -+ printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); -+ printk(KERN_INFO "vc-cma: vc_cma_size = 0x%08x (%u MiB)\n", -+ vc_cma_size, vc_cma_size / (1024 * 1024)); -+ printk(KERN_INFO "vc-cma: vc_cma_initial = 0x%08x (%u MiB)\n", -+ vc_cma_initial, vc_cma_initial / (1024 * 1024)); ++ LOG_INFO("vc-cma: Videocore CMA driver"); ++ LOG_INFO("vc-cma: vc_cma_base = %pa", &vc_cma_base); ++ LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", ++ vc_cma_size, vc_cma_size / (1024 * 1024)); ++ LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", ++ vc_cma_initial, vc_cma_initial / (1024 * 1024)); + + vc_cma_base_page = phys_to_page(vc_cma_base); + @@ -81427,7 +81768,7 @@ index 0000000..a635f9f + for (vc_cma_chunks_used = 0; + vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { + struct page *chunk; -+ chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, ++ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, + PAGES_PER_CHUNK, + VC_CMA_CHUNK_ORDER); + if (!chunk) @@ -81530,19 +81871,6 @@ index 0000000..a635f9f +module_exit(vc_cma_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom Corporation"); -diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index c085ede..43d2ac9 100644 ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -51,7 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ - obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ - obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o - obj-$(CONFIG_SRAM) += sram.o --obj-y += vc04_services/ -+obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/ - obj-y += mic/ - obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ diff --git a/include/linux/broadcom/vc_cma.h b/include/linux/broadcom/vc_cma.h new file mode 100644 index 0000000..5325832 @@ -81579,10 +81907,10 @@ index 0000000..5325832 + +#endif /* VC_CMA_H */ -From 3d1f88f9fc8393b6c8e34c991dac0dbc58ae3f4d Mon Sep 17 00:00:00 2001 +From 228e8e7cf4c46585562ff24e2244540e5e6d649a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 26 Mar 2012 22:15:50 +0100 -Subject: [PATCH 014/172] bcm2708: alsa sound driver +Subject: [PATCH 010/104] bcm2708: alsa sound driver Signed-off-by: popcornmix @@ -81605,19 +81933,16 @@ alsa: reduce severity of expected warning message snd-bcm2708: Fix dmesg spam for non-error case --- - arch/arm/configs/bcmrpi_defconfig | 20 + - arch/arm/mach-bcm2708/bcm2708.c | 54 ++ - .../interface/vchiq_arm/vchiq_kern_lib.c | 7 +- - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 12 +- - sound/arm/Kconfig | 7 + - sound/arm/Makefile | 5 + - sound/arm/bcm2835-ctl.c | 323 ++++++++ - sound/arm/bcm2835-pcm.c | 552 +++++++++++++ - sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++ - sound/arm/bcm2835.c | 420 ++++++++++ - sound/arm/bcm2835.h | 167 ++++ - sound/arm/vc_vchi_audioserv_defs.h | 116 +++ - 12 files changed, 2578 insertions(+), 7 deletions(-) + arch/arm/mach-bcm2708/bcm2708.c | 54 +++ + sound/arm/Kconfig | 7 + + sound/arm/Makefile | 5 + + sound/arm/bcm2835-ctl.c | 323 +++++++++++++ + sound/arm/bcm2835-pcm.c | 552 +++++++++++++++++++++++ + sound/arm/bcm2835-vchiq.c | 902 +++++++++++++++++++++++++++++++++++++ + sound/arm/bcm2835.c | 420 +++++++++++++++++ + sound/arm/bcm2835.h | 167 +++++++ + sound/arm/vc_vchi_audioserv_defs.h | 116 +++++ + 9 files changed, 2546 insertions(+) create mode 100755 sound/arm/bcm2835-ctl.c create mode 100755 sound/arm/bcm2835-pcm.c create mode 100755 sound/arm/bcm2835-vchiq.c @@ -81625,42 +81950,11 @@ snd-bcm2708: Fix dmesg spam for non-error case create mode 100755 sound/arm/bcm2835.h create mode 100644 sound/arm/vc_vchi_audioserv_defs.h -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 8950e53..63cb6a4 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -202,6 +202,26 @@ CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set - # CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=m -+CONFIG_SND_SEQUENCER=m -+CONFIG_SND_SEQ_DUMMY=m -+CONFIG_SND_MIXER_OSS=m -+CONFIG_SND_PCM_OSS=m -+CONFIG_SND_SEQUENCER_OSS=y -+CONFIG_SND_HRTIMER=m -+CONFIG_SND_DUMMY=m -+CONFIG_SND_ALOOP=m -+CONFIG_SND_VIRMIDI=m -+CONFIG_SND_MTPAV=m -+CONFIG_SND_SERIAL_U16550=m -+CONFIG_SND_MPU401=m -+CONFIG_SND_BCM2835=m -+CONFIG_SND_USB_AUDIO=m -+CONFIG_SND_USB_UA101=m -+CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_6FIRE=m -+CONFIG_SOUND_PRIME=m - CONFIG_HID_A4TECH=m - CONFIG_HID_ACRUX=m - CONFIG_HID_APPLE=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 7503649..f3dccae 100644 +index d1308ed..f54e3e9b 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -403,6 +403,58 @@ struct platform_device bcm2708_powerman_device = { +@@ -447,6 +447,58 @@ struct platform_device bcm2708_powerman_device = { .coherent_dma_mask = 0xffffffffUL}, }; @@ -81719,87 +82013,17 @@ index 7503649..f3dccae 100644 int __init bcm_register_device(struct platform_device *pdev) { int ret; -@@ -508,6 +560,8 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_powerman_device); - +@@ -556,6 +608,8 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2835_emmc_device); + #endif bcm2708_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { struct amba_device *d = amba_devs[i]; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -index 5a4182e..25e7011 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c -@@ -288,11 +288,12 @@ VCHIQ_STATUS_T vchiq_open_service( - NULL); - - if (service) { -+ *phandle = service->handle; - status = vchiq_open_service_internal(service, current->pid); -- if (status == VCHIQ_SUCCESS) -- *phandle = service->handle; -- else -+ if (status != VCHIQ_SUCCESS) { - vchiq_remove_service(service->handle); -+ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; -+ } - } - - failed: -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -index 72eacdaf..c26eff7 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -634,6 +634,9 @@ int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, - { - VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; - SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ - if (service) { - VCHIQ_SERVICE_PARAMS_T params; - VCHIQ_STATUS_T status; -@@ -650,11 +653,10 @@ int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, - if (status != VCHIQ_SUCCESS) { - service_free(service); - service = NULL; -+ *handle = NULL; - } - } - -- *handle = (VCHI_SERVICE_HANDLE_T)service; -- - return (service != NULL) ? 0 : -1; - } - EXPORT_SYMBOL(vchi_service_open); -@@ -665,6 +667,9 @@ int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, - { - VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; - SHIM_SERVICE_T *service = service_alloc(instance, setup); -+ -+ *handle = (VCHI_SERVICE_HANDLE_T)service; -+ - if (service) { - VCHIQ_SERVICE_PARAMS_T params; - VCHIQ_STATUS_T status; -@@ -680,11 +685,10 @@ int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, - if (status != VCHIQ_SUCCESS) { - service_free(service); - service = NULL; -+ *handle = NULL; - } - } - -- *handle = (VCHI_SERVICE_HANDLE_T)service; -- - return (service != NULL) ? 0 : -1; - } - EXPORT_SYMBOL(vchi_service_create); diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig -index 885683a..f7ceafd 100644 +index 885683a..ada7ba2 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -39,5 +39,12 @@ config SND_PXA2XX_AC97 @@ -81808,7 +82032,7 @@ index 885683a..f7ceafd 100644 +config SND_BCM2835 + tristate "BCM2835 ALSA driver" -+ depends on ARCH_BCM2708 && BCM2708_VCHIQ && SND ++ depends on (ARCH_BCM2708 || ARCH_BCM2709) && BCM2708_VCHIQ && SND + select SND_PCM + help + Say Y or M if you want to support BCM2835 Alsa pcm card driver @@ -84345,57 +84569,1763 @@ index 0000000..af3e6eb + +#endif // _VC_AUDIO_DEFS_H_ -From 440a7ce5f52fd43466a0d25132a3bc855c224be9 Mon Sep 17 00:00:00 2001 +From d613f62318f4535aa6449f69dbac27b44feefd93 Mon Sep 17 00:00:00 2001 From: popcornmix -Date: Wed, 3 Jul 2013 00:51:55 +0100 -Subject: [PATCH 015/172] Add hwrng (hardware random number generator) driver +Date: Tue, 2 Jul 2013 23:42:01 +0100 +Subject: [PATCH 011/104] bcm2708 vchiq driver +Signed-off-by: popcornmix + +vchiq: create_pagelist copes with vmalloc memory + +Signed-off-by: Daniel Stone + +vchiq: fix the shim message release + +Signed-off-by: Daniel Stone + +vchiq: export additional symbols + +Signed-off-by: Daniel Stone + +VCHIQ: Make service closure fully synchronous (drv) + +This is one half of a two-part patch, the other half of which is to +the vchiq_lib user library. With these patches, calls to +vchiq_close_service and vchiq_remove_service won't return until any +associated callbacks have been delivered to the callback thread. + +VCHIQ: Add per-service tracing + +The new service option VCHIQ_SERVICE_OPTION_TRACE is a boolean that +toggles tracing for the specified service. + +This commit also introduces vchi_service_set_option and the associated +option VCHI_SERVICE_OPTION_TRACE. + +vchiq: Make the synchronous-CLOSE logic more tolerant + +vchiq: Move logging control into debugfs + +vchiq: Take care of a corner case tickled by VCSM + +Closing a connection that isn't fully open requires care, since one +side does not know the other side's port number. Code was present to +handle the case where a CLOSE is sent immediately after an OPEN, i.e. +before the OPENACK has been received, but this was incorrectly being +used when an OPEN from a client using port 0 was rejected. + +(In the observed failure, the host was attempting to use the VCSM +service, which isn't present in the 'cutdown' firmware. The failure +was intermittent because sometimes the keepalive service would +grab port 0.) + +This case can be distinguished because the client's remoteport will +still be VCHIQ_PORT_FREE, and the srvstate will be OPENING. Either +condition is sufficient to differentiate it from the special case +described above. + +vchiq: Avoid high load when blocked and unkillable + +vchiq: Include SIGSTOP and SIGCONT in list of signals not-masked by vchiq to allow gdb to work + +vchiq_arm: Complete support for SYNCHRONOUS mode + +vchiq: Remove inline from suspend/resume --- - drivers/char/hw_random/Kconfig | 11 ++++ - drivers/char/hw_random/Makefile | 1 + - drivers/char/hw_random/bcm2708-rng.c | 118 +++++++++++++++++++++++++++++++++++ - 3 files changed, 130 insertions(+) - create mode 100755 drivers/char/hw_random/bcm2708-rng.c + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/vc04_services/Kconfig | 9 + + drivers/misc/vc04_services/Makefile | 14 + + .../interface/vchi/connections/connection.h | 328 ++ + .../interface/vchi/message_drivers/message.h | 204 + + drivers/misc/vc04_services/interface/vchi/vchi.h | 378 ++ + .../misc/vc04_services/interface/vchi/vchi_cfg.h | 224 ++ + .../interface/vchi/vchi_cfg_internal.h | 71 + + .../vc04_services/interface/vchi/vchi_common.h | 175 + + .../misc/vc04_services/interface/vchi/vchi_mh.h | 42 + + .../misc/vc04_services/interface/vchiq_arm/vchiq.h | 40 + + .../vc04_services/interface/vchiq_arm/vchiq_2835.h | 42 + + .../interface/vchiq_arm/vchiq_2835_arm.c | 562 +++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 2884 ++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 223 ++ + .../interface/vchiq_arm/vchiq_build_info.h | 37 + + .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 69 + + .../interface/vchiq_arm/vchiq_connected.c | 120 + + .../interface/vchiq_arm/vchiq_connected.h | 50 + + .../vc04_services/interface/vchiq_arm/vchiq_core.c | 3934 ++++++++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_core.h | 712 ++++ + .../interface/vchiq_arm/vchiq_debugfs.c | 383 ++ + .../interface/vchiq_arm/vchiq_debugfs.h | 52 + + .../interface/vchiq_arm/vchiq_genversion | 87 + + .../vc04_services/interface/vchiq_arm/vchiq_if.h | 189 + + .../interface/vchiq_arm/vchiq_ioctl.h | 131 + + .../interface/vchiq_arm/vchiq_kern_lib.c | 458 +++ + .../interface/vchiq_arm/vchiq_killable.h | 69 + + .../interface/vchiq_arm/vchiq_memdrv.h | 71 + + .../interface/vchiq_arm/vchiq_pagelist.h | 58 + + .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 860 +++++ + .../vc04_services/interface/vchiq_arm/vchiq_util.c | 152 + + .../vc04_services/interface/vchiq_arm/vchiq_util.h | 81 + + .../interface/vchiq_arm/vchiq_version.c | 59 + + 35 files changed, 12770 insertions(+) + create mode 100644 drivers/misc/vc04_services/Kconfig + create mode 100644 drivers/misc/vc04_services/Makefile + create mode 100644 drivers/misc/vc04_services/interface/vchi/connections/connection.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/message_drivers/message.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_common.h + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c -diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig -index de57b38..2a24bec 100644 ---- a/drivers/char/hw_random/Kconfig -+++ b/drivers/char/hw_random/Kconfig -@@ -320,6 +320,17 @@ config HW_RANDOM_TPM - - If unsure, say Y. - -+config HW_RANDOM_BCM2708 -+ tristate "BCM2708 generic true random number generator support" -+ depends on HW_RANDOM && ARCH_BCM2708 -+ ---help--- -+ This driver provides the kernel-side support for the BCM2708 hardware. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called bcm2708-rng. -+ -+ If unsure, say N. -+ - config HW_RANDOM_MSM - tristate "Qualcomm SoCs Random Number Generator support" - depends on HW_RANDOM && ARCH_QCOM -diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile -index 0b4cd57..78b019c 100644 ---- a/drivers/char/hw_random/Makefile -+++ b/drivers/char/hw_random/Makefile -@@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o - obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o - obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o - obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o -+obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o - obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o - obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o -diff --git a/drivers/char/hw_random/bcm2708-rng.c b/drivers/char/hw_random/bcm2708-rng.c -new file mode 100755 -index 0000000..340f004 +diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig +index 006242c..4e6f46c3 100644 +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -524,6 +524,7 @@ source "drivers/misc/carma/Kconfig" + source "drivers/misc/altera-stapl/Kconfig" + source "drivers/misc/mei/Kconfig" + source "drivers/misc/vmw_vmci/Kconfig" ++source "drivers/misc/vc04_services/Kconfig" + source "drivers/misc/mic/Kconfig" + source "drivers/misc/genwqe/Kconfig" + source "drivers/misc/echo/Kconfig" +diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile +index 7d5c4cd..43d2ac9 100644 +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -51,6 +51,7 @@ obj-$(CONFIG_INTEL_MEI) += mei/ + obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/ + obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o + obj-$(CONFIG_SRAM) += sram.o ++obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/ + obj-y += mic/ + obj-$(CONFIG_GENWQE) += genwqe/ + obj-$(CONFIG_ECHO) += echo/ +diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig +new file mode 100644 +index 0000000..b94e6cd --- /dev/null -+++ b/drivers/char/hw_random/bcm2708-rng.c -@@ -0,0 +1,118 @@ ++++ b/drivers/misc/vc04_services/Kconfig +@@ -0,0 +1,9 @@ ++config BCM2708_VCHIQ ++ tristate "Videocore VCHIQ" ++ depends on MACH_BCM2708 || MACH_BCM2709 ++ default y ++ help ++ Kernel to VideoCore communication interface for the ++ BCM2708 family of products. ++ Defaults to Y when the Broadcom Videocore services ++ are included in the build, N otherwise. +diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile +new file mode 100644 +index 0000000..8d038fe +--- /dev/null ++++ b/drivers/misc/vc04_services/Makefile +@@ -0,0 +1,14 @@ ++obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o ++ ++vchiq-objs := \ ++ interface/vchiq_arm/vchiq_core.o \ ++ interface/vchiq_arm/vchiq_arm.o \ ++ interface/vchiq_arm/vchiq_kern_lib.o \ ++ interface/vchiq_arm/vchiq_2835_arm.o \ ++ interface/vchiq_arm/vchiq_debugfs.o \ ++ interface/vchiq_arm/vchiq_shim.o \ ++ interface/vchiq_arm/vchiq_util.o \ ++ interface/vchiq_arm/vchiq_connected.o \ ++ ++ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 ++ +diff --git a/drivers/misc/vc04_services/interface/vchi/connections/connection.h b/drivers/misc/vc04_services/interface/vchi/connections/connection.h +new file mode 100644 +index 0000000..fef6ac3 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/connections/connection.h +@@ -0,0 +1,328 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef CONNECTION_H_ ++#define CONNECTION_H_ ++ ++#include ++#include ++#include ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/message_drivers/message.h" ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++// Opaque handle for a connection / service pair ++typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; ++ ++// opaque handle to the connection state information ++typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; ++ ++typedef struct vchi_connection_t VCHI_CONNECTION_T; ++ ++ ++/****************************************************************************** ++ API ++ *****************************************************************************/ ++ ++// Routine to init a connection with a particular low level driver ++typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, ++ const VCHI_MESSAGE_DRIVER_T * driver ); ++ ++// Routine to control CRC enabling at a connection level ++typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ VCHI_CRC_CONTROL_T control ); ++ ++// Routine to create a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ int32_t service_id, ++ uint32_t rx_fifo_size, ++ uint32_t tx_fifo_size, ++ int server, ++ VCHI_CALLBACK_T callback, ++ void *callback_param, ++ int32_t want_crc, ++ int32_t want_unaligned_bulk_rx, ++ int32_t want_unaligned_bulk_tx, ++ VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); ++ ++// Routine to close a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); ++ ++// Routine to queue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) message queueing ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to dequeue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to peek at a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to hold a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ void **message_handle ); ++ ++// Routine to initialise a received message iterator ++typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to release a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle ); ++ ++// Routine to get info on a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle, ++ void **data, ++ int32_t *msg_size, ++ uint32_t *tx_timestamp, ++ uint32_t *rx_timestamp ); ++ ++// Routine to check whether the iterator has a next message ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ const VCHI_MSG_ITER_T *iter ); ++ ++// Routine to advance the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Routine to remove the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter ); ++ ++// Routine to hold the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **msg_handle ); ++ ++// Routine to transmit bulk data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to receive data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to report if a server is available ++typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t peer_flags ); ++ ++// Routine to report the number of RX slots available ++typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Routine to report the RX slot size ++typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, ++ int32_t service, ++ uint32_t length, ++ MESSAGE_TX_CHANNEL_T channel, ++ uint32_t channel_params, ++ uint32_t data_length, ++ uint32_t data_offset); ++ ++// Callback to inform a service that a Xon or Xoff message has been received ++typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, int32_t xoff); ++ ++// Callback to inform a service that a server available reply message has been received ++typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, int32_t service_id, uint32_t flags); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); ++ ++// Callback with all the connection info you require ++typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); ++ ++// Callback to inform of a disconnect ++typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); ++ ++// Callback to inform of a power control request ++typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, int32_t enable); ++ ++// allocate memory suitably aligned for this connection ++typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); ++ ++// free memory allocated by buffer_allocate ++typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); ++ ++ ++/****************************************************************************** ++ System driver struct ++ *****************************************************************************/ ++ ++struct opaque_vchi_connection_api_t ++{ ++ // Routine to init the connection ++ VCHI_CONNECTION_INIT_T init; ++ ++ // Connection-level CRC control ++ VCHI_CONNECTION_CRC_CONTROL_T crc_control; ++ ++ // Routine to connect to or create service ++ VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; ++ ++ // Routine to disconnect from a service ++ VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; ++ ++ // Routine to queue a message ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; ++ ++ // scatter-gather (vector) message queue ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; ++ ++ // Routine to dequeue a message ++ VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; ++ ++ // Routine to peek at a message ++ VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; ++ ++ // Routine to hold a message ++ VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; ++ ++ // Routine to initialise a received message iterator ++ VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; ++ ++ // Routine to release a message ++ VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; ++ ++ // Routine to get information on a held message ++ VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; ++ ++ // Routine to check for next message on iterator ++ VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; ++ ++ // Routine to get next message on iterator ++ VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; ++ ++ // Routine to remove the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; ++ ++ // Routine to hold the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; ++ ++ // Routine to transmit bulk data ++ VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; ++ ++ // Routine to receive data ++ VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; ++ ++ // Routine to report the available servers ++ VCHI_CONNECTION_SERVER_PRESENT server_present; ++ ++ // Routine to report the number of RX slots available ++ VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; ++ ++ // Routine to report the RX slot size ++ VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; ++ ++ // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++ VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; ++ ++ // Callback to inform a service that a Xon or Xoff message has been received ++ VCHI_CONNECTION_FLOW_CONTROL flow_control; ++ ++ // Callback to inform a service that a server available reply message has been received ++ VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; ++ ++ // Callback to indicate that bulk auxiliary messages have arrived ++ VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; ++ ++ // Callback to indicate that a bulk auxiliary message has been transmitted ++ VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; ++ ++ // Callback to provide information about the connection ++ VCHI_CONNECTION_INFO connection_info; ++ ++ // Callback to notify that peer has requested disconnect ++ VCHI_CONNECTION_DISCONNECT disconnect; ++ ++ // Callback to notify that peer has requested power change ++ VCHI_CONNECTION_POWER_CONTROL power_control; ++ ++ // allocate memory suitably aligned for this connection ++ VCHI_BUFFER_ALLOCATE buffer_allocate; ++ ++ // free memory allocated by buffer_allocate ++ VCHI_BUFFER_FREE buffer_free; ++ ++}; ++ ++struct vchi_connection_t { ++ const VCHI_CONNECTION_API_T *api; ++ VCHI_CONNECTION_STATE_T *state; ++#ifdef VCHI_COARSE_LOCKING ++ struct semaphore sem; ++#endif ++}; ++ ++ ++#endif /* CONNECTION_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h b/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h +new file mode 100644 +index 0000000..8b3f767 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h +@@ -0,0 +1,204 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef _VCHI_MESSAGE_H_ ++#define _VCHI_MESSAGE_H_ ++ ++#include ++#include ++#include ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vchi/vchi_common.h" ++ ++ ++typedef enum message_event_type { ++ MESSAGE_EVENT_NONE, ++ MESSAGE_EVENT_NOP, ++ MESSAGE_EVENT_MESSAGE, ++ MESSAGE_EVENT_SLOT_COMPLETE, ++ MESSAGE_EVENT_RX_BULK_PAUSED, ++ MESSAGE_EVENT_RX_BULK_COMPLETE, ++ MESSAGE_EVENT_TX_COMPLETE, ++ MESSAGE_EVENT_MSG_DISCARDED ++} MESSAGE_EVENT_TYPE_T; ++ ++typedef enum vchi_msg_flags ++{ ++ VCHI_MSG_FLAGS_NONE = 0x0, ++ VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 ++} VCHI_MSG_FLAGS_T; ++ ++typedef enum message_tx_channel ++{ ++ MESSAGE_TX_CHANNEL_MESSAGE = 0, ++ MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_TX_CHANNEL_T; ++ ++// Macros used for cycling through bulk channels ++#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++ ++typedef enum message_rx_channel ++{ ++ MESSAGE_RX_CHANNEL_MESSAGE = 0, ++ MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_RX_CHANNEL_T; ++ ++// Message receive slot information ++typedef struct rx_msg_slot_info { ++ ++ struct rx_msg_slot_info *next; ++ //struct slot_info *prev; ++#if !defined VCHI_COARSE_LOCKING ++ struct semaphore sem; ++#endif ++ ++ uint8_t *addr; // base address of slot ++ uint32_t len; // length of slot in bytes ++ ++ uint32_t write_ptr; // hardware causes this to advance ++ uint32_t read_ptr; // this module does the reading ++ int active; // is this slot in the hardware dma fifo? ++ uint32_t msgs_parsed; // count how many messages are in this slot ++ uint32_t msgs_released; // how many messages have been released ++ void *state; // connection state information ++ uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services ++} RX_MSG_SLOTINFO_T; ++ ++// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. ++// In particular, it mustn't use addr and len - they're the client buffer, but the message ++// driver will be tasked with sending the aligned core section. ++typedef struct rx_bulk_slotinfo_t { ++ struct rx_bulk_slotinfo_t *next; ++ ++ struct semaphore *blocking; ++ ++ // needed by DMA ++ void *addr; ++ uint32_t len; ++ ++ // needed for the callback ++ void *service; ++ void *handle; ++ VCHI_FLAGS_T flags; ++} RX_BULK_SLOTINFO_T; ++ ++ ++/* ---------------------------------------------------------------------- ++ * each connection driver will have a pool of the following struct. ++ * ++ * the pool will be managed by vchi_qman_* ++ * this means there will be multiple queues (single linked lists) ++ * a given struct message_info will be on exactly one of these queues ++ * at any one time ++ * -------------------------------------------------------------------- */ ++typedef struct rx_message_info { ++ ++ struct message_info *next; ++ //struct message_info *prev; ++ ++ uint8_t *addr; ++ uint32_t len; ++ RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message ++ uint32_t tx_timestamp; ++ uint32_t rx_timestamp; ++ ++} RX_MESSAGE_INFO_T; ++ ++typedef struct { ++ MESSAGE_EVENT_TYPE_T type; ++ ++ struct { ++ // for messages ++ void *addr; // address of message ++ uint16_t slot_delta; // whether this message indicated slot delta ++ uint32_t len; // length of message ++ RX_MSG_SLOTINFO_T *slot; // slot this message is in ++ int32_t service; // service id this message is destined for ++ uint32_t tx_timestamp; // timestamp from the header ++ uint32_t rx_timestamp; // timestamp when we parsed it ++ } message; ++ ++ // FIXME: cleanup slot reporting... ++ RX_MSG_SLOTINFO_T *rx_msg; ++ RX_BULK_SLOTINFO_T *rx_bulk; ++ void *tx_handle; ++ MESSAGE_TX_CHANNEL_T tx_channel; ++ ++} MESSAGE_EVENT_T; ++ ++ ++// callbacks ++typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); ++ ++typedef struct { ++ VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; ++} VCHI_MESSAGE_DRIVER_OPEN_T; ++ ++ ++// handle to this instance of message driver (as returned by ->open) ++typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; ++ ++struct opaque_vchi_message_driver_t { ++ VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); ++ int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, int32_t enable ); ++ int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message ++ int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) ++ int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) ++ void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver ++ int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, int32_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void ++ *address, uint32_t length_avail, uint32_t max_total_length, int32_t pad_to_fill, int32_t allow_partial ); ++ ++ int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); ++ int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); ++ void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); ++ void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); ++ int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ ++ int32_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); ++ void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); ++ void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); ++}; ++ ++ ++#endif // _VCHI_MESSAGE_H_ ++ ++/****************************** End of file ***********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi.h b/drivers/misc/vc04_services/interface/vchi/vchi.h +new file mode 100644 +index 0000000..1b17e98 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi.h +@@ -0,0 +1,378 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_H_ ++#define VCHI_H_ ++ ++#include "interface/vchi/vchi_cfg.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/connections/connection.h" ++#include "vchi_mh.h" ++ ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) ++ ++#ifdef USE_VCHIQ_ARM ++#define VCHI_BULK_ALIGNED(x) 1 ++#else ++#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) ++#endif ++ ++struct vchi_version { ++ uint32_t version; ++ uint32_t version_min; ++}; ++#define VCHI_VERSION(v_) { v_, v_ } ++#define VCHI_VERSION_EX(v_, m_) { v_, m_ } ++ ++typedef enum ++{ ++ VCHI_VEC_POINTER, ++ VCHI_VEC_HANDLE, ++ VCHI_VEC_LIST ++} VCHI_MSG_VECTOR_TYPE_T; ++ ++typedef struct vchi_msg_vector_ex { ++ ++ VCHI_MSG_VECTOR_TYPE_T type; ++ union ++ { ++ // a memory handle ++ struct ++ { ++ VCHI_MEM_HANDLE_T handle; ++ uint32_t offset; ++ int32_t vec_len; ++ } handle; ++ ++ // an ordinary data pointer ++ struct ++ { ++ const void *vec_base; ++ int32_t vec_len; ++ } ptr; ++ ++ // a nested vector list ++ struct ++ { ++ struct vchi_msg_vector_ex *vec; ++ uint32_t vec_len; ++ } list; ++ } u; ++} VCHI_MSG_VECTOR_EX_T; ++ ++ ++// Construct an entry in a msg vector for a pointer (p) of length (l) ++#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } ++ ++// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) ++#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } ++ ++// Macros to manipulate 'FOURCC' values ++#define MAKE_FOURCC(x) ((int32_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) ++#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF ++ ++ ++// Opaque service information ++struct opaque_vchi_service_t; ++ ++// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, ++// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. ++typedef struct ++{ ++ struct opaque_vchi_service_t *service; ++ void *message; ++} VCHI_HELD_MSG_T; ++ ++ ++ ++// structure used to provide the information needed to open a server or a client ++typedef struct { ++ struct vchi_version version; ++ int32_t service_id; ++ VCHI_CONNECTION_T *connection; ++ uint32_t rx_fifo_size; ++ uint32_t tx_fifo_size; ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++ /* client intends to receive bulk transfers of ++ odd lengths or into unaligned buffers */ ++ int32_t want_unaligned_bulk_rx; ++ /* client intends to transmit bulk transfers of ++ odd lengths or out of unaligned buffers */ ++ int32_t want_unaligned_bulk_tx; ++ /* client wants to check CRCs on (bulk) xfers. ++ Only needs to be set at 1 end - will do both directions. */ ++ int32_t want_crc; ++} SERVICE_CREATION_T; ++ ++// Opaque handle for a VCHI instance ++typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; ++ ++// Opaque handle for a server or client ++typedef struct opaque_vchi_service_handle_t *VCHI_SERVICE_HANDLE_T; ++ ++// Service registration & startup ++typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); ++ ++typedef struct service_info_tag { ++ const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ ++ VCHI_SERVICE_INIT init; /* Service initialisation function */ ++ void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ ++} SERVICE_INFO_T; ++ ++/****************************************************************************** ++ Global funcs - implementation is specific to which side you are on (local / remote) ++ *****************************************************************************/ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, ++ const VCHI_MESSAGE_DRIVER_T * low_level); ++ ++ ++// Routine used to initialise the vchi on both local + remote connections ++extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); ++ ++extern int32_t vchi_exit( void ); ++ ++extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle ); ++ ++//When this is called, ensure that all services have no data pending. ++//Bulk transfers can remain 'queued' ++extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); ++ ++// Global control over bulk CRC checking ++extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, ++ VCHI_CRC_CONTROL_T control ); ++ ++// helper functions ++extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); ++extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); ++extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); ++ ++ ++/****************************************************************************** ++ Global service API ++ *****************************************************************************/ ++// Routine to create a named service ++extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle ); ++ ++// Routine to destory a service ++extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to open a named service ++extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle); ++ ++extern int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, ++ short *peer_version ); ++ ++// Routine to close a named service ++extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to increment ref count on a named service ++extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to decrement ref count on a named service ++extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to set a control option for a named service ++extern int32_t vchi_service_set_option( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_SERVICE_OPTION_T option, ++ int value); ++ ++// Routine to send a message across a service ++extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) and send message ++int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_EX_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// legacy scatter-gather (vector) and send message, only handles pointers ++int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to receive a msg from a service ++// Dequeue is equivalent to hold, copy into client buffer, release ++extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to look at a message in place. ++// The message is not dequeued, so a subsequent call to peek or dequeue ++// will return the same message. ++extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to remove a message after it has been read in place with peek ++// The first message on the queue is dequeued. ++extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to look at a message in place. ++// The message is dequeued, so the caller is left holding it; the descriptor is ++// filled in and must be released when the user has finished with the message. ++extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, ++ void **data, // } may be NULL, as info can be ++ uint32_t *msg_size, // } obtained from HELD_MSG_T ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_descriptor ); ++ ++// Initialise an iterator to look through messages in place ++extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++/****************************************************************************** ++ Global service support API - operations on held messages and message iterators ++ *****************************************************************************/ ++ ++// Routine to get the address of a held message ++extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the size of a held message ++extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the transmit timestamp as written into the header by the peer ++extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the reception timestamp, written as we parsed the header ++extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to release a held message after it has been processed ++extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); ++ ++// Indicates whether the iterator has a next message. ++extern int32_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); ++ ++// Return the pointer and length for the next message and advance the iterator. ++extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Remove the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); ++ ++// Hold the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, ++ VCHI_HELD_MSG_T *message ); ++ ++// Return information for the next message, and hold it, advancing the iterator. ++extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, ++ void **data, // } may be NULL ++ uint32_t *msg_size, // } ++ VCHI_HELD_MSG_T *message ); ++ ++ ++/****************************************************************************** ++ Global bulk API ++ *****************************************************************************/ ++ ++// Routine to prepare interface for a transfer from the other side ++extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++// Prepare interface for a transfer from the other side into relocatable memory. ++int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_dst, ++ uint32_t offset, ++ uint32_t data_size, ++ const VCHI_FLAGS_T flags, ++ void * const bulk_handle ); ++ ++// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) ++extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++/****************************************************************************** ++ Configuration plumbing ++ *****************************************************************************/ ++ ++// function prototypes for the different mid layers (the state info gives the different physical connections) ++extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); ++ ++// declare all message drivers here ++const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_src, ++ uint32_t offset, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++#endif /* VCHI_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h +new file mode 100644 +index 0000000..26bc2d38 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h +@@ -0,0 +1,224 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_CFG_H_ ++#define VCHI_CFG_H_ ++ ++/**************************************************************************************** ++ * Defines in this first section are part of the VCHI API and may be examined by VCHI ++ * services. ++ ***************************************************************************************/ ++ ++/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_ALIGN ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_ALIGN 16 ++# endif ++#endif ++ ++/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ ++/* May be less than or greater than VCHI_BULK_ALIGN */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_GRANULARITY ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_GRANULARITY 16 ++# endif ++#endif ++ ++/* The largest possible message to be queued with vchi_msg_queue. */ ++#ifndef VCHI_MAX_MSG_SIZE ++# if defined VCHI_LOCAL_HOST_PORT ++# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? ++# else ++# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! ++# endif ++#endif ++ ++/****************************************************************************************** ++ * Defines below are system configuration options, and should not be used by VCHI services. ++ *****************************************************************************************/ ++ ++/* How many connections can we support? A localhost implementation uses 2 connections, ++ * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW ++ * driver. */ ++#ifndef VCHI_MAX_NUM_CONNECTIONS ++# define VCHI_MAX_NUM_CONNECTIONS 3 ++#endif ++ ++/* How many services can we open per connection? Extending this doesn't cost processing time, just a small ++ * amount of static memory. */ ++#ifndef VCHI_MAX_SERVICES_PER_CONNECTION ++# define VCHI_MAX_SERVICES_PER_CONNECTION 36 ++#endif ++ ++/* Adjust if using a message driver that supports more logical TX channels */ ++#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels ++#endif ++ ++/* Adjust if using a message driver that supports more logical RX channels */ ++#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI ++#endif ++ ++/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective ++ * receive queue space, less message headers. */ ++#ifndef VCHI_NUM_READ_SLOTS ++# if defined(VCHI_LOCAL_HOST_PORT) ++# define VCHI_NUM_READ_SLOTS 4 ++# else ++# define VCHI_NUM_READ_SLOTS 48 ++# endif ++#endif ++ ++/* Do we utilise overrun facility for receive message slots? Can aid peer transmit ++ * performance. Only define on VideoCore end, talking to host. ++ */ ++//#define VCHI_MSG_RX_OVERRUN ++ ++/* How many transmit slots do we use. Generally don't need many, as the hardware driver ++ * underneath VCHI will usually have its own buffering. */ ++#ifndef VCHI_NUM_WRITE_SLOTS ++# define VCHI_NUM_WRITE_SLOTS 4 ++#endif ++ ++/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, ++ * then it's taking up too much buffer space, and the peer service will be told to stop ++ * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS ++ * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency ++ * is too high. */ ++#ifndef VCHI_XOFF_THRESHOLD ++# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) ++#endif ++ ++/* After we've sent an XOFF, the peer will be told to resume transmission once the local ++ * service has dequeued/released enough messages that it's now occupying ++ * VCHI_XON_THRESHOLD slots or fewer. */ ++#ifndef VCHI_XON_THRESHOLD ++# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) ++#endif ++ ++/* A size below which a bulk transfer omits the handshake completely and always goes ++ * via the message channel, if bulk auxiliary is being sent on that service. (The user ++ * can guarantee this by enabling unaligned transmits). ++ * Not API. */ ++#ifndef VCHI_MIN_BULK_SIZE ++# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) ++#endif ++ ++/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between ++ * speed and latency; the smaller the chunk size the better change of messages and other ++ * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not ++ * break transmissions into chunks. ++ */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI ++# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) ++#endif ++ ++/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode ++ * with multiple-line frames. Only use if the receiver can cope. */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 ++# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 ++#endif ++ ++/* How many TX messages can we have pending in our transmit slots. Once exhausted, ++ * vchi_msg_queue will be blocked. */ ++#ifndef VCHI_TX_MSG_QUEUE_SIZE ++# define VCHI_TX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing ++ * will be suspended until older messages are dequeued/released. */ ++#ifndef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* Really should be able to cope if we run out of received message descriptors, by ++ * suspending parsing as the comment above says, but we don't. This sweeps the issue ++ * under the carpet. */ ++#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++# undef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++#endif ++ ++/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit ++ * will be blocked. */ ++#ifndef VCHI_TX_BULK_QUEUE_SIZE ++# define VCHI_TX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive ++ * will be blocked. */ ++#ifndef VCHI_RX_BULK_QUEUE_SIZE ++# define VCHI_RX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* A limit on how many outstanding bulk requests we expect the peer to give us. If ++ * the peer asks for more than this, VCHI will fail and assert. The number is determined ++ * by the peer's hardware - it's the number of outstanding requests that can be queued ++ * on all bulk channels. VC3's MPHI peripheral allows 16. */ ++#ifndef VCHI_MAX_PEER_BULK_REQUESTS ++# define VCHI_MAX_PEER_BULK_REQUESTS 32 ++#endif ++ ++/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 ++ * transmitter on and off. ++ */ ++/*#define VCHI_CCP2TX_MANUAL_POWER*/ ++ ++#ifndef VCHI_CCP2TX_MANUAL_POWER ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set ++ * negative for no IDLE. ++ */ ++# ifndef VCHI_CCP2TX_IDLE_TIMEOUT ++# define VCHI_CCP2TX_IDLE_TIMEOUT 5 ++# endif ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set ++ * negative for no OFF. ++ */ ++# ifndef VCHI_CCP2TX_OFF_TIMEOUT ++# define VCHI_CCP2TX_OFF_TIMEOUT 1000 ++# endif ++ ++#endif /* VCHI_CCP2TX_MANUAL_POWER */ ++ ++#endif /* VCHI_CFG_H_ */ ++ ++/****************************** End of file **********************************/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h +new file mode 100644 +index 0000000..35dcba4 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h +@@ -0,0 +1,71 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_CFG_INTERNAL_H_ ++#define VCHI_CFG_INTERNAL_H_ ++ ++/**************************************************************************************** ++ * Control optimisation attempts. ++ ***************************************************************************************/ ++ ++// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second ++#define VCHI_COARSE_LOCKING ++ ++// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_ELIDE_BLOCK_EXIT_LOCK ++ ++// Avoid lock on non-blocking peek ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_AVOID_PEEK_LOCK ++ ++// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. ++#define VCHI_MULTIPLE_HANDLER_THREADS ++ ++// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash ++// our way through the pool of descriptors. ++#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD ++ ++// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. ++#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS ++ ++// Don't use message descriptors for TX messages that don't need them ++#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS ++ ++// Nano-locks for multiqueue ++//#define VCHI_MQUEUE_NANOLOCKS ++ ++// Lock-free(er) dequeuing ++//#define VCHI_RX_NANOLOCKS ++ ++#endif /*VCHI_CFG_INTERNAL_H_*/ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h +new file mode 100644 +index 0000000..d535a72 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h +@@ -0,0 +1,175 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_COMMON_H_ ++#define VCHI_COMMON_H_ ++ ++ ++//flags used when sending messages (must be bitmapped) ++typedef enum ++{ ++ VCHI_FLAGS_NONE = 0x0, ++ VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go ++ VCHI_FLAGS_ALLOW_PARTIAL = 0x8, ++ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, ++ VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, ++ ++ VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only ++ VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only ++ VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only ++ VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only ++ VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only ++ VCHI_FLAGS_INTERNAL = 0xFF0000 ++} VCHI_FLAGS_T; ++ ++// constants for vchi_crc_control() ++typedef enum { ++ VCHI_CRC_NOTHING = -1, ++ VCHI_CRC_PER_SERVICE = 0, ++ VCHI_CRC_EVERYTHING = 1, ++} VCHI_CRC_CONTROL_T; ++ ++//callback reasons when an event occurs on a service ++typedef enum ++{ ++ VCHI_CALLBACK_REASON_MIN, ++ ++ //This indicates that there is data available ++ //handle is the msg id that was transmitted with the data ++ // When a message is received and there was no FULL message available previously, send callback ++ // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails ++ VCHI_CALLBACK_MSG_AVAILABLE, ++ VCHI_CALLBACK_MSG_SENT, ++ VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ // This indicates that a transfer from the other side has completed ++ VCHI_CALLBACK_BULK_RECEIVED, ++ //This indicates that data queued up to be sent has now gone ++ //handle is the msg id that was used when sending the data ++ VCHI_CALLBACK_BULK_SENT, ++ VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented ++ VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ VCHI_CALLBACK_SERVICE_CLOSED, ++ ++ // this side has sent XOFF to peer due to lack of data consumption by service ++ // (suggests the service may need to take some recovery action if it has ++ // been deliberately holding off consuming data) ++ VCHI_CALLBACK_SENT_XOFF, ++ VCHI_CALLBACK_SENT_XON, ++ ++ // indicates that a bulk transfer has finished reading the source buffer ++ VCHI_CALLBACK_BULK_DATA_READ, ++ ++ // power notification events (currently host side only) ++ VCHI_CALLBACK_PEER_OFF, ++ VCHI_CALLBACK_PEER_SUSPENDED, ++ VCHI_CALLBACK_PEER_ON, ++ VCHI_CALLBACK_PEER_RESUMED, ++ VCHI_CALLBACK_FORCED_POWER_OFF, ++ ++#ifdef USE_VCHIQ_ARM ++ // some extra notifications provided by vchiq_arm ++ VCHI_CALLBACK_SERVICE_OPENED, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++#endif ++ ++ VCHI_CALLBACK_REASON_MAX ++} VCHI_CALLBACK_REASON_T; ++ ++// service control options ++typedef enum ++{ ++ VCHI_SERVICE_OPTION_MIN, ++ ++ VCHI_SERVICE_OPTION_TRACE, ++ VCHI_SERVICE_OPTION_SYNCHRONOUS, ++ ++ VCHI_SERVICE_OPTION_MAX ++} VCHI_SERVICE_OPTION_T; ++ ++ ++//Callback used by all services / bulk transfers ++typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param ++ VCHI_CALLBACK_REASON_T reason, ++ void *handle ); //for transmitting msg's only ++ ++ ++ ++/* ++ * Define vector struct for scatter-gather (vector) operations ++ * Vectors can be nested - if a vector element has negative length, then ++ * the data pointer is treated as pointing to another vector array, with ++ * '-vec_len' elements. Thus to append a header onto an existing vector, ++ * you can do this: ++ * ++ * void foo(const VCHI_MSG_VECTOR_T *v, int n) ++ * { ++ * VCHI_MSG_VECTOR_T nv[2]; ++ * nv[0].vec_base = my_header; ++ * nv[0].vec_len = sizeof my_header; ++ * nv[1].vec_base = v; ++ * nv[1].vec_len = -n; ++ * ... ++ * ++ */ ++typedef struct vchi_msg_vector { ++ const void *vec_base; ++ int32_t vec_len; ++} VCHI_MSG_VECTOR_T; ++ ++// Opaque type for a connection API ++typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; ++ ++// Opaque type for a message driver ++typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; ++ ++ ++// Iterator structure for reading ahead through received message queue. Allocated by client, ++// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. ++// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - ++// will not proceed to messages received since. Behaviour is undefined if an iterator ++// is used again after messages for that service are removed/dequeued by any ++// means other than vchi_msg_iter_... calls on the iterator itself. ++typedef struct { ++ struct opaque_vchi_service_t *service; ++ void *last; ++ void *next; ++ void *remove; ++} VCHI_MSG_ITER_T; ++ ++ ++#endif // VCHI_COMMON_H_ +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +new file mode 100644 +index 0000000..198bd07 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +@@ -0,0 +1,42 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHI_MH_H_ ++#define VCHI_MH_H_ ++ ++#include ++ ++typedef int32_t VCHI_MEM_HANDLE_T; ++#define VCHI_MEM_HANDLE_INVALID 0 ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h +new file mode 100644 +index 0000000..ad398ba +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h +@@ -0,0 +1,40 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_VCHIQ_H ++#define VCHIQ_VCHIQ_H ++ ++#include "vchiq_if.h" ++#include "vchiq_util.h" ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h +new file mode 100644 +index 0000000..7ea5c64 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h +@@ -0,0 +1,42 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_2835_H ++#define VCHIQ_2835_H ++ ++#include "vchiq_pagelist.h" ++ ++#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0 ++#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 ++ ++#endif /* VCHIQ_2835_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +new file mode 100644 +index 0000000..7e7b09f +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -0,0 +1,562 @@ +/** + * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * @@ -84430,26135 +86360,11344 @@ index 0000000..340f004 + */ + +#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#define RNG_CTRL (0x0) -+#define RNG_STATUS (0x4) -+#define RNG_DATA (0x8) -+#define RNG_FF_THRESHOLD (0xc) -+ -+/* enable rng */ -+#define RNG_RBGEN 0x1 -+/* double speed, less random mode */ -+#define RNG_RBG2X 0x2 -+ -+/* the initial numbers generated are "less random" so will be discarded */ -+#define RNG_WARMUP_COUNT 0x40000 -+ -+static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer) -+{ -+ void __iomem *rng_base = (void __iomem *)rng->priv; -+ unsigned words; -+ /* wait for a random number to be in fifo */ -+ do { -+ words = __raw_readl(rng_base + RNG_STATUS)>>24; -+ } -+ while (words == 0); -+ /* read the random number */ -+ *buffer = __raw_readl(rng_base + RNG_DATA); -+ return 4; -+} -+ -+static struct hwrng bcm2708_rng_ops = { -+ .name = "bcm2708", -+ .data_read = bcm2708_rng_data_read, -+}; -+ -+static int __init bcm2708_rng_init(void) -+{ -+ void __iomem *rng_base; -+ int err; -+ -+ /* map peripheral */ -+ rng_base = ioremap(RNG_BASE, 0x10); -+ pr_info("bcm2708_rng_init=%p\n", rng_base); -+ if (!rng_base) { -+ pr_err("bcm2708_rng_init failed to ioremap\n"); -+ return -ENOMEM; -+ } -+ bcm2708_rng_ops.priv = (unsigned long)rng_base; -+ -+ /* set warm-up count & enable */ -+ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); -+ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); -+ -+ /* register driver */ -+ err = hwrng_register(&bcm2708_rng_ops); -+ if (err) { -+ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err); -+ iounmap(rng_base); -+ } -+ return err; -+} -+ -+static void __exit bcm2708_rng_exit(void) -+{ -+ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv; -+ pr_info("bcm2708_rng_exit\n"); -+ /* disable rng hardware */ -+ __raw_writel(0, rng_base + RNG_CTRL); -+ /* unregister driver */ -+ hwrng_unregister(&bcm2708_rng_ops); -+ iounmap(rng_base); -+} -+ -+module_init(bcm2708_rng_init); -+module_exit(bcm2708_rng_exit); -+ -+MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver"); -+MODULE_LICENSE("GPL and additional rights"); - -From f543037234f72aed89e2239f62f2dce7ea94aefa Mon Sep 17 00:00:00 2001 -From: Aron Szabo -Date: Sat, 16 Jun 2012 12:15:55 +0200 -Subject: [PATCH 016/172] lirc: added support for RaspberryPi GPIO - -lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others -See: https://github.com/raspberrypi/linux/issues/525 - -lirc: Remove restriction on gpio pins that can be used with lirc - -Compute Module, for example could use different pins - -lirc_rpi: Add parameter to specify input pin pull - -Depending on the connected IR circuitry it might be desirable to change the -gpios internal pull from it pull-down default behaviour. Add a module -parameter to allow the user to set it explicitly. - -Signed-off-by: Julian Scheel - -lirc-rpi: Use the higher-level irq control functions - -This module used to access the irq_chip methods of the -gpio controller directly, rather than going through the -standard enable_irq/irq_set_irq_type functions. This -caused problems on pinctrl-bcm2835 which only implements -the irq_enable/disable methods and not irq_unmask/mask. - -lirc-rpi: Correct the interrupt usage - -1) Correct the use of enable_irq (i.e. don't call it so often) -2) Correct the shutdown sequence. -3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier ---- - drivers/staging/media/lirc/Kconfig | 6 + - drivers/staging/media/lirc/Makefile | 1 + - drivers/staging/media/lirc/lirc_rpi.c | 659 ++++++++++++++++++++++++++++++++++ - 3 files changed, 666 insertions(+) - create mode 100644 drivers/staging/media/lirc/lirc_rpi.c - -diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig -index 6879c46..77c69f2 100644 ---- a/drivers/staging/media/lirc/Kconfig -+++ b/drivers/staging/media/lirc/Kconfig -@@ -32,6 +32,12 @@ config LIRC_PARALLEL - help - Driver for Homebrew Parallel Port Receivers - -+config LIRC_RPI -+ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" -+ depends on LIRC -+ help -+ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi -+ - config LIRC_SASEM - tristate "Sasem USB IR Remote" - depends on LIRC && USB -diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile -index 5430adf..9e53cd0 100644 ---- a/drivers/staging/media/lirc/Makefile -+++ b/drivers/staging/media/lirc/Makefile -@@ -6,6 +6,7 @@ - obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o - obj-$(CONFIG_LIRC_IMON) += lirc_imon.o - obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o -+obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o - obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o - obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o - obj-$(CONFIG_LIRC_SIR) += lirc_sir.o -diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -new file mode 100644 -index 0000000..c688364 ---- /dev/null -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -0,0 +1,659 @@ -+/* -+ * lirc_rpi.c -+ * -+ * lirc_rpi - Device driver that records pulse- and pause-lengths -+ * (space-lengths) (just like the lirc_serial driver does) -+ * between GPIO interrupt events on the Raspberry Pi. -+ * Lots of code has been taken from the lirc_serial module, -+ * so I would like say thanks to the authors. -+ * -+ * Copyright (C) 2012 Aron Robert Szabo , -+ * Michael Bishop -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include ++#include +#include +#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include +#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include ++#include ++#include ++#include + -+#include ++#include + -+#define LIRC_DRIVER_NAME "lirc_rpi" -+#define RBUF_LEN 256 -+#define LIRC_TRANSMITTER_LATENCY 50 ++#include ++#include + -+#ifndef MAX_UDELAY_MS -+#define MAX_UDELAY_US 5000 -+#else -+#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) -+#endif ++#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) + -+#define dprintk(fmt, args...) \ -+ do { \ -+ if (debug) \ -+ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ -+ fmt, ## args); \ -+ } while (0) ++#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 ++#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x)) + -+/* module parameters */ ++#include "vchiq_arm.h" ++#include "vchiq_2835.h" ++#include "vchiq_connected.h" ++#include "vchiq_killable.h" + -+/* set the default GPIO input pin */ -+static int gpio_in_pin = 18; -+/* set the default pull behaviour for input pin */ -+static int gpio_in_pull = BCM2708_PULL_DOWN; -+/* set the default GPIO output pin */ -+static int gpio_out_pin = 17; -+/* enable debugging messages */ -+static bool debug; -+/* -1 = auto, 0 = active high, 1 = active low */ -+static int sense = -1; -+/* use softcarrier by default */ -+static bool softcarrier = 1; -+/* 0 = do not invert output, 1 = invert output */ -+static bool invert = 0; ++#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) + -+struct gpio_chip *gpiochip; -+static int irq_num; ++typedef struct vchiq_2835_state_struct { ++ int inited; ++ VCHIQ_ARM_STATE_T arm_state; ++} VCHIQ_2835_ARM_STATE_T; + -+/* forward declarations */ -+static long send_pulse(unsigned long length); -+static void send_space(long length); -+static void lirc_rpi_exit(void); ++static char *g_slot_mem; ++static int g_slot_mem_size; ++dma_addr_t g_slot_phys; ++static FRAGMENTS_T *g_fragments_base; ++static FRAGMENTS_T *g_free_fragments; ++struct semaphore g_free_fragments_sema; + -+static struct platform_device *lirc_rpi_dev; -+static struct timeval lasttv = { 0, 0 }; -+static struct lirc_buffer rbuf; -+static spinlock_t lock; ++extern int vchiq_arm_log_level; + -+/* initialized/set in init_timing_params() */ -+static unsigned int freq = 38000; -+static unsigned int duty_cycle = 50; -+static unsigned long period; -+static unsigned long pulse_width; -+static unsigned long space_width; ++static DEFINE_SEMAPHORE(g_free_fragments_mutex); + -+static void safe_udelay(unsigned long usecs) ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id); ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist); ++ ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual); ++ ++int __init ++vchiq_platform_init(VCHIQ_STATE_T *state) +{ -+ while (usecs > MAX_UDELAY_US) { -+ udelay(MAX_UDELAY_US); -+ usecs -= MAX_UDELAY_US; ++ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; ++ int frag_mem_size; ++ int err; ++ int i; ++ ++ /* Allocate space for the channels in coherent memory */ ++ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); ++ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS); ++ ++ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size, ++ &g_slot_phys, GFP_ATOMIC); ++ ++ if (!g_slot_mem) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocate channel memory"); ++ err = -ENOMEM; ++ goto failed_alloc; + } -+ udelay(usecs); -+} + -+static int init_timing_params(unsigned int new_duty_cycle, -+ unsigned int new_freq) -+{ -+ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= -+ LIRC_TRANSMITTER_LATENCY) -+ return -EINVAL; -+ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= -+ LIRC_TRANSMITTER_LATENCY) -+ return -EINVAL; -+ duty_cycle = new_duty_cycle; -+ freq = new_freq; -+ period = 1000 * 1000000L / freq; -+ pulse_width = period * duty_cycle / 100; -+ space_width = period - pulse_width; -+ dprintk("in init_timing_params, freq=%d pulse=%ld, " -+ "space=%ld\n", freq, pulse_width, space_width); -+ return 0; -+} ++ WARN_ON(((int)g_slot_mem & (PAGE_SIZE - 1)) != 0); + -+static long send_pulse_softcarrier(unsigned long length) -+{ -+ int flag; -+ unsigned long actual, target; -+ unsigned long actual_us, initial_us, target_us; -+ -+ length *= 1000; -+ -+ actual = 0; target = 0; flag = 0; -+ read_current_timer(&actual_us); -+ -+ while (actual < length) { -+ if (flag) { -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ target += space_width; -+ } else { -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); -+ target += pulse_width; -+ } -+ initial_us = actual_us; -+ target_us = actual_us + (target - actual) / 1000; -+ /* -+ * Note - we've checked in ioctl that the pulse/space -+ * widths are big enough so that d is > 0 -+ */ -+ if ((int)(target_us - actual_us) > 0) -+ udelay(target_us - actual_us); -+ read_current_timer(&actual_us); -+ actual += (actual_us - initial_us) * 1000; -+ flag = !flag; ++ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size); ++ if (!vchiq_slot_zero) { ++ err = -EINVAL; ++ goto failed_init_slots; + } -+ return (actual-length) / 1000; ++ ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = ++ (int)g_slot_phys + g_slot_mem_size; ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = ++ MAX_FRAGMENTS; ++ ++ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size); ++ g_slot_mem_size += frag_mem_size; ++ ++ g_free_fragments = g_fragments_base; ++ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { ++ *(FRAGMENTS_T **)&g_fragments_base[i] = ++ &g_fragments_base[i + 1]; ++ } ++ *(FRAGMENTS_T **)&g_fragments_base[i] = NULL; ++ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); ++ ++ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) != ++ VCHIQ_SUCCESS) { ++ err = -EINVAL; ++ goto failed_vchiq_init; ++ } ++ ++ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq, ++ IRQF_IRQPOLL, "VCHIQ doorbell", ++ state); ++ if (err < 0) { ++ vchiq_log_error(vchiq_arm_log_level, "%s: failed to register " ++ "irq=%d err=%d", __func__, ++ VCHIQ_DOORBELL_IRQ, err); ++ goto failed_request_irq; ++ } ++ ++ /* Send the base address of the slots to VideoCore */ ++ ++ dsb(); /* Ensure all writes have completed */ ++ ++ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys); ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq_init - done (slots %x, phys %x)", ++ (unsigned int)vchiq_slot_zero, g_slot_phys); ++ ++ vchiq_call_connected_callbacks(); ++ ++ return 0; ++ ++failed_request_irq: ++failed_vchiq_init: ++failed_init_slots: ++ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys); ++ ++failed_alloc: ++ return err; +} + -+static long send_pulse(unsigned long length) ++void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state) +{ -+ if (length <= 0) -+ return 0; ++ free_irq(VCHIQ_DOORBELL_IRQ, state); ++ dma_free_coherent(NULL, g_slot_mem_size, ++ g_slot_mem, g_slot_phys); ++} + -+ if (softcarrier) { -+ return send_pulse_softcarrier(length); ++ ++VCHIQ_STATUS_T ++vchiq_platform_init_state(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ state->platform_state = kzalloc(sizeof(VCHIQ_2835_ARM_STATE_T), GFP_KERNEL); ++ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 1; ++ status = vchiq_arm_init_state(state, &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state); ++ if(status != VCHIQ_SUCCESS) ++ { ++ ((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited = 0; ++ } ++ return status; ++} ++ ++VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state) ++{ ++ if(!((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->inited) ++ { ++ BUG(); ++ } ++ return &((VCHIQ_2835_ARM_STATE_T*)state->platform_state)->arm_state; ++} ++ ++void ++remote_event_signal(REMOTE_EVENT_T *event) ++{ ++ wmb(); ++ ++ event->fired = 1; ++ ++ dsb(); /* data barrier operation */ ++ ++ if (event->armed) { ++ /* trigger vc interrupt */ ++ ++ writel(0, __io_address(ARM_0_BELL2)); ++ } ++} ++ ++int ++vchiq_copy_from_user(void *dst, const void *src, int size) ++{ ++ if ((uint32_t)src < TASK_SIZE) { ++ return copy_from_user(dst, src, size); + } else { -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); -+ safe_udelay(length); ++ memcpy(dst, src, size); + return 0; + } +} + -+static void send_space(long length) ++VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, ++ void *offset, int size, int dir) +{ -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ if (length <= 0) -+ return; -+ safe_udelay(length); ++ PAGELIST_T *pagelist; ++ int ret; ++ ++ WARN_ON(memhandle != VCHI_MEM_HANDLE_INVALID); ++ ++ ret = create_pagelist((char __user *)offset, size, ++ (dir == VCHIQ_BULK_RECEIVE) ++ ? PAGELIST_READ ++ : PAGELIST_WRITE, ++ current, ++ &pagelist); ++ if (ret != 0) ++ return VCHIQ_ERROR; ++ ++ bulk->handle = memhandle; ++ bulk->data = VCHIQ_ARM_ADDRESS(pagelist); ++ ++ /* Store the pagelist address in remote_data, which isn't used by the ++ slave. */ ++ bulk->remote_data = pagelist; ++ ++ return VCHIQ_SUCCESS; +} + -+static void rbwrite(int l) ++void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk) +{ -+ if (lirc_buffer_full(&rbuf)) { -+ /* no new signals will be accepted */ -+ dprintk("Buffer overrun\n"); -+ return; -+ } -+ lirc_buffer_write(&rbuf, (void *)&l); ++ if (bulk && bulk->remote_data && bulk->actual) ++ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual); +} + -+static void frbwrite(int l) ++void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk) +{ -+ /* simple noise filter */ -+ static int pulse, space; -+ static unsigned int ptr; -+ -+ if (ptr > 0 && (l & PULSE_BIT)) { -+ pulse += l & PULSE_MASK; -+ if (pulse > 250) { -+ rbwrite(space); -+ rbwrite(pulse | PULSE_BIT); -+ ptr = 0; -+ pulse = 0; -+ } -+ return; -+ } -+ if (!(l & PULSE_BIT)) { -+ if (ptr == 0) { -+ if (l > 20000) { -+ space = l; -+ ptr++; -+ return; -+ } -+ } else { -+ if (l > 20000) { -+ space += pulse; -+ if (space > PULSE_MASK) -+ space = PULSE_MASK; -+ space += l; -+ if (space > PULSE_MASK) -+ space = PULSE_MASK; -+ pulse = 0; -+ return; -+ } -+ rbwrite(space); -+ rbwrite(pulse | PULSE_BIT); -+ ptr = 0; -+ pulse = 0; -+ } -+ } -+ rbwrite(l); ++ /* ++ * This should only be called on the master (VideoCore) side, but ++ * provide an implementation to avoid the need for ifdefery. ++ */ ++ BUG(); +} + -+static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) ++void ++vchiq_dump_platform_state(void *dump_context) +{ -+ struct timeval tv; -+ long deltv; -+ int data; -+ int signal; -+ -+ /* use the GPIO signal level */ -+ signal = gpiochip->get(gpiochip, gpio_in_pin); -+ -+ if (sense != -1) { -+ /* get current time */ -+ do_gettimeofday(&tv); -+ -+ /* calc time since last interrupt in microseconds */ -+ deltv = tv.tv_sec-lasttv.tv_sec; -+ if (tv.tv_sec < lasttv.tv_sec || -+ (tv.tv_sec == lasttv.tv_sec && -+ tv.tv_usec < lasttv.tv_usec)) { -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": AIEEEE: your clock just jumped backwards\n"); -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": %d %d %lx %lx %lx %lx\n", signal, sense, -+ tv.tv_sec, lasttv.tv_sec, -+ tv.tv_usec, lasttv.tv_usec); -+ data = PULSE_MASK; -+ } else if (deltv > 15) { -+ data = PULSE_MASK; /* really long time */ -+ if (!(signal^sense)) { -+ /* sanity check */ -+ printk(KERN_WARNING LIRC_DRIVER_NAME -+ ": AIEEEE: %d %d %lx %lx %lx %lx\n", -+ signal, sense, tv.tv_sec, lasttv.tv_sec, -+ tv.tv_usec, lasttv.tv_usec); -+ /* -+ * detecting pulse while this -+ * MUST be a space! -+ */ -+ sense = sense ? 0 : 1; -+ } -+ } else { -+ data = (int) (deltv*1000000 + -+ (tv.tv_usec - lasttv.tv_usec)); -+ } -+ frbwrite(signal^sense ? data : (data|PULSE_BIT)); -+ lasttv = tv; -+ wake_up_interruptible(&rbuf.wait_poll); -+ } -+ -+ return IRQ_HANDLED; ++ char buf[80]; ++ int len; ++ len = snprintf(buf, sizeof(buf), ++ " Platform: 2835 (VC master)"); ++ vchiq_dump(dump_context, buf, len + 1); +} + -+static int is_right_chip(struct gpio_chip *chip, void *data) ++VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state) +{ -+ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); -+ -+ if (strcmp(data, chip->label) == 0) -+ return 1; -+ return 0; ++ return VCHIQ_ERROR; +} + -+static int init_port(void) ++VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state) +{ -+ int i, nlow, nhigh, ret; ++ return VCHIQ_SUCCESS; ++} + -+ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++void ++vchiq_platform_paused(VCHIQ_STATE_T *state) ++{ ++} + -+ if (!gpiochip) -+ return -ENODEV; ++void ++vchiq_platform_resumed(VCHIQ_STATE_T *state) ++{ ++} + -+ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { -+ printk(KERN_ALERT LIRC_DRIVER_NAME -+ ": cant claim gpio pin %d\n", gpio_out_pin); -+ ret = -ENODEV; -+ goto exit_init_port; ++int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state) ++{ ++ return 1; // autosuspend not supported - videocore always wanted ++} ++ ++int ++vchiq_platform_use_suspend_timer(void) ++{ ++ return 0; ++} ++void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state) ++{ ++ vchiq_log_info((vchiq_arm_log_level>=VCHIQ_LOG_INFO),"Suspend timer not in use"); ++} ++void ++vchiq_platform_handle_timeout(VCHIQ_STATE_T *state) ++{ ++ (void)state; ++} ++/* ++ * Local functions ++ */ ++ ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id) ++{ ++ VCHIQ_STATE_T *state = dev_id; ++ irqreturn_t ret = IRQ_NONE; ++ unsigned int status; ++ ++ /* Read (and clear) the doorbell */ ++ status = readl(__io_address(ARM_0_BELL0)); ++ ++ if (status & 0x4) { /* Was the doorbell rung? */ ++ remote_event_pollall(state); ++ ret = IRQ_HANDLED; + } + -+ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { -+ printk(KERN_ALERT LIRC_DRIVER_NAME -+ ": cant claim gpio pin %d\n", gpio_in_pin); -+ ret = -ENODEV; -+ goto exit_gpio_free_out_pin; -+ } -+ -+ bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); -+ gpiochip->direction_input(gpiochip, gpio_in_pin); -+ gpiochip->direction_output(gpiochip, gpio_out_pin, 1); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); -+ -+ irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); -+ dprintk("to_irq %d\n", irq_num); -+ -+ /* if pin is high, then this must be an active low receiver. */ -+ if (sense == -1) { -+ /* wait 1/2 sec for the power supply */ -+ msleep(500); -+ -+ /* -+ * probe 9 times every 0.04s, collect "votes" for -+ * active high/low -+ */ -+ nlow = 0; -+ nhigh = 0; -+ for (i = 0; i < 9; i++) { -+ if (gpiochip->get(gpiochip, gpio_in_pin)) -+ nlow++; -+ else -+ nhigh++; -+ msleep(40); -+ } -+ sense = (nlow >= nhigh ? 1 : 0); -+ printk(KERN_INFO LIRC_DRIVER_NAME -+ ": auto-detected active %s receiver on GPIO pin %d\n", -+ sense ? "low" : "high", gpio_in_pin); -+ } else { -+ printk(KERN_INFO LIRC_DRIVER_NAME -+ ": manually using active %s receiver on GPIO pin %d\n", -+ sense ? "low" : "high", gpio_in_pin); -+ } -+ -+ return 0; -+ -+ exit_gpio_free_out_pin: -+ gpio_free(gpio_out_pin); -+ -+ exit_init_port: + return ret; +} + -+// called when the character device is opened -+static int set_use_inc(void *data) ++/* There is a potential problem with partial cache lines (pages?) ++** at the ends of the block when reading. If the CPU accessed anything in ++** the same line (page?) then it may have pulled old data into the cache, ++** obscuring the new data underneath. We can solve this by transferring the ++** partial cache lines separately, and allowing the ARM to copy into the ++** cached area. ++ ++** N.B. This implementation plays slightly fast and loose with the Linux ++** driver programming rules, e.g. its use of __virt_to_bus instead of ++** dma_map_single, but it isn't a multi-platform driver and it benefits ++** from increased speed as a result. ++*/ ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist) +{ -+ int result; ++ PAGELIST_T *pagelist; ++ struct page **pages; ++ struct page *page; ++ unsigned long *addrs; ++ unsigned int num_pages, offset, i; ++ char *addr, *base_addr, *next_addr; ++ int run, addridx, actual_pages; ++ unsigned long *need_release; + -+ /* initialize timestamp */ -+ do_gettimeofday(&lasttv); ++ offset = (unsigned int)buf & (PAGE_SIZE - 1); ++ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; + -+ result = request_irq(irq_num, -+ (irq_handler_t) irq_handler, -+ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, -+ LIRC_DRIVER_NAME, (void*) 0); ++ *ppagelist = NULL; + -+ switch (result) { -+ case -EBUSY: -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": IRQ %d is busy\n", -+ irq_num); -+ return -EBUSY; -+ case -EINVAL: -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": Bad irq number or handler\n"); -+ return -EINVAL; -+ default: -+ dprintk("Interrupt %d obtained\n", -+ irq_num); -+ break; -+ }; ++ /* Allocate enough storage to hold the page pointers and the page ++ ** list ++ */ ++ pagelist = kmalloc(sizeof(PAGELIST_T) + ++ (num_pages * sizeof(unsigned long)) + ++ sizeof(unsigned long) + ++ (num_pages * sizeof(pages[0])), ++ GFP_KERNEL); + -+ /* initialize pulse/space widths */ -+ init_timing_params(duty_cycle, freq); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "create_pagelist - %x", (unsigned int)pagelist); ++ if (!pagelist) ++ return -ENOMEM; ++ ++ addrs = pagelist->addrs; ++ need_release = (unsigned long *)(addrs + num_pages); ++ pages = (struct page **)(addrs + num_pages + 1); ++ ++ if (is_vmalloc_addr(buf)) { ++ for (actual_pages = 0; actual_pages < num_pages; actual_pages++) { ++ pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE)); ++ } ++ *need_release = 0; /* do not try and release vmalloc pages */ ++ } else { ++ down_read(&task->mm->mmap_sem); ++ actual_pages = get_user_pages(task, task->mm, ++ (unsigned long)buf & ~(PAGE_SIZE - 1), ++ num_pages, ++ (type == PAGELIST_READ) /*Write */ , ++ 0 /*Force */ , ++ pages, ++ NULL /*vmas */); ++ up_read(&task->mm->mmap_sem); ++ ++ if (actual_pages != num_pages) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "create_pagelist - only %d/%d pages locked", ++ actual_pages, ++ num_pages); ++ ++ /* This is probably due to the process being killed */ ++ while (actual_pages > 0) ++ { ++ actual_pages--; ++ page_cache_release(pages[actual_pages]); ++ } ++ kfree(pagelist); ++ if (actual_pages == 0) ++ actual_pages = -ENOMEM; ++ return actual_pages; ++ } ++ *need_release = 1; /* release user pages */ ++ } ++ ++ pagelist->length = count; ++ pagelist->type = type; ++ pagelist->offset = offset; ++ ++ /* Group the pages into runs of contiguous pages */ ++ ++ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0])); ++ next_addr = base_addr + PAGE_SIZE; ++ addridx = 0; ++ run = 0; ++ ++ for (i = 1; i < num_pages; i++) { ++ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i])); ++ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) { ++ next_addr += PAGE_SIZE; ++ run++; ++ } else { ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ base_addr = addr; ++ next_addr = addr + PAGE_SIZE; ++ run = 0; ++ } ++ } ++ ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ ++ /* Partial cache lines (fragments) require special measures */ ++ if ((type == PAGELIST_READ) && ++ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) || ++ ((pagelist->offset + pagelist->length) & ++ (CACHE_LINE_SIZE - 1)))) { ++ FRAGMENTS_T *fragments; ++ ++ if (down_interruptible(&g_free_fragments_sema) != 0) { ++ kfree(pagelist); ++ return -EINTR; ++ } ++ ++ WARN_ON(g_free_fragments == NULL); ++ ++ down(&g_free_fragments_mutex); ++ fragments = (FRAGMENTS_T *) g_free_fragments; ++ WARN_ON(fragments == NULL); ++ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments; ++ up(&g_free_fragments_mutex); ++ pagelist->type = ++ PAGELIST_READ_WITH_FRAGMENTS + (fragments - ++ g_fragments_base); ++ } ++ ++ for (page = virt_to_page(pagelist); ++ page <= virt_to_page(addrs + num_pages - 1); page++) { ++ flush_dcache_page(page); ++ } ++ ++ *ppagelist = pagelist; + + return 0; +} + -+static void set_use_dec(void *data) ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual) +{ -+ /* GPIO Pin Falling/Rising Edge Detect Disable */ -+ irq_set_irq_type(irq_num, 0); -+ disable_irq(irq_num); ++ unsigned long *need_release; ++ struct page **pages; ++ unsigned int num_pages, i; + -+ free_irq(irq_num, (void *) 0); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "free_pagelist - %x, %d", (unsigned int)pagelist, actual); + -+ dprintk(KERN_INFO LIRC_DRIVER_NAME -+ ": freed IRQ %d\n", irq_num); -+} ++ num_pages = ++ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / ++ PAGE_SIZE; + -+static ssize_t lirc_write(struct file *file, const char *buf, -+ size_t n, loff_t *ppos) -+{ -+ int i, count; -+ unsigned long flags; -+ long delta = 0; -+ int *wbuf; ++ need_release = (unsigned long *)(pagelist->addrs + num_pages); ++ pages = (struct page **)(pagelist->addrs + num_pages + 1); + -+ count = n / sizeof(int); -+ if (n % sizeof(int) || count % 2 == 0) -+ return -EINVAL; -+ wbuf = memdup_user(buf, n); -+ if (IS_ERR(wbuf)) -+ return PTR_ERR(wbuf); -+ spin_lock_irqsave(&lock, flags); ++ /* Deal with any partial cache lines (fragments) */ ++ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { ++ FRAGMENTS_T *fragments = g_fragments_base + ++ (pagelist->type - PAGELIST_READ_WITH_FRAGMENTS); ++ int head_bytes, tail_bytes; ++ head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & ++ (CACHE_LINE_SIZE - 1); ++ tail_bytes = (pagelist->offset + actual) & ++ (CACHE_LINE_SIZE - 1); + -+ for (i = 0; i < count; i++) { -+ if (i%2) -+ send_space(wbuf[i] - delta); -+ else -+ delta = send_pulse(wbuf[i]); ++ if ((actual >= 0) && (head_bytes != 0)) { ++ if (head_bytes > actual) ++ head_bytes = actual; ++ ++ memcpy((char *)page_address(pages[0]) + ++ pagelist->offset, ++ fragments->headbuf, ++ head_bytes); ++ } ++ if ((actual >= 0) && (head_bytes < actual) && ++ (tail_bytes != 0)) { ++ memcpy((char *)page_address(pages[num_pages - 1]) + ++ ((pagelist->offset + actual) & ++ (PAGE_SIZE - 1) & ~(CACHE_LINE_SIZE - 1)), ++ fragments->tailbuf, tail_bytes); ++ } ++ ++ down(&g_free_fragments_mutex); ++ *(FRAGMENTS_T **) fragments = g_free_fragments; ++ g_free_fragments = fragments; ++ up(&g_free_fragments_mutex); ++ up(&g_free_fragments_sema); + } -+ gpiochip->set(gpiochip, gpio_out_pin, invert); + -+ spin_unlock_irqrestore(&lock, flags); -+ kfree(wbuf); -+ return n; ++ if (*need_release) { ++ for (i = 0; i < num_pages; i++) { ++ if (pagelist->type != PAGELIST_WRITE) ++ set_page_dirty(pages[i]); ++ ++ page_cache_release(pages[i]); ++ } ++ } ++ ++ kfree(pagelist); ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +new file mode 100644 +index 0000000..0ad9656 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -0,0 +1,2884 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "vchiq_core.h" ++#include "vchiq_ioctl.h" ++#include "vchiq_arm.h" ++#include "vchiq_debugfs.h" ++#include "vchiq_killable.h" ++ ++#define DEVICE_NAME "vchiq" ++ ++/* Override the default prefix, which would be vchiq_arm (from the filename) */ ++#undef MODULE_PARAM_PREFIX ++#define MODULE_PARAM_PREFIX DEVICE_NAME "." ++ ++#define VCHIQ_MINOR 0 ++ ++/* Some per-instance constants */ ++#define MAX_COMPLETIONS 16 ++#define MAX_SERVICES 64 ++#define MAX_ELEMENTS 8 ++#define MSG_QUEUE_SIZE 64 ++ ++#define KEEPALIVE_VER 1 ++#define KEEPALIVE_VER_MIN KEEPALIVE_VER ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++int vchiq_arm_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_susp_log_level = VCHIQ_LOG_ERROR; ++ ++#define SUSPEND_TIMER_TIMEOUT_MS 100 ++#define SUSPEND_RETRY_TIMER_TIMEOUT_MS 1000 ++ ++#define VC_SUSPEND_NUM_OFFSET 3 /* number of values before idle which are -ve */ ++static const char *const suspend_state_names[] = { ++ "VC_SUSPEND_FORCE_CANCELED", ++ "VC_SUSPEND_REJECTED", ++ "VC_SUSPEND_FAILED", ++ "VC_SUSPEND_IDLE", ++ "VC_SUSPEND_REQUESTED", ++ "VC_SUSPEND_IN_PROGRESS", ++ "VC_SUSPEND_SUSPENDED" ++}; ++#define VC_RESUME_NUM_OFFSET 1 /* number of values before idle which are -ve */ ++static const char *const resume_state_names[] = { ++ "VC_RESUME_FAILED", ++ "VC_RESUME_IDLE", ++ "VC_RESUME_REQUESTED", ++ "VC_RESUME_IN_PROGRESS", ++ "VC_RESUME_RESUMED" ++}; ++/* The number of times we allow force suspend to timeout before actually ++** _forcing_ suspend. This is to cater for SW which fails to release vchiq ++** correctly - we don't want to prevent ARM suspend indefinitely in this case. ++*/ ++#define FORCE_SUSPEND_FAIL_MAX 8 ++ ++/* The time in ms allowed for videocore to go idle when force suspend has been ++ * requested */ ++#define FORCE_SUSPEND_TIMEOUT_MS 200 ++ ++ ++static void suspend_timer_callback(unsigned long context); ++ ++ ++typedef struct user_service_struct { ++ VCHIQ_SERVICE_T *service; ++ void *userdata; ++ VCHIQ_INSTANCE_T instance; ++ char is_vchi; ++ char dequeue_pending; ++ char close_pending; ++ int message_available_pos; ++ int msg_insert; ++ int msg_remove; ++ struct semaphore insert_event; ++ struct semaphore remove_event; ++ struct semaphore close_event; ++ VCHIQ_HEADER_T * msg_queue[MSG_QUEUE_SIZE]; ++} USER_SERVICE_T; ++ ++struct bulk_waiter_node { ++ struct bulk_waiter bulk_waiter; ++ int pid; ++ struct list_head list; ++}; ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; ++ int completion_insert; ++ int completion_remove; ++ struct semaphore insert_event; ++ struct semaphore remove_event; ++ struct mutex completion_mutex; ++ ++ int connected; ++ int closing; ++ int pid; ++ int mark; ++ int use_close_delivered; ++ int trace; ++ ++ struct list_head bulk_waiter_list; ++ struct mutex bulk_waiter_list_mutex; ++ ++ VCHIQ_DEBUGFS_NODE_T debugfs_node; ++}; ++ ++typedef struct dump_context_struct { ++ char __user *buf; ++ size_t actual; ++ size_t space; ++ loff_t offset; ++} DUMP_CONTEXT_T; ++ ++static struct cdev vchiq_cdev; ++static dev_t vchiq_devid; ++static VCHIQ_STATE_T g_state; ++static struct class *vchiq_class; ++static struct device *vchiq_dev; ++static DEFINE_SPINLOCK(msg_queue_spinlock); ++ ++static const char *const ioctl_names[] = { ++ "CONNECT", ++ "SHUTDOWN", ++ "CREATE_SERVICE", ++ "REMOVE_SERVICE", ++ "QUEUE_MESSAGE", ++ "QUEUE_BULK_TRANSMIT", ++ "QUEUE_BULK_RECEIVE", ++ "AWAIT_COMPLETION", ++ "DEQUEUE_MESSAGE", ++ "GET_CLIENT_ID", ++ "GET_CONFIG", ++ "CLOSE_SERVICE", ++ "USE_SERVICE", ++ "RELEASE_SERVICE", ++ "SET_SERVICE_OPTION", ++ "DUMP_PHYS_MEM", ++ "LIB_VERSION", ++ "CLOSE_DELIVERED" ++}; ++ ++vchiq_static_assert((sizeof(ioctl_names)/sizeof(ioctl_names[0])) == ++ (VCHIQ_IOC_MAX + 1)); ++ ++static void ++dump_phys_mem(void *virt_addr, uint32_t num_bytes); ++ ++/**************************************************************************** ++* ++* add_completion ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, USER_SERVICE_T *user_service, ++ void *bulk_userdata) ++{ ++ VCHIQ_COMPLETION_DATA_T *completion; ++ DEBUG_INITIALISE(g_state.local) ++ ++ while (instance->completion_insert == ++ (instance->completion_remove + MAX_COMPLETIONS)) { ++ /* Out of space - wait for the client */ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "add_completion - completion queue full"); ++ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); ++ if (down_interruptible(&instance->remove_event) != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback interrupted"); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback closing"); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ } ++ ++ completion = ++ &instance->completions[instance->completion_insert & ++ (MAX_COMPLETIONS - 1)]; ++ ++ completion->header = header; ++ completion->reason = reason; ++ /* N.B. service_userdata is updated while processing AWAIT_COMPLETION */ ++ completion->service_userdata = user_service->service; ++ completion->bulk_userdata = bulk_userdata; ++ ++ if (reason == VCHIQ_SERVICE_CLOSED) { ++ /* Take an extra reference, to be held until ++ this CLOSED notification is delivered. */ ++ lock_service(user_service->service); ++ if (instance->use_close_delivered) ++ user_service->close_pending = 1; ++ } ++ ++ /* A write barrier is needed here to ensure that the entire completion ++ record is written out before the insert point. */ ++ wmb(); ++ ++ if (reason == VCHIQ_MESSAGE_AVAILABLE) ++ user_service->message_available_pos = ++ instance->completion_insert; ++ instance->completion_insert++; ++ ++ up(&instance->insert_event); ++ ++ return VCHIQ_SUCCESS; +} + -+static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) ++/**************************************************************************** ++* ++* service_callback ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) +{ -+ int result; -+ __u32 value; ++ /* How do we ensure the callback goes to the right client? ++ ** The service_user data points to a USER_SERVICE_T record containing ++ ** the original callback and the user state structure, which contains a ++ ** circular buffer for completion records. ++ */ ++ USER_SERVICE_T *user_service; ++ VCHIQ_SERVICE_T *service; ++ VCHIQ_INSTANCE_T instance; ++ DEBUG_INITIALISE(g_state.local) ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ ++ service = handle_to_service(handle); ++ BUG_ON(!service); ++ user_service = (USER_SERVICE_T *)service->base.userdata; ++ instance = user_service->instance; ++ ++ if (!instance || instance->closing) ++ return VCHIQ_SUCCESS; ++ ++ vchiq_log_trace(vchiq_arm_log_level, ++ "service_callback - service %lx(%d,%p), reason %d, header %lx, " ++ "instance %lx, bulk_userdata %lx", ++ (unsigned long)user_service, ++ service->localport, user_service->userdata, ++ reason, (unsigned long)header, ++ (unsigned long)instance, (unsigned long)bulk_userdata); ++ ++ if (header && user_service->is_vchi) { ++ spin_lock(&msg_queue_spinlock); ++ while (user_service->msg_insert == ++ (user_service->msg_remove + MSG_QUEUE_SIZE)) { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); ++ vchiq_log_trace(vchiq_arm_log_level, ++ "service_callback - msg queue full"); ++ /* If there is no MESSAGE_AVAILABLE in the completion ++ ** queue, add one ++ */ ++ if ((user_service->message_available_pos - ++ instance->completion_remove) < 0) { ++ VCHIQ_STATUS_T status; ++ vchiq_log_info(vchiq_arm_log_level, ++ "Inserting extra MESSAGE_AVAILABLE"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ status = add_completion(instance, reason, ++ NULL, user_service, bulk_userdata); ++ if (status != VCHIQ_SUCCESS) { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return status; ++ } ++ } ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ if (down_interruptible(&user_service->remove_event) ++ != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback interrupted"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "service_callback closing"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ spin_lock(&msg_queue_spinlock); ++ } ++ ++ user_service->msg_queue[user_service->msg_insert & ++ (MSG_QUEUE_SIZE - 1)] = header; ++ user_service->msg_insert++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ up(&user_service->insert_event); ++ ++ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if ++ ** there is a MESSAGE_AVAILABLE in the completion queue then ++ ** bypass the completion queue. ++ */ ++ if (((user_service->message_available_pos - ++ instance->completion_remove) >= 0) || ++ user_service->dequeue_pending) { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ user_service->dequeue_pending = 0; ++ return VCHIQ_SUCCESS; ++ } ++ ++ header = NULL; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ ++ return add_completion(instance, reason, header, user_service, ++ bulk_userdata); ++} ++ ++/**************************************************************************** ++* ++* user_service_free ++* ++***************************************************************************/ ++static void ++user_service_free(void *userdata) ++{ ++ kfree(userdata); ++} ++ ++/**************************************************************************** ++* ++* close_delivered ++* ++***************************************************************************/ ++static void close_delivered(USER_SERVICE_T *user_service) ++{ ++ vchiq_log_info(vchiq_arm_log_level, ++ "close_delivered(handle=%x)", ++ user_service->service->handle); ++ ++ if (user_service->close_pending) { ++ /* Allow the underlying service to be culled */ ++ unlock_service(user_service->service); ++ ++ /* Wake the user-thread blocked in close_ or remove_service */ ++ up(&user_service->close_event); ++ ++ user_service->close_pending = 0; ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_ioctl ++* ++***************************************************************************/ ++static long ++vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ VCHIQ_SERVICE_T *service = NULL; ++ long ret = 0; ++ int i, rc; ++ DEBUG_INITIALISE(g_state.local) ++ ++ vchiq_log_trace(vchiq_arm_log_level, ++ "vchiq_ioctl - instance %x, cmd %s, arg %lx", ++ (unsigned int)instance, ++ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? ++ ioctl_names[_IOC_NR(cmd)] : "", arg); + + switch (cmd) { -+ case LIRC_GET_SEND_MODE: -+ return -ENOIOCTLCMD; ++ case VCHIQ_IOC_SHUTDOWN: ++ if (!instance->connected) ++ break; ++ ++ /* Remove all services */ ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ status = vchiq_remove_service(service->handle); ++ unlock_service(service); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ service = NULL; ++ ++ if (status == VCHIQ_SUCCESS) { ++ /* Wake the completion thread and ask it to exit */ ++ instance->closing = 1; ++ up(&instance->insert_event); ++ } ++ + break; + -+ case LIRC_SET_SEND_MODE: -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ /* only LIRC_MODE_PULSE supported */ -+ if (value != LIRC_MODE_PULSE) -+ return -ENOSYS; ++ case VCHIQ_IOC_CONNECT: ++ if (instance->connected) { ++ ret = -EINVAL; ++ break; ++ } ++ rc = mutex_lock_interruptible(&instance->state->mutex); ++ if (rc != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq: connect: could not lock mutex for " ++ "state %d: %d", ++ instance->state->id, rc); ++ ret = -EINTR; ++ break; ++ } ++ status = vchiq_connect_internal(instance->state, instance); ++ mutex_unlock(&instance->state->mutex); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ else ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq: could not connect: %d", status); + break; + -+ case LIRC_GET_LENGTH: -+ return -ENOSYS; -+ break; -+ -+ case LIRC_SET_SEND_DUTY_CYCLE: -+ dprintk("SET_SEND_DUTY_CYCLE\n"); -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ if (value <= 0 || value > 100) -+ return -EINVAL; -+ return init_timing_params(value, freq); -+ break; -+ -+ case LIRC_SET_SEND_CARRIER: -+ dprintk("SET_SEND_CARRIER\n"); -+ result = get_user(value, (__u32 *) arg); -+ if (result) -+ return result; -+ if (value > 500000 || value < 20000) -+ return -EINVAL; -+ return init_timing_params(duty_cycle, value); -+ break; -+ -+ default: -+ return lirc_dev_fop_ioctl(filep, cmd, arg); -+ } -+ return 0; -+} -+ -+static const struct file_operations lirc_fops = { -+ .owner = THIS_MODULE, -+ .write = lirc_write, -+ .unlocked_ioctl = lirc_ioctl, -+ .read = lirc_dev_fop_read, -+ .poll = lirc_dev_fop_poll, -+ .open = lirc_dev_fop_open, -+ .release = lirc_dev_fop_close, -+ .llseek = no_llseek, -+}; -+ -+static struct lirc_driver driver = { -+ .name = LIRC_DRIVER_NAME, -+ .minor = -1, -+ .code_length = 1, -+ .sample_rate = 0, -+ .data = NULL, -+ .add_to_buf = NULL, -+ .rbuf = &rbuf, -+ .set_use_inc = set_use_inc, -+ .set_use_dec = set_use_dec, -+ .fops = &lirc_fops, -+ .dev = NULL, -+ .owner = THIS_MODULE, -+}; -+ -+static struct platform_driver lirc_rpi_driver = { -+ .driver = { -+ .name = LIRC_DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init lirc_rpi_init(void) -+{ -+ int result; -+ -+ /* Init read buffer. */ -+ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); -+ if (result < 0) -+ return -ENOMEM; -+ -+ result = platform_driver_register(&lirc_rpi_driver); -+ if (result) { -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": lirc register returned %d\n", result); -+ goto exit_buffer_free; -+ } -+ -+ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); -+ if (!lirc_rpi_dev) { -+ result = -ENOMEM; -+ goto exit_driver_unregister; -+ } -+ -+ result = platform_device_add(lirc_rpi_dev); -+ if (result) -+ goto exit_device_put; -+ -+ return 0; -+ -+ exit_device_put: -+ platform_device_put(lirc_rpi_dev); -+ -+ exit_driver_unregister: -+ platform_driver_unregister(&lirc_rpi_driver); -+ -+ exit_buffer_free: -+ lirc_buffer_free(&rbuf); -+ -+ return result; -+} -+ -+static void lirc_rpi_exit(void) -+{ -+ if (!lirc_rpi_dev->dev.of_node) -+ platform_device_unregister(lirc_rpi_dev); -+ platform_driver_unregister(&lirc_rpi_driver); -+ lirc_buffer_free(&rbuf); -+} -+ -+static int __init lirc_rpi_init_module(void) -+{ -+ int result; -+ -+ result = lirc_rpi_init(); -+ if (result) -+ return result; -+ -+ if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) { -+ result = -EINVAL; -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": invalid GPIO pin(s) specified!\n"); -+ goto exit_rpi; -+ } -+ -+ result = init_port(); -+ if (result < 0) -+ goto exit_rpi; -+ -+ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | -+ LIRC_CAN_SET_SEND_CARRIER | -+ LIRC_CAN_SEND_PULSE | -+ LIRC_CAN_REC_MODE2; -+ -+ driver.dev = &lirc_rpi_dev->dev; -+ driver.minor = lirc_register_driver(&driver); -+ -+ if (driver.minor < 0) { -+ printk(KERN_ERR LIRC_DRIVER_NAME -+ ": device registration failed with %d\n", result); -+ result = -EIO; -+ goto exit_rpi; -+ } -+ -+ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); -+ -+ return 0; -+ -+ exit_rpi: -+ lirc_rpi_exit(); -+ -+ return result; -+} -+ -+static void __exit lirc_rpi_exit_module(void) -+{ -+ lirc_unregister_driver(driver.minor); -+ -+ gpio_free(gpio_out_pin); -+ gpio_free(gpio_in_pin); -+ -+ lirc_rpi_exit(); -+ -+ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); -+} -+ -+module_init(lirc_rpi_init_module); -+module_exit(lirc_rpi_exit_module); -+ -+MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); -+MODULE_AUTHOR("Aron Robert Szabo "); -+MODULE_AUTHOR("Michael Bishop "); -+MODULE_LICENSE("GPL"); -+ -+module_param(gpio_out_pin, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" -+ " processor. (default 17"); -+ -+module_param(gpio_in_pin, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." -+ " (default 18"); -+ -+module_param(gpio_in_pull, int, S_IRUGO); -+MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." -+ " (0 = off, 1 = up, 2 = down, default down)"); -+ -+module_param(sense, int, S_IRUGO); -+MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" -+ " (0 = active high, 1 = active low )"); -+ -+module_param(softcarrier, bool, S_IRUGO); -+MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); -+ -+module_param(invert, bool, S_IRUGO); -+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); -+ -+module_param(debug, bool, S_IRUGO | S_IWUSR); -+MODULE_PARM_DESC(debug, "Enable debugging messages"); - -From c66c4562de98ab99b0bd25fcd238104ce8f4ebbe Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:49:20 +0100 -Subject: [PATCH 017/172] Add cpufreq driver - ---- - arch/arm/Kconfig | 1 + - drivers/cpufreq/Kconfig.arm | 8 ++ - drivers/cpufreq/Makefile | 1 + - drivers/cpufreq/bcm2835-cpufreq.c | 224 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 234 insertions(+) - create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 1dc698c..fdfb230 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -376,6 +376,7 @@ config ARCH_BCM2708 - select NEED_MACH_GPIO_H - select NEED_MACH_MEMORY_H - select CLKDEV_LOOKUP -+ select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select ARM_ERRATA_411920 - select MACH_BCM2708 -diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm -index 0f9a2c3..c71b6e1 100644 ---- a/drivers/cpufreq/Kconfig.arm -+++ b/drivers/cpufreq/Kconfig.arm -@@ -241,6 +241,14 @@ config ARM_SPEAR_CPUFREQ - help - This adds the CPUFreq driver support for SPEAr SOCs. - -+config ARM_BCM2835_CPUFREQ -+ bool "BCM2835 Driver" -+ default y -+ help -+ This adds the CPUFreq driver for BCM2835 -+ -+ If in doubt, say N. -+ - config ARM_TEGRA_CPUFREQ - bool "TEGRA CPUFreq support" - depends on ARCH_TEGRA -diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile -index b3ca7b0..2824b68 100644 ---- a/drivers/cpufreq/Makefile -+++ b/drivers/cpufreq/Makefile -@@ -74,6 +74,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o - obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o - obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o - obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o -+obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o - obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o - obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o - -diff --git a/drivers/cpufreq/bcm2835-cpufreq.c b/drivers/cpufreq/bcm2835-cpufreq.c -new file mode 100755 -index 0000000..447ca09 ---- /dev/null -+++ b/drivers/cpufreq/bcm2835-cpufreq.c -@@ -0,0 +1,224 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+/***************************************************************************** -+* FILENAME: bcm2835-cpufreq.h -+* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM -+* processor. Messages are sent to Videocore either setting or requesting the -+* frequency of the ARM in order to match an appropiate frequency to the current -+* usage of the processor. The policy which selects the frequency to use is -+* defined in the kernel .config file, but can be changed during runtime. -+*****************************************************************************/ -+ -+/* ---------- INCLUDES ---------- */ -+#include -+#include -+#include -+#include -+#include -+ -+/* ---------- DEFINES ---------- */ -+/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */ -+#define MODULE_NAME "bcm2835-cpufreq" -+ -+#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */ -+ -+/* debug printk macros */ -+#ifdef CPUFREQ_DEBUG_ENABLE -+#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__) -+ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the message id */ -+ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */ -+ uint32_t data_size; /* amount of data being sent or received */ -+ uint32_t dev_id; /* the ID of the clock/voltage to get or set */ -+ uint32_t val; /* the value (e.g. rate (in Hz)) to set */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+/* ---------- GLOBALS ---------- */ -+static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */ -+ -+static struct cpufreq_frequency_table bcm2835_freq_table[] = { -+ {0, 0, 0}, -+ {0, 0, 0}, -+ {0, 0, CPUFREQ_TABLE_END}, -+}; -+ -+/* -+ =============================================== -+ clk_rate either gets or sets the clock rates. -+ =============================================== -+*/ -+static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) -+{ -+ int s, actual_rate=0; -+ struct vc_msg msg; -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ msg.msg_size = sizeof msg; -+ -+ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE; -+ msg.tag.buffer_size = 8; -+ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */ -+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; -+ msg.tag.val = arm_rate * 1000; -+ -+ /* send the message */ -+ s = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in KHz */ -+ if (s == 0 && (msg.request_code & 0x80000000)) -+ actual_rate = msg.tag.val/1000; -+ -+ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate); -+ return actual_rate; -+} -+ -+static uint32_t bcm2835_cpufreq_get_clock(int tag) -+{ -+ int s; -+ int arm_rate = 0; -+ struct vc_msg msg; -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ msg.msg_size = sizeof msg; -+ msg.tag.tag_id = tag; -+ msg.tag.buffer_size = 8; -+ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */ -+ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; -+ -+ /* send the message */ -+ s = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in KHz */ -+ if (s == 0 && (msg.request_code & 0x80000000)) -+ arm_rate = msg.tag.val/1000; -+ -+ print_debug("%s frequency = %d\n", -+ tag == VCMSG_GET_CLOCK_RATE ? "Current": -+ tag == VCMSG_GET_MIN_CLOCK ? "Min": -+ tag == VCMSG_GET_MAX_CLOCK ? "Max": -+ "Unexpected", arm_rate); -+ -+ return arm_rate; -+} -+ -+/* -+ ==================================================== -+ Module Initialisation registers the cpufreq driver -+ ==================================================== -+*/ -+static int __init bcm2835_cpufreq_module_init(void) -+{ -+ print_debug("IN\n"); -+ return cpufreq_register_driver(&bcm2835_cpufreq_driver); -+} -+ -+/* -+ ============= -+ Module exit -+ ============= -+*/ -+static void __exit bcm2835_cpufreq_module_exit(void) -+{ -+ print_debug("IN\n"); -+ cpufreq_unregister_driver(&bcm2835_cpufreq_driver); -+ return; -+} -+ -+/* -+ ============================================================== -+ Initialisation function sets up the CPU policy for first use -+ ============================================================== -+*/ -+static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy) -+{ -+ /* measured value of how long it takes to change frequency */ -+ const unsigned int transition_latency = 355000; /* ns */ -+ -+ /* now find out what the maximum and minimum frequencies are */ -+ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK); -+ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK); -+ -+ print_info("min=%d max=%d\n", bcm2835_freq_table[0].frequency, bcm2835_freq_table[1].frequency); -+ return cpufreq_generic_init(policy, bcm2835_freq_table, transition_latency); -+} -+ -+/* -+ ===================================================================== -+ Target index function chooses the requested frequency from the table -+ ===================================================================== -+*/ -+ -+static int bcm2835_cpufreq_driver_target_index(struct cpufreq_policy *policy, unsigned int state) -+{ -+ unsigned int target_freq = bcm2835_freq_table[state].frequency; -+ unsigned int cur = bcm2835_cpufreq_set_clock(policy->cur, target_freq); -+ -+ if (!cur) -+ { -+ print_err("Error occurred setting a new frequency (%d)\n", target_freq); -+ return -EINVAL; -+ } -+ print_debug("%s: %i: freq %d->%d\n", policy->governor->name, state, policy->cur, cur); -+ return 0; -+} -+ -+/* -+ ====================================================== -+ Get function returns the current frequency from table -+ ====================================================== -+*/ -+ -+static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu) -+{ -+ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE); -+ print_debug("%d: freq=%d\n", cpu, actual_rate); -+ return actual_rate <= bcm2835_freq_table[0].frequency ? bcm2835_freq_table[0].frequency : bcm2835_freq_table[1].frequency; -+} -+ -+/* the CPUFreq driver */ -+static struct cpufreq_driver bcm2835_cpufreq_driver = { -+ .name = "BCM2835 CPUFreq", -+ .init = bcm2835_cpufreq_driver_init, -+ .verify = cpufreq_generic_frequency_table_verify, -+ .target_index = bcm2835_cpufreq_driver_target_index, -+ .get = bcm2835_cpufreq_driver_get, -+ .attr = cpufreq_generic_attr, -+}; -+ -+MODULE_AUTHOR("Dorian Peake and Dom Cobley"); -+MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip"); -+MODULE_LICENSE("GPL"); -+ -+module_init(bcm2835_cpufreq_module_init); -+module_exit(bcm2835_cpufreq_module_exit); - -From b5de470ee4adcb247acbb7a8515a73db3a9494d7 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 19:24:24 +0000 -Subject: [PATCH 018/172] Added hwmon/thermal driver for reporting core - temperature. Thanks Dorian - ---- - arch/arm/mach-bcm2708/bcm2708.c | 11 ++ - drivers/hwmon/Kconfig | 10 ++ - drivers/hwmon/Makefile | 1 + - drivers/hwmon/bcm2835-hwmon.c | 219 ++++++++++++++++++++++++++++++++++++++ - drivers/thermal/Kconfig | 6 ++ - drivers/thermal/Makefile | 1 + - drivers/thermal/bcm2835-thermal.c | 184 ++++++++++++++++++++++++++++++++ - 7 files changed, 432 insertions(+) - create mode 100644 drivers/hwmon/bcm2835-hwmon.c - create mode 100644 drivers/thermal/bcm2835-thermal.c - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index f3dccae..af57d11 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -455,6 +455,14 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+static struct platform_device bcm2835_hwmon_device = { -+ .name = "bcm2835_hwmon", -+}; -+ -+static struct platform_device bcm2835_thermal_device = { -+ .name = "bcm2835_thermal", -+}; -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -563,6 +571,9 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index a7de26d..42c82b4 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -1702,6 +1702,16 @@ config SENSORS_ULTRA45 - This driver provides support for the Ultra45 workstation environmental - sensors. - -+config SENSORS_BCM2835 -+ depends on THERMAL_BCM2835=n -+ tristate "Broadcom BCM2835 HWMON Driver" -+ help -+ If you say yes here you get support for the hardware -+ monitoring features of the BCM2835 Chip -+ -+ This driver can also be built as a module. If so, the module -+ will be called bcm2835-hwmon. -+ - if ACPI - - comment "ACPI drivers" -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 6c94147..e7c60cb 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o - obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o - obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o - obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o -+obj-$(CONFIG_SENSORS_BCM2835) += bcm2835-hwmon.o - - obj-$(CONFIG_PMBUS) += pmbus/ - -diff --git a/drivers/hwmon/bcm2835-hwmon.c b/drivers/hwmon/bcm2835-hwmon.c -new file mode 100644 -index 0000000..5bbed45 ---- /dev/null -+++ b/drivers/hwmon/bcm2835-hwmon.c -@@ -0,0 +1,219 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define MODULE_NAME "bcm2835_hwmon" -+ -+/*#define HWMON_DEBUG_ENABLE*/ -+ -+#ifdef HWMON_DEBUG_ENABLE -+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__) -+ -+#define VC_TAG_GET_TEMP 0x00030006 -+#define VC_TAG_GET_MAX_TEMP 0x0003000A -+ -+/* --- STRUCTS --- */ -+struct bcm2835_hwmon_data { -+ struct device *hwmon_dev; -+}; -+ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the tag ID for the temperature */ -+ uint32_t buffer_size; /* size of the buffer (should be 8) */ -+ uint32_t request_code; /* identifies message as a request (should be 0) */ -+ uint32_t id; /* extra ID field (should be 0) */ -+ uint32_t val; /* returned value of the temperature */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+typedef enum { -+ TEMP, -+ MAX_TEMP, -+} temp_type; -+ -+/* --- PROTOTYPES --- */ -+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf); -+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf); -+ -+/* --- GLOBALS --- */ -+ -+static struct bcm2835_hwmon_data *bcm2835_data; -+static struct platform_driver bcm2835_hwmon_driver; -+ -+static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0); -+static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP); -+static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP); -+ -+static struct attribute* bcm2835_attributes[] = { -+ &sensor_dev_attr_name.dev_attr.attr, -+ &sensor_dev_attr_temp1_input.dev_attr.attr, -+ &sensor_dev_attr_temp1_max.dev_attr.attr, -+ NULL, -+}; -+ -+static struct attribute_group bcm2835_attr_group = { -+ .attrs = bcm2835_attributes, -+}; -+ -+/* --- FUNCTIONS --- */ -+ -+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ return sprintf(buf,"bcm2835_hwmon\n"); -+} -+ -+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct vc_msg msg; -+ int result; -+ uint temp = 0; -+ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index; -+ -+ print_debug("IN"); -+ -+ /* wipe all previous message data */ -+ memset(&msg, 0, sizeof msg); -+ -+ /* determine the message type */ -+ if(index == TEMP) -+ msg.tag.tag_id = VC_TAG_GET_TEMP; -+ else if (index == MAX_TEMP) -+ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP; -+ else -+ { -+ print_debug("Unknown temperature message!"); -+ return -EINVAL; -+ } -+ -+ msg.msg_size = sizeof msg; -+ msg.tag.buffer_size = 8; -+ -+ /* send the message */ -+ result = bcm_mailbox_property(&msg, sizeof msg); -+ -+ /* check if it was all ok and return the rate in milli degrees C */ -+ if (result == 0 && (msg.request_code & 0x80000000)) -+ temp = (uint)msg.tag.val; -+ #ifdef HWMON_DEBUG_ENABLE -+ else -+ print_debug("Failed to get temperature!"); -+ #endif -+ print_debug("Got temperature as %u",temp); -+ print_debug("OUT"); -+ return sprintf(buf, "%u\n", temp); -+} -+ -+ -+static int bcm2835_hwmon_probe(struct platform_device *pdev) -+{ -+ int err; -+ -+ print_debug("IN"); -+ print_debug("HWMON Driver has been probed!"); -+ -+ /* check that the device isn't null!*/ -+ if(pdev == NULL) -+ { -+ print_debug("Platform device is empty!"); -+ return -ENODEV; -+ } -+ -+ /* allocate memory for neccessary data */ -+ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL); -+ if(!bcm2835_data) -+ { -+ print_debug("Unable to allocate memory for hwmon data!"); -+ err = -ENOMEM; -+ goto kzalloc_error; -+ } -+ -+ /* create the sysfs files */ -+ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group)) -+ { -+ print_debug("Unable to create sysfs files!"); -+ err = -EFAULT; -+ goto sysfs_error; -+ } -+ -+ /* register the hwmon device */ -+ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev); -+ if (IS_ERR(bcm2835_data->hwmon_dev)) -+ { -+ err = PTR_ERR(bcm2835_data->hwmon_dev); -+ goto hwmon_error; -+ } -+ print_debug("OUT"); -+ return 0; -+ -+ /* error goto's */ -+ hwmon_error: -+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); -+ -+ sysfs_error: -+ kfree(bcm2835_data); -+ -+ kzalloc_error: -+ -+ return err; -+ -+} -+ -+static int bcm2835_hwmon_remove(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ hwmon_device_unregister(bcm2835_data->hwmon_dev); -+ -+ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); -+ print_debug("OUT"); -+ return 0; -+} -+ -+/* Hwmon Driver */ -+static struct platform_driver bcm2835_hwmon_driver = { -+ .probe = bcm2835_hwmon_probe, -+ .remove = bcm2835_hwmon_remove, -+ .driver = { -+ .name = "bcm2835_hwmon", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Dorian Peake"); -+MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip"); -+ -+module_platform_driver(bcm2835_hwmon_driver); -diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig -index af40db0..4584b1e 100644 ---- a/drivers/thermal/Kconfig -+++ b/drivers/thermal/Kconfig -@@ -238,6 +238,12 @@ config INTEL_POWERCLAMP - enforce idle time which results in more package C-state residency. The - user interface is exposed via generic thermal framework. - -+config THERMAL_BCM2835 -+ tristate "BCM2835 Thermal Driver" -+ help -+ This will enable temperature monitoring for the Broadcom BCM2835 -+ chip. If built as a module, it will be called 'bcm2835-thermal'. -+ - config X86_PKG_TEMP_THERMAL - tristate "X86 package temperature thermal driver" - depends on X86_THERMAL_VECTOR -diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile -index fa0dc48..453d4d9 100644 ---- a/drivers/thermal/Makefile -+++ b/drivers/thermal/Makefile -@@ -33,6 +33,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o - obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o - obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o - obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o -+obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o - obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o - obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o - obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ -diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c -new file mode 100644 -index 0000000..85fceb5 ---- /dev/null -+++ b/drivers/thermal/bcm2835-thermal.c -@@ -0,0 +1,184 @@ -+/***************************************************************************** -+* Copyright 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* --- DEFINITIONS --- */ -+#define MODULE_NAME "bcm2835_thermal" -+ -+/*#define THERMAL_DEBUG_ENABLE*/ -+ -+#ifdef THERMAL_DEBUG_ENABLE -+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) -+#else -+#define print_debug(fmt,...) -+#endif -+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) -+ -+#define VC_TAG_GET_TEMP 0x00030006 -+#define VC_TAG_GET_MAX_TEMP 0x0003000A -+ -+typedef enum { -+ TEMP, -+ MAX_TEMP, -+} temp_type; -+ -+/* --- STRUCTS --- */ -+/* tag part of the message */ -+struct vc_msg_tag { -+ uint32_t tag_id; /* the tag ID for the temperature */ -+ uint32_t buffer_size; /* size of the buffer (should be 8) */ -+ uint32_t request_code; /* identifies message as a request (should be 0) */ -+ uint32_t id; /* extra ID field (should be 0) */ -+ uint32_t val; /* returned value of the temperature */ -+}; -+ -+/* message structure to be sent to videocore */ -+struct vc_msg { -+ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ -+ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ -+ struct vc_msg_tag tag; /* the tag structure above to make */ -+ uint32_t end_tag; /* an end identifier, should be set to NULL */ -+}; -+ -+struct bcm2835_thermal_data { -+ struct thermal_zone_device *thermal_dev; -+ struct vc_msg msg; -+}; -+ -+/* --- GLOBALS --- */ -+static struct bcm2835_thermal_data bcm2835_data; -+ -+/* Thermal Device Operations */ -+static struct thermal_zone_device_ops ops; -+ -+/* --- FUNCTIONS --- */ -+ -+static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id) -+{ -+ int result = -1, retry = 3; -+ print_debug("IN"); -+ -+ *temp = 0; -+ while (result != 0 && retry-- > 0) { -+ /* wipe all previous message data */ -+ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg); -+ -+ /* prepare message */ -+ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg; -+ bcm2835_data.msg.tag.buffer_size = 8; -+ bcm2835_data.msg.tag.tag_id = tag_id; -+ -+ /* send the message */ -+ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg); -+ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code); -+ if (!(bcm2835_data.msg.request_code & 0x80000000)) -+ result = -1; -+ } -+ -+ /* check if it was all ok and return the rate in milli degrees C */ -+ if (result == 0) -+ *temp = (uint)bcm2835_data.msg.tag.val; -+ else -+ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result); -+ print_debug("OUT"); -+ return result; -+} -+ -+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp) -+{ -+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP); -+} -+ -+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp) -+{ -+ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP); -+} -+ -+static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type) -+{ -+ *trip_type = THERMAL_TRIP_HOT; -+ return 0; -+} -+ -+ -+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode) -+{ -+ *dev_mode = THERMAL_DEVICE_ENABLED; -+ return 0; -+} -+ -+ -+static int bcm2835_thermal_probe(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ print_debug("THERMAL Driver has been probed!"); -+ -+ /* check that the device isn't null!*/ -+ if(pdev == NULL) -+ { -+ print_debug("Platform device is empty!"); -+ return -ENODEV; -+ } -+ -+ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0))) -+ { -+ print_debug("Unable to register the thermal device!"); -+ return -EFAULT; -+ } -+ return 0; -+} -+ -+ -+static int bcm2835_thermal_remove(struct platform_device *pdev) -+{ -+ print_debug("IN"); -+ -+ thermal_zone_device_unregister(bcm2835_data.thermal_dev); -+ -+ print_debug("OUT"); -+ -+ return 0; -+} -+ -+static struct thermal_zone_device_ops ops = { -+ .get_temp = bcm2835_get_temp, -+ .get_trip_temp = bcm2835_get_max_temp, -+ .get_trip_type = bcm2835_get_trip_type, -+ .get_mode = bcm2835_get_mode, -+}; -+ -+/* Thermal Driver */ -+static struct platform_driver bcm2835_thermal_driver = { -+ .probe = bcm2835_thermal_probe, -+ .remove = bcm2835_thermal_remove, -+ .driver = { -+ .name = "bcm2835_thermal", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Dorian Peake"); -+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); -+ -+module_platform_driver(bcm2835_thermal_driver); - -From 5af87652d480b6de0edca41bde86a469bf15ffc6 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 26 Mar 2013 17:26:38 +0000 -Subject: [PATCH 019/172] Allow mac address to be set in smsc95xx - -Signed-off-by: popcornmix ---- - drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 56 insertions(+) - -diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c -index 26423ad..e29a323 100644 ---- a/drivers/net/usb/smsc95xx.c -+++ b/drivers/net/usb/smsc95xx.c -@@ -59,6 +59,7 @@ - #define SUSPEND_SUSPEND3 (0x08) - #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ - SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) -+#define MAC_ADDR_LEN (6) - - struct smsc95xx_priv { - u32 mac_cr; -@@ -74,6 +75,10 @@ static bool turbo_mode = true; - module_param(turbo_mode, bool, 0644); - MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); - -+static char *macaddr = ":"; -+module_param(macaddr, charp, 0); -+MODULE_PARM_DESC(macaddr, "MAC address"); -+ - static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, - u32 *data, int in_pm) - { -@@ -763,8 +768,59 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) - return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); - } - -+/* Check the macaddr module parameter for a MAC address */ -+static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac) -+{ -+ int i, j, got_num, num; -+ u8 mtbl[MAC_ADDR_LEN]; -+ -+ if (macaddr[0] == ':') -+ return 0; -+ -+ i = 0; -+ j = 0; -+ num = 0; -+ got_num = 0; -+ while (j < MAC_ADDR_LEN) { -+ if (macaddr[i] && macaddr[i] != ':') { -+ got_num++; -+ if ('0' <= macaddr[i] && macaddr[i] <= '9') -+ num = num * 16 + macaddr[i] - '0'; -+ else if ('A' <= macaddr[i] && macaddr[i] <= 'F') -+ num = num * 16 + 10 + macaddr[i] - 'A'; -+ else if ('a' <= macaddr[i] && macaddr[i] <= 'f') -+ num = num * 16 + 10 + macaddr[i] - 'a'; -+ else -+ break; -+ i++; -+ } else if (got_num == 2) { -+ mtbl[j++] = (u8) num; -+ num = 0; -+ got_num = 0; -+ i++; -+ } else { -+ break; -+ } -+ } -+ -+ if (j == MAC_ADDR_LEN) { -+ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: " -+ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2], -+ mtbl[3], mtbl[4], mtbl[5]); -+ for (i = 0; i < MAC_ADDR_LEN; i++) -+ dev_mac[i] = mtbl[i]; -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ - static void smsc95xx_init_mac_address(struct usbnet *dev) - { -+ /* Check module parameters */ -+ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) -+ return; -+ - /* try reading mac address from EEPROM */ - if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, - dev->net->dev_addr) == 0) { - -From b8f0a117f160b4a8a9c41d3e61754dbe220a69dd Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 4 Nov 2013 18:56:10 +0000 -Subject: [PATCH 020/172] Add Chris Boot's i2c and spi drivers. - -i2c-bcm2708: fixed baudrate - -Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock). -In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits. -This resulted in incorrect setting of CDIV and higher baudrate than intended. -Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz -After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz -The correct baudrate is shown in the log after the cdiv > 0xffff correction. ---- - arch/arm/configs/bcmrpi_defconfig | 7 + - arch/arm/mach-bcm2708/Kconfig | 7 + - arch/arm/mach-bcm2708/bcm2708.c | 104 ++++++- - drivers/i2c/busses/Kconfig | 19 ++ - drivers/i2c/busses/Makefile | 1 + - drivers/i2c/busses/i2c-bcm2708.c | 420 +++++++++++++++++++++++++ - drivers/spi/Kconfig | 8 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-bcm2708.c | 626 ++++++++++++++++++++++++++++++++++++++ - 9 files changed, 1191 insertions(+), 2 deletions(-) - create mode 100644 drivers/i2c/busses/i2c-bcm2708.c - create mode 100644 drivers/spi/spi-bcm2708.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 63cb6a4..6d2eae1 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -195,6 +195,13 @@ CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y - # CONFIG_HW_RANDOM is not set - CONFIG_RAW_DRIVER=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2708=m -+CONFIG_SPI_SPIDEV=m -+CONFIG_GPIO_SYSFS=y - # CONFIG_HWMON is not set - CONFIG_WATCHDOG=y - CONFIG_FB=y -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index 9355841..e151ed4 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -31,4 +31,11 @@ config BCM2708_NOL2CACHE - help - Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. - -+config BCM2708_SPIDEV -+ bool "Bind spidev to SPI0 master" -+ depends on MACH_BCM2708 -+ depends on SPI -+ default y -+ help -+ Binds spidev driver to the SPI0 master - endmenu -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index af57d11..82f56fb 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -205,7 +206,6 @@ static struct clk osc_clk = { - - /* warning - the USB needs a clock > 34MHz */ - --#ifdef CONFIG_MMC_BCM2708 - static struct clk sdhost_clk = { - #ifdef CONFIG_ARCH_BCM2708_CHIPIT - .rate = 4000000, /* 4MHz */ -@@ -213,7 +213,6 @@ static struct clk sdhost_clk = { - .rate = 250000000, /* 250MHz */ - #endif - }; --#endif - - static struct clk_lookup lookups[] = { - { /* UART0 */ -@@ -223,6 +222,15 @@ static struct clk_lookup lookups[] = { - { /* USB */ - .dev_id = "bcm2708_usb", - .clk = &osc_clk, -+ }, { /* SPI */ -+ .dev_id = "bcm2708_spi.0", -+ .clk = &sdhost_clk, -+ }, { /* BSC0 */ -+ .dev_id = "bcm2708_i2c.0", -+ .clk = &sdhost_clk, -+ }, { /* BSC1 */ -+ .dev_id = "bcm2708_i2c.1", -+ .clk = &sdhost_clk, - } - }; - -@@ -455,6 +463,89 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+static struct resource bcm2708_spi_resources[] = { -+ { -+ .start = SPI0_BASE, -+ .end = SPI0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = IRQ_SPI, -+ .end = IRQ_SPI, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+ -+static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+static struct platform_device bcm2708_spi_device = { -+ .name = "bcm2708_spi", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), -+ .resource = bcm2708_spi_resources, -+ .dev = { -+ .dma_mask = &bcm2708_spi_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, -+}; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+static struct spi_board_info bcm2708_spi_devices[] = { -+#ifdef CONFIG_SPI_SPIDEV -+ { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 0, -+ .mode = SPI_MODE_0, -+ }, { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 1, -+ .mode = SPI_MODE_0, -+ } -+#endif -+}; -+#endif -+ -+static struct resource bcm2708_bsc0_resources[] = { -+ { -+ .start = BSC0_BASE, -+ .end = BSC0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc0_device = { -+ .name = "bcm2708_i2c", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), -+ .resource = bcm2708_bsc0_resources, -+}; -+ -+ -+static struct resource bcm2708_bsc1_resources[] = { -+ { -+ .start = BSC1_BASE, -+ .end = BSC1_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc1_device = { -+ .name = "bcm2708_i2c", -+ .id = 1, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), -+ .resource = bcm2708_bsc1_resources, -+}; -+ - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", - }; -@@ -571,6 +662,10 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2708_spi_device); -+ bcm_register_device(&bcm2708_bsc0_device); -+ bcm_register_device(&bcm2708_bsc1_device); -+ - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); - -@@ -580,6 +675,11 @@ void __init bcm2708_init(void) - } - system_rev = boardrev; - system_serial_low = serial; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); -+#endif - } - - static void timer_set_mode(enum clock_event_mode mode, -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index ab838d9..68362ab 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -372,6 +372,25 @@ config I2C_BCM2835 - This support is also available as a module. If so, the module - will be called i2c-bcm2835. - -+config I2C_BCM2708 -+ tristate "BCM2708 BSC" -+ depends on MACH_BCM2708 -+ help -+ Enabling this option will add BSC (Broadcom Serial Controller) -+ support for the BCM2708. BSC is a Broadcom proprietary bus compatible -+ with I2C/TWI/SMBus. -+ -+config I2C_BCM2708_BAUDRATE -+ prompt "BCM2708 I2C baudrate" -+ depends on I2C_BCM2708 -+ int -+ default 100000 -+ help -+ Set the I2C baudrate. This will alter the default value. A -+ different baudrate can be set by using a module parameter as well. If -+ no parameter is provided when loading, this is the value that will be -+ used. -+ - config I2C_BCM_KONA - tristate "BCM Kona I2C adapter" - depends on ARCH_BCM_MOBILE -diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile -index 56388f6..5403a9b 100644 ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o - obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o - obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o - obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o -+obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o - obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o - obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o - obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -new file mode 100644 -index 0000000..09203c0 ---- /dev/null -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -0,0 +1,420 @@ -+/* -+ * Driver for Broadcom BCM2708 BSC Controllers -+ * -+ * Copyright (C) 2012 Chris Boot & Frank Buss -+ * -+ * This driver is inspired by: -+ * i2c-ocores.c, by Peter Korsgaard -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* BSC register offsets */ -+#define BSC_C 0x00 -+#define BSC_S 0x04 -+#define BSC_DLEN 0x08 -+#define BSC_A 0x0c -+#define BSC_FIFO 0x10 -+#define BSC_DIV 0x14 -+#define BSC_DEL 0x18 -+#define BSC_CLKT 0x1c -+ -+/* Bitfields in BSC_C */ -+#define BSC_C_I2CEN 0x00008000 -+#define BSC_C_INTR 0x00000400 -+#define BSC_C_INTT 0x00000200 -+#define BSC_C_INTD 0x00000100 -+#define BSC_C_ST 0x00000080 -+#define BSC_C_CLEAR_1 0x00000020 -+#define BSC_C_CLEAR_2 0x00000010 -+#define BSC_C_READ 0x00000001 -+ -+/* Bitfields in BSC_S */ -+#define BSC_S_CLKT 0x00000200 -+#define BSC_S_ERR 0x00000100 -+#define BSC_S_RXF 0x00000080 -+#define BSC_S_TXE 0x00000040 -+#define BSC_S_RXD 0x00000020 -+#define BSC_S_TXD 0x00000010 -+#define BSC_S_RXR 0x00000008 -+#define BSC_S_TXW 0x00000004 -+#define BSC_S_DONE 0x00000002 -+#define BSC_S_TA 0x00000001 -+ -+#define I2C_TIMEOUT_MS 150 -+ -+#define DRV_NAME "bcm2708_i2c" -+ -+static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; -+module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); -+MODULE_PARM_DESC(baudrate, "The I2C baudrate"); -+ -+ -+struct bcm2708_i2c { -+ struct i2c_adapter adapter; -+ -+ spinlock_t lock; -+ void __iomem *base; -+ int irq; -+ struct clk *clk; -+ -+ struct completion done; -+ -+ struct i2c_msg *msg; -+ int pos; -+ int nmsgs; -+ bool error; -+}; -+ -+/* -+ * This function sets the ALT mode on the I2C pins so that we can use them with -+ * the BSC hardware. -+ * -+ * FIXME: This is a hack. Use pinmux / pinctrl. -+ */ -+static void bcm2708_i2c_init_pinmode(int id) -+{ -+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) -+ -+ int pin; -+ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); -+ -+ BUG_ON(id != 0 && id != 1); -+ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */ -+ for (pin = id*2+0; pin <= id*2+1; pin++) { -+printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin); -+ INP_GPIO(pin); /* set mode to GPIO input first */ -+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ -+ } -+ -+ iounmap(gpio); -+ -+#undef INP_GPIO -+#undef SET_GPIO_ALT -+} -+ -+static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg) -+{ -+ return readl(bi->base + reg); -+} -+ -+static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val) -+{ -+ writel(val, bi->base + reg); -+} -+ -+static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi) -+{ -+ bcm2708_wr(bi, BSC_C, 0); -+ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); -+} -+ -+static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi) -+{ -+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len)) -+ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); -+} -+ -+static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) -+{ -+ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len)) -+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); -+} -+ -+static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) -+{ -+ unsigned long bus_hz; -+ u32 cdiv; -+ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; -+ -+ bus_hz = clk_get_rate(bi->clk); -+ cdiv = bus_hz / baudrate; -+ if (cdiv > 0xffff) -+ cdiv = 0xffff; -+ -+ if (bi->msg->flags & I2C_M_RD) -+ c |= BSC_C_INTR | BSC_C_READ; -+ else -+ c |= BSC_C_INTT; -+ -+ bcm2708_wr(bi, BSC_DIV, cdiv); -+ bcm2708_wr(bi, BSC_A, bi->msg->addr); -+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ bcm2708_wr(bi, BSC_C, c); -+} -+ -+static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) -+{ -+ struct bcm2708_i2c *bi = dev_id; -+ bool handled = true; -+ u32 s; -+ -+ spin_lock(&bi->lock); -+ -+ /* we may see camera interrupts on the "other" I2C channel -+ Just return if we've not sent anything */ -+ if (!bi->nmsgs || !bi->msg ) -+ goto early_exit; -+ -+ s = bcm2708_rd(bi, BSC_S); -+ -+ if (s & (BSC_S_CLKT | BSC_S_ERR)) { -+ bcm2708_bsc_reset(bi); -+ bi->error = true; -+ -+ /* wake up our bh */ -+ complete(&bi->done); -+ } else if (s & BSC_S_DONE) { -+ bi->nmsgs--; -+ -+ if (bi->msg->flags & I2C_M_RD) -+ bcm2708_bsc_fifo_drain(bi); -+ -+ bcm2708_bsc_reset(bi); -+ -+ if (bi->nmsgs) { -+ /* advance to next message */ -+ bi->msg++; -+ bi->pos = 0; -+ bcm2708_bsc_setup(bi); ++ case VCHIQ_IOC_CREATE_SERVICE: { ++ VCHIQ_CREATE_SERVICE_T args; ++ USER_SERVICE_T *user_service = NULL; ++ void *userdata; ++ int srvstate; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ user_service = kmalloc(sizeof(USER_SERVICE_T), GFP_KERNEL); ++ if (!user_service) { ++ ret = -ENOMEM; ++ break; ++ } ++ ++ if (args.is_open) { ++ if (!instance->connected) { ++ ret = -ENOTCONN; ++ kfree(user_service); ++ break; ++ } ++ srvstate = VCHIQ_SRVSTATE_OPENING; + } else { -+ /* wake up our bh */ -+ complete(&bi->done); ++ srvstate = ++ instance->connected ? ++ VCHIQ_SRVSTATE_LISTENING : ++ VCHIQ_SRVSTATE_HIDDEN; + } -+ } else if (s & BSC_S_TXW) { -+ bcm2708_bsc_fifo_fill(bi); -+ } else if (s & BSC_S_RXR) { -+ bcm2708_bsc_fifo_drain(bi); -+ } else { -+ handled = false; -+ } + -+early_exit: -+ spin_unlock(&bi->lock); -+ -+ return handled ? IRQ_HANDLED : IRQ_NONE; -+} -+ -+static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap, -+ struct i2c_msg *msgs, int num) -+{ -+ struct bcm2708_i2c *bi = adap->algo_data; -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&bi->lock, flags); -+ -+ reinit_completion(&bi->done); -+ bi->msg = msgs; -+ bi->pos = 0; -+ bi->nmsgs = num; -+ bi->error = false; -+ -+ bcm2708_bsc_setup(bi); -+ -+ /* unlockig _after_ the setup to avoid races with the interrupt routine */ -+ spin_unlock_irqrestore(&bi->lock, flags); -+ -+ ret = wait_for_completion_timeout(&bi->done, -+ msecs_to_jiffies(I2C_TIMEOUT_MS)); -+ if (ret == 0) { -+ dev_err(&adap->dev, "transfer timed out\n"); -+ spin_lock_irqsave(&bi->lock, flags); -+ bcm2708_bsc_reset(bi); -+ spin_unlock_irqrestore(&bi->lock, flags); -+ return -ETIMEDOUT; -+ } -+ -+ return bi->error ? -EIO : num; -+} -+ -+static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap) -+{ -+ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL; -+} -+ -+static struct i2c_algorithm bcm2708_i2c_algorithm = { -+ .master_xfer = bcm2708_i2c_master_xfer, -+ .functionality = bcm2708_i2c_functionality, -+}; -+ -+static int bcm2708_i2c_probe(struct platform_device *pdev) -+{ -+ struct resource *regs; -+ int irq, err = -ENOMEM; -+ struct clk *clk; -+ struct bcm2708_i2c *bi; -+ struct i2c_adapter *adap; -+ unsigned long bus_hz; -+ u32 cdiv; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) { -+ dev_err(&pdev->dev, "could not get IO memory\n"); -+ return -ENXIO; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(&pdev->dev, "could not get IRQ\n"); -+ return irq; -+ } -+ -+ clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); -+ return PTR_ERR(clk); -+ } -+ -+ bcm2708_i2c_init_pinmode(pdev->id); -+ -+ bi = kzalloc(sizeof(*bi), GFP_KERNEL); -+ if (!bi) -+ goto out_clk_put; -+ -+ platform_set_drvdata(pdev, bi); -+ -+ adap = &bi->adapter; -+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC; -+ adap->algo = &bcm2708_i2c_algorithm; -+ adap->algo_data = bi; -+ adap->dev.parent = &pdev->dev; -+ adap->nr = pdev->id; -+ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); -+ -+ switch (pdev->id) { -+ case 0: -+ adap->class = I2C_CLASS_HWMON; -+ break; -+ case 1: -+ adap->class = I2C_CLASS_DDC; -+ break; -+ default: -+ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n"); -+ err = -ENXIO; -+ goto out_free_bi; -+ } -+ -+ spin_lock_init(&bi->lock); -+ init_completion(&bi->done); -+ -+ bi->base = ioremap(regs->start, resource_size(regs)); -+ if (!bi->base) { -+ dev_err(&pdev->dev, "could not remap memory\n"); -+ goto out_free_bi; -+ } -+ -+ bi->irq = irq; -+ bi->clk = clk; -+ -+ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED, -+ dev_name(&pdev->dev), bi); -+ if (err) { -+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); -+ goto out_iounmap; -+ } -+ -+ bcm2708_bsc_reset(bi); -+ -+ err = i2c_add_numbered_adapter(adap); -+ if (err < 0) { -+ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err); -+ goto out_free_irq; -+ } -+ -+ bus_hz = clk_get_rate(bi->clk); -+ cdiv = bus_hz / baudrate; -+ if (cdiv > 0xffff) { -+ cdiv = 0xffff; -+ baudrate = bus_hz / cdiv; -+ } -+ -+ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", -+ pdev->id, (unsigned long)regs->start, irq, baudrate); -+ -+ return 0; -+ -+out_free_irq: -+ free_irq(bi->irq, bi); -+out_iounmap: -+ iounmap(bi->base); -+out_free_bi: -+ kfree(bi); -+out_clk_put: -+ clk_put(clk); -+ return err; -+} -+ -+static int bcm2708_i2c_remove(struct platform_device *pdev) -+{ -+ struct bcm2708_i2c *bi = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ -+ i2c_del_adapter(&bi->adapter); -+ free_irq(bi->irq, bi); -+ iounmap(bi->base); -+ clk_disable(bi->clk); -+ clk_put(bi->clk); -+ kfree(bi); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm2708_i2c_driver = { -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm2708_i2c_probe, -+ .remove = bcm2708_i2c_remove, -+}; -+ -+// module_platform_driver(bcm2708_i2c_driver); -+ -+ -+static int __init bcm2708_i2c_init(void) -+{ -+ return platform_driver_register(&bcm2708_i2c_driver); -+} -+ -+static void __exit bcm2708_i2c_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_i2c_driver); -+} -+ -+module_init(bcm2708_i2c_init); -+module_exit(bcm2708_i2c_exit); -+ -+ -+ -+MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708"); -+MODULE_AUTHOR("Chris Boot "); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" DRV_NAME); -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 9982998..ae4ea48 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -86,6 +86,14 @@ config SPI_BCM2835 - is for the regular SPI controller. Slave mode operation is not also - not supported. - -+config SPI_BCM2708 -+ tristate "BCM2708 SPI controller driver (SPI0)" -+ depends on MACH_BCM2708 -+ help -+ This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This -+ driver is not compatible with the "Universal SPI Master" or the SPI slave -+ device. -+ - config SPI_BFIN5XX - tristate "SPI controller driver for ADI Blackfin5xx" - depends on BLACKFIN && !BF60x -diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile -index 6b9d2ac..b322f27 100644 ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o - obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o - obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o - obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o -+obj-$(CONFIG_SPI_BCM2708) += spi-bcm2708.o - obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o - obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o - obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -new file mode 100644 -index 0000000..b04a57d ---- /dev/null -+++ b/drivers/spi/spi-bcm2708.c -@@ -0,0 +1,626 @@ -+/* -+ * Driver for Broadcom BCM2708 SPI Controllers -+ * -+ * Copyright (C) 2012 Chris Boot -+ * -+ * This driver is inspired by: -+ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos -+ * spi-atmel.c, Copyright (C) 2006 Atmel Corporation -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* SPI register offsets */ -+#define SPI_CS 0x00 -+#define SPI_FIFO 0x04 -+#define SPI_CLK 0x08 -+#define SPI_DLEN 0x0c -+#define SPI_LTOH 0x10 -+#define SPI_DC 0x14 -+ -+/* Bitfields in CS */ -+#define SPI_CS_LEN_LONG 0x02000000 -+#define SPI_CS_DMA_LEN 0x01000000 -+#define SPI_CS_CSPOL2 0x00800000 -+#define SPI_CS_CSPOL1 0x00400000 -+#define SPI_CS_CSPOL0 0x00200000 -+#define SPI_CS_RXF 0x00100000 -+#define SPI_CS_RXR 0x00080000 -+#define SPI_CS_TXD 0x00040000 -+#define SPI_CS_RXD 0x00020000 -+#define SPI_CS_DONE 0x00010000 -+#define SPI_CS_LEN 0x00002000 -+#define SPI_CS_REN 0x00001000 -+#define SPI_CS_ADCS 0x00000800 -+#define SPI_CS_INTR 0x00000400 -+#define SPI_CS_INTD 0x00000200 -+#define SPI_CS_DMAEN 0x00000100 -+#define SPI_CS_TA 0x00000080 -+#define SPI_CS_CSPOL 0x00000040 -+#define SPI_CS_CLEAR_RX 0x00000020 -+#define SPI_CS_CLEAR_TX 0x00000010 -+#define SPI_CS_CPOL 0x00000008 -+#define SPI_CS_CPHA 0x00000004 -+#define SPI_CS_CS_10 0x00000002 -+#define SPI_CS_CS_01 0x00000001 -+ -+#define SPI_TIMEOUT_MS 150 -+ -+#define DRV_NAME "bcm2708_spi" -+ -+struct bcm2708_spi { -+ spinlock_t lock; -+ void __iomem *base; -+ int irq; -+ struct clk *clk; -+ bool stopping; -+ -+ struct list_head queue; -+ struct workqueue_struct *workq; -+ struct work_struct work; -+ struct completion done; -+ -+ const u8 *tx_buf; -+ u8 *rx_buf; -+ int len; -+}; -+ -+struct bcm2708_spi_state { -+ u32 cs; -+ u16 cdiv; -+}; -+ -+/* -+ * This function sets the ALT mode on the SPI pins so that we can use them with -+ * the SPI hardware. -+ * -+ * FIXME: This is a hack. Use pinmux / pinctrl. -+ */ -+static void bcm2708_init_pinmode(void) -+{ -+#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) -+#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) -+ -+ int pin; -+ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); -+ -+ /* SPI is on GPIO 7..11 */ -+ for (pin = 7; pin <= 11; pin++) { -+ INP_GPIO(pin); /* set mode to GPIO input first */ -+ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ -+ } -+ -+ iounmap(gpio); -+ -+#undef INP_GPIO -+#undef SET_GPIO_ALT -+} -+ -+static inline u32 bcm2708_rd(struct bcm2708_spi *bs, unsigned reg) -+{ -+ return readl(bs->base + reg); -+} -+ -+static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val) -+{ -+ writel(val, bs->base + reg); -+} -+ -+static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len) -+{ -+ u8 byte; -+ -+ while (len--) { -+ byte = bcm2708_rd(bs, SPI_FIFO); -+ if (bs->rx_buf) -+ *bs->rx_buf++ = byte; -+ } -+} -+ -+static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len) -+{ -+ u8 byte; -+ u16 val; -+ -+ if (len > bs->len) -+ len = bs->len; -+ -+ if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) { -+ /* LoSSI mode */ -+ if (unlikely(len % 2)) { -+ printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n"); -+ bs->len = 0; -+ return; -+ } -+ while (len) { -+ if (bs->tx_buf) { -+ val = *(const u16 *)bs->tx_buf; -+ bs->tx_buf += 2; -+ } else -+ val = 0; -+ bcm2708_wr(bs, SPI_FIFO, val); -+ bs->len -= 2; -+ len -= 2; -+ } -+ return; -+ } -+ -+ while (len--) { -+ byte = bs->tx_buf ? *bs->tx_buf++ : 0; -+ bcm2708_wr(bs, SPI_FIFO, byte); -+ bs->len--; -+ } -+} -+ -+static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id) -+{ -+ struct spi_master *master = dev_id; -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ u32 cs; -+ -+ spin_lock(&bs->lock); -+ -+ cs = bcm2708_rd(bs, SPI_CS); -+ -+ if (cs & SPI_CS_DONE) { -+ if (bs->len) { /* first interrupt in a transfer */ -+ /* fill the TX fifo with up to 16 bytes */ -+ bcm2708_wr_fifo(bs, 16); -+ } else { /* transfer complete */ -+ /* disable interrupts */ -+ cs &= ~(SPI_CS_INTR | SPI_CS_INTD); -+ bcm2708_wr(bs, SPI_CS, cs); -+ -+ /* drain RX FIFO */ -+ while (cs & SPI_CS_RXD) { -+ bcm2708_rd_fifo(bs, 1); -+ cs = bcm2708_rd(bs, SPI_CS); ++ userdata = args.params.userdata; ++ args.params.callback = service_callback; ++ args.params.userdata = user_service; ++ service = vchiq_add_service_internal( ++ instance->state, ++ &args.params, srvstate, ++ instance, user_service_free); ++ ++ if (service != NULL) { ++ user_service->service = service; ++ user_service->userdata = userdata; ++ user_service->instance = instance; ++ user_service->is_vchi = (args.is_vchi != 0); ++ user_service->dequeue_pending = 0; ++ user_service->close_pending = 0; ++ user_service->message_available_pos = ++ instance->completion_remove - 1; ++ user_service->msg_insert = 0; ++ user_service->msg_remove = 0; ++ sema_init(&user_service->insert_event, 0); ++ sema_init(&user_service->remove_event, 0); ++ sema_init(&user_service->close_event, 0); ++ ++ if (args.is_open) { ++ status = vchiq_open_service_internal ++ (service, instance->pid); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_remove_service(service->handle); ++ service = NULL; ++ ret = (status == VCHIQ_RETRY) ? ++ -EINTR : -EIO; ++ break; ++ } + } + -+ /* wake up our bh */ -+ complete(&bs->done); ++ if (copy_to_user((void __user *) ++ &(((VCHIQ_CREATE_SERVICE_T __user *) ++ arg)->handle), ++ (const void *)&service->handle, ++ sizeof(service->handle)) != 0) { ++ ret = -EFAULT; ++ vchiq_remove_service(service->handle); ++ } ++ ++ service = NULL; ++ } else { ++ ret = -EEXIST; ++ kfree(user_service); + } -+ } else if (cs & SPI_CS_RXR) { -+ /* read 12 bytes of data */ -+ bcm2708_rd_fifo(bs, 12); ++ } break; + -+ /* write up to 12 bytes */ -+ bcm2708_wr_fifo(bs, 12); -+ } ++ case VCHIQ_IOC_CLOSE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + -+ spin_unlock(&bs->lock); ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_close_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } + -+ return IRQ_HANDLED; -+} -+ -+static int bcm2708_setup_state(struct spi_master *master, -+ struct device *dev, struct bcm2708_spi_state *state, -+ u32 hz, u8 csel, u8 mode, u8 bpw) -+{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ int cdiv; -+ unsigned long bus_hz; -+ u32 cs = 0; -+ -+ bus_hz = clk_get_rate(bs->clk); -+ -+ if (hz >= bus_hz) { -+ cdiv = 2; /* bus_hz / 2 is as fast as we can go */ -+ } else if (hz) { -+ cdiv = DIV_ROUND_UP(bus_hz, hz); -+ -+ /* CDIV must be a power of 2, so round up */ -+ cdiv = roundup_pow_of_two(cdiv); -+ -+ if (cdiv > 65536) { -+ dev_dbg(dev, -+ "setup: %d Hz too slow, cdiv %u; min %ld Hz\n", -+ hz, cdiv, bus_hz / 65536); -+ return -EINVAL; -+ } else if (cdiv == 65536) { -+ cdiv = 0; -+ } else if (cdiv == 1) { -+ cdiv = 2; /* 1 gets rounded down to 0; == 65536 */ ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; + } -+ } else { -+ cdiv = 0; -+ } ++ else ++ ret = -EINVAL; ++ } break; + -+ switch (bpw) { -+ case 8: -+ break; -+ case 9: -+ /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */ -+ cs |= SPI_CS_LEN; -+ break; -+ default: -+ dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n", -+ bpw); -+ return -EINVAL; -+ } ++ case VCHIQ_IOC_REMOVE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; + -+ if (mode & SPI_CPOL) -+ cs |= SPI_CS_CPOL; -+ if (mode & SPI_CPHA) -+ cs |= SPI_CS_CPHA; ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ /* close_pending is false on first entry, and when the ++ wait in vchiq_close_service has been interrupted. */ ++ if (!user_service->close_pending) { ++ status = vchiq_remove_service(service->handle); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } + -+ if (!(mode & SPI_NO_CS)) { -+ if (mode & SPI_CS_HIGH) { -+ cs |= SPI_CS_CSPOL; -+ cs |= SPI_CS_CSPOL0 << csel; ++ /* close_pending is true once the underlying service ++ has been closed until the client library calls the ++ CLOSE_DELIVERED ioctl, signalling close_event. */ ++ if (user_service->close_pending && ++ down_interruptible(&user_service->close_event)) ++ status = VCHIQ_RETRY; ++ } ++ else ++ ret = -EINVAL; ++ } break; ++ ++ case VCHIQ_IOC_USE_SERVICE: ++ case VCHIQ_IOC_RELEASE_SERVICE: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ service = find_service_for_instance(instance, handle); ++ if (service != NULL) { ++ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? ++ vchiq_use_service_internal(service) : ++ vchiq_release_service_internal(service); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s: cmd %s returned error %d for " ++ "service %c%c%c%c:%03d", ++ __func__, ++ (cmd == VCHIQ_IOC_USE_SERVICE) ? ++ "VCHIQ_IOC_USE_SERVICE" : ++ "VCHIQ_IOC_RELEASE_SERVICE", ++ status, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->client_id); ++ ret = -EINVAL; ++ } ++ } else ++ ret = -EINVAL; ++ } break; ++ ++ case VCHIQ_IOC_QUEUE_MESSAGE: { ++ VCHIQ_QUEUE_MESSAGE_T args; ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; + } + -+ cs |= csel; -+ } else { -+ cs |= SPI_CS_CS_10 | SPI_CS_CS_01; -+ } ++ service = find_service_for_instance(instance, args.handle); + -+ if (state) { -+ state->cs = cs; -+ state->cdiv = cdiv; -+ dev_dbg(dev, "setup: want %d Hz; " -+ "bus_hz=%lu / cdiv=%u == %lu Hz; " -+ "mode %u: cs 0x%08X\n", -+ hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs); -+ } ++ if ((service != NULL) && (args.count <= MAX_ELEMENTS)) { ++ /* Copy elements into kernel space */ ++ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; ++ if (copy_from_user(elements, args.elements, ++ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) ++ status = vchiq_queue_message ++ (args.handle, ++ elements, args.count); ++ else ++ ret = -EFAULT; ++ } else { ++ ret = -EINVAL; ++ } ++ } break; + -+ return 0; -+} ++ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: ++ case VCHIQ_IOC_QUEUE_BULK_RECEIVE: { ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ struct bulk_waiter_node *waiter = NULL; ++ VCHIQ_BULK_DIR_T dir = ++ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? ++ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; + -+static int bcm2708_process_transfer(struct bcm2708_spi *bs, -+ struct spi_message *msg, struct spi_transfer *xfer) -+{ -+ struct spi_device *spi = msg->spi; -+ struct bcm2708_spi_state state, *stp; -+ int ret; -+ u32 cs; ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } + -+ if (bs->stopping) -+ return -ESHUTDOWN; ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } + -+ if (xfer->bits_per_word || xfer->speed_hz) { -+ ret = bcm2708_setup_state(spi->master, &spi->dev, &state, -+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, -+ spi->chip_select, spi->mode, -+ xfer->bits_per_word ? xfer->bits_per_word : -+ spi->bits_per_word); -+ if (ret) -+ return ret; ++ if (args.mode == VCHIQ_BULK_MODE_BLOCKING) { ++ waiter = kzalloc(sizeof(struct bulk_waiter_node), ++ GFP_KERNEL); ++ if (!waiter) { ++ ret = -ENOMEM; ++ break; ++ } ++ args.userdata = &waiter->bulk_waiter; ++ } else if (args.mode == VCHIQ_BULK_MODE_WAITING) { ++ struct list_head *pos; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_for_each(pos, &instance->bulk_waiter_list) { ++ if (list_entry(pos, struct bulk_waiter_node, ++ list)->pid == current->pid) { ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ break; ++ } + -+ stp = &state; -+ } else { -+ stp = spi->controller_state; -+ } ++ } ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ if (!waiter) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "no bulk_waiter found for pid %d", ++ current->pid); ++ ret = -ESRCH; ++ break; ++ } ++ vchiq_log_info(vchiq_arm_log_level, ++ "found bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); ++ args.userdata = &waiter->bulk_waiter; ++ } ++ status = vchiq_bulk_transfer ++ (args.handle, ++ VCHI_MEM_HANDLE_INVALID, ++ args.data, args.size, ++ args.userdata, args.mode, ++ dir); ++ if (!waiter) ++ break; ++ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || ++ !waiter->bulk_waiter.bulk) { ++ if (waiter->bulk_waiter.bulk) { ++ /* Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ waiter->bulk_waiter.bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); ++ } ++ kfree(waiter); ++ } else { ++ const VCHIQ_BULK_MODE_T mode_waiting = ++ VCHIQ_BULK_MODE_WAITING; ++ waiter->pid = current->pid; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_add(&waiter->list, &instance->bulk_waiter_list); ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ vchiq_log_info(vchiq_arm_log_level, ++ "saved bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); + -+ reinit_completion(&bs->done); -+ bs->tx_buf = xfer->tx_buf; -+ bs->rx_buf = xfer->rx_buf; -+ bs->len = xfer->len; ++ if (copy_to_user((void __user *) ++ &(((VCHIQ_QUEUE_BULK_TRANSFER_T __user *) ++ arg)->mode), ++ (const void *)&mode_waiting, ++ sizeof(mode_waiting)) != 0) ++ ret = -EFAULT; ++ } ++ } break; + -+ cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA; ++ case VCHIQ_IOC_AWAIT_COMPLETION: { ++ VCHIQ_AWAIT_COMPLETION_T args; + -+ bcm2708_wr(bs, SPI_CLK, stp->cdiv); -+ bcm2708_wr(bs, SPI_CS, cs); ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ if (!instance->connected) { ++ ret = -ENOTCONN; ++ break; ++ } + -+ ret = wait_for_completion_timeout(&bs->done, -+ msecs_to_jiffies(SPI_TIMEOUT_MS)); -+ if (ret == 0) { -+ dev_err(&spi->dev, "transfer timed out\n"); -+ return -ETIMEDOUT; -+ } ++ if (copy_from_user(&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } + -+ if (xfer->delay_usecs) -+ udelay(xfer->delay_usecs); ++ mutex_lock(&instance->completion_mutex); + -+ if (list_is_last(&xfer->transfer_list, &msg->transfers) || -+ xfer->cs_change) { -+ /* clear TA and interrupt flags */ -+ bcm2708_wr(bs, SPI_CS, stp->cs); -+ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ while ((instance->completion_remove == ++ instance->completion_insert) ++ && !instance->closing) { ++ int rc; ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ mutex_unlock(&instance->completion_mutex); ++ rc = down_interruptible(&instance->insert_event); ++ mutex_lock(&instance->completion_mutex); ++ if (rc != 0) { ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ vchiq_log_info(vchiq_arm_log_level, ++ "AWAIT_COMPLETION interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); + -+ msg->actual_length += (xfer->len - bs->len); ++ /* A read memory barrier is needed to stop prefetch of a stale ++ ** completion record ++ */ ++ rmb(); + -+ return 0; -+} ++ if (ret == 0) { ++ int msgbufcount = args.msgbufcount; ++ for (ret = 0; ret < args.count; ret++) { ++ VCHIQ_COMPLETION_DATA_T *completion; ++ VCHIQ_SERVICE_T *service; ++ USER_SERVICE_T *user_service; ++ VCHIQ_HEADER_T *header; ++ if (instance->completion_remove == ++ instance->completion_insert) ++ break; ++ completion = &instance->completions[ ++ instance->completion_remove & ++ (MAX_COMPLETIONS - 1)]; + -+static void bcm2708_work(struct work_struct *work) -+{ -+ struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work); -+ unsigned long flags; -+ struct spi_message *msg; -+ struct spi_transfer *xfer; -+ int status = 0; ++ service = completion->service_userdata; ++ user_service = service->base.userdata; ++ completion->service_userdata = ++ user_service->userdata; + -+ spin_lock_irqsave(&bs->lock, flags); -+ while (!list_empty(&bs->queue)) { -+ msg = list_first_entry(&bs->queue, struct spi_message, queue); -+ list_del_init(&msg->queue); -+ spin_unlock_irqrestore(&bs->lock, flags); ++ header = completion->header; ++ if (header) { ++ void __user *msgbuf; ++ int msglen; + -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ status = bcm2708_process_transfer(bs, msg, xfer); -+ if (status) ++ msglen = header->size + ++ sizeof(VCHIQ_HEADER_T); ++ /* This must be a VCHIQ-style service */ ++ if (args.msgbufsize < msglen) { ++ vchiq_log_error( ++ vchiq_arm_log_level, ++ "header %x: msgbufsize" ++ " %x < msglen %x", ++ (unsigned int)header, ++ args.msgbufsize, ++ msglen); ++ WARN(1, "invalid message " ++ "size\n"); ++ if (ret == 0) ++ ret = -EMSGSIZE; ++ break; ++ } ++ if (msgbufcount <= 0) ++ /* Stall here for lack of a ++ ** buffer for the message. */ ++ break; ++ /* Get the pointer from user space */ ++ msgbufcount--; ++ if (copy_from_user(&msgbuf, ++ (const void __user *) ++ &args.msgbufs[msgbufcount], ++ sizeof(msgbuf)) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Copy the message to user space */ ++ if (copy_to_user(msgbuf, header, ++ msglen) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Now it has been copied, the message ++ ** can be released. */ ++ vchiq_release_message(service->handle, ++ header); ++ ++ /* The completion must point to the ++ ** msgbuf. */ ++ completion->header = msgbuf; ++ } ++ ++ if ((completion->reason == ++ VCHIQ_SERVICE_CLOSED) && ++ !instance->use_close_delivered) ++ unlock_service(service); ++ ++ if (copy_to_user((void __user *)( ++ (size_t)args.buf + ++ ret * sizeof(VCHIQ_COMPLETION_DATA_T)), ++ completion, ++ sizeof(VCHIQ_COMPLETION_DATA_T)) != 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ instance->completion_remove++; ++ } ++ ++ if (msgbufcount != args.msgbufcount) { ++ if (copy_to_user((void __user *) ++ &((VCHIQ_AWAIT_COMPLETION_T *)arg)-> ++ msgbufcount, ++ &msgbufcount, ++ sizeof(msgbufcount)) != 0) { ++ ret = -EFAULT; ++ } ++ } ++ } ++ ++ if (ret != 0) ++ up(&instance->remove_event); ++ mutex_unlock(&instance->completion_mutex); ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ } break; ++ ++ case VCHIQ_IOC_DEQUEUE_MESSAGE: { ++ VCHIQ_DEQUEUE_MESSAGE_T args; ++ USER_SERVICE_T *user_service; ++ VCHIQ_HEADER_T *header; ++ ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } ++ user_service = (USER_SERVICE_T *)service->base.userdata; ++ if (user_service->is_vchi == 0) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ spin_lock(&msg_queue_spinlock); ++ if (user_service->msg_remove == user_service->msg_insert) { ++ if (!args.blocking) { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ ret = -EWOULDBLOCK; ++ break; ++ } ++ user_service->dequeue_pending = 1; ++ do { ++ spin_unlock(&msg_queue_spinlock); ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (down_interruptible( ++ &user_service->insert_event) != 0) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "DEQUEUE_MESSAGE interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ spin_lock(&msg_queue_spinlock); ++ } while (user_service->msg_remove == ++ user_service->msg_insert); ++ ++ if (ret) + break; + } + -+ msg->status = status; -+ msg->complete(msg->context); ++ BUG_ON((int)(user_service->msg_insert - ++ user_service->msg_remove) < 0); + -+ spin_lock_irqsave(&bs->lock, flags); ++ header = user_service->msg_queue[user_service->msg_remove & ++ (MSG_QUEUE_SIZE - 1)]; ++ user_service->msg_remove++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ up(&user_service->remove_event); ++ if (header == NULL) ++ ret = -ENOTCONN; ++ else if (header->size <= args.bufsize) { ++ /* Copy to user space if msgbuf is not NULL */ ++ if ((args.buf == NULL) || ++ (copy_to_user((void __user *)args.buf, ++ header->data, ++ header->size) == 0)) { ++ ret = header->size; ++ vchiq_release_message( ++ service->handle, ++ header); ++ } else ++ ret = -EFAULT; ++ } else { ++ vchiq_log_error(vchiq_arm_log_level, ++ "header %x: bufsize %x < size %x", ++ (unsigned int)header, args.bufsize, ++ header->size); ++ WARN(1, "invalid size\n"); ++ ret = -EMSGSIZE; ++ } ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ } break; ++ ++ case VCHIQ_IOC_GET_CLIENT_ID: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ ret = vchiq_get_client_id(handle); ++ } break; ++ ++ case VCHIQ_IOC_GET_CONFIG: { ++ VCHIQ_GET_CONFIG_T args; ++ VCHIQ_CONFIG_T config; ++ ++ if (copy_from_user(&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ if (args.config_size > sizeof(config)) { ++ ret = -EINVAL; ++ break; ++ } ++ status = vchiq_get_config(instance, args.config_size, &config); ++ if (status == VCHIQ_SUCCESS) { ++ if (copy_to_user((void __user *)args.pconfig, ++ &config, args.config_size) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ } ++ } break; ++ ++ case VCHIQ_IOC_SET_SERVICE_OPTION: { ++ VCHIQ_SET_SERVICE_OPTION_T args; ++ ++ if (copy_from_user( ++ &args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ service = find_service_for_instance(instance, args.handle); ++ if (!service) { ++ ret = -EINVAL; ++ break; ++ } ++ ++ status = vchiq_set_service_option( ++ args.handle, args.option, args.value); ++ } break; ++ ++ case VCHIQ_IOC_DUMP_PHYS_MEM: { ++ VCHIQ_DUMP_MEM_T args; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ dump_phys_mem(args.virt_addr, args.num_bytes); ++ } break; ++ ++ case VCHIQ_IOC_LIB_VERSION: { ++ unsigned int lib_version = (unsigned int)arg; ++ ++ if (lib_version < VCHIQ_VERSION_MIN) ++ ret = -EINVAL; ++ else if (lib_version >= VCHIQ_VERSION_CLOSE_DELIVERED) ++ instance->use_close_delivered = 1; ++ } break; ++ ++ case VCHIQ_IOC_CLOSE_DELIVERED: { ++ VCHIQ_SERVICE_HANDLE_T handle = (VCHIQ_SERVICE_HANDLE_T)arg; ++ ++ service = find_closed_service_for_instance(instance, handle); ++ if (service != NULL) { ++ USER_SERVICE_T *user_service = ++ (USER_SERVICE_T *)service->base.userdata; ++ close_delivered(user_service); ++ } ++ else ++ ret = -EINVAL; ++ } break; ++ ++ default: ++ ret = -ENOTTY; ++ break; + } -+ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ if (service) ++ unlock_service(service); ++ ++ if (ret == 0) { ++ if (status == VCHIQ_ERROR) ++ ret = -EIO; ++ else if (status == VCHIQ_RETRY) ++ ret = -EINTR; ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && (ret < 0) && (ret != -EINTR) && ++ (ret != -EWOULDBLOCK)) ++ vchiq_log_info(vchiq_arm_log_level, ++ " ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ++ ioctl_names[_IOC_NR(cmd)] : ++ "", ++ status, ret); ++ else ++ vchiq_log_trace(vchiq_arm_log_level, ++ " ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ++ ioctl_names[_IOC_NR(cmd)] : ++ "", ++ status, ret); ++ ++ return ret; +} + -+static int bcm2708_spi_setup(struct spi_device *spi) ++/**************************************************************************** ++* ++* vchiq_open ++* ++***************************************************************************/ ++ ++static int ++vchiq_open(struct inode *inode, struct file *file) +{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); -+ struct bcm2708_spi_state *state; -+ int ret; ++ int dev = iminor(inode) & 0x0f; ++ vchiq_log_info(vchiq_arm_log_level, "vchiq_open"); ++ switch (dev) { ++ case VCHIQ_MINOR: { ++ int ret; ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ VCHIQ_INSTANCE_T instance; + -+ if (bs->stopping) -+ return -ESHUTDOWN; ++ if (!state) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "vchiq has no connection to VideoCore"); ++ return -ENOTCONN; ++ } + -+ if (!(spi->mode & SPI_NO_CS) && -+ (spi->chip_select > spi->master->num_chipselect)) { -+ dev_dbg(&spi->dev, -+ "setup: invalid chipselect %u (%u defined)\n", -+ spi->chip_select, spi->master->num_chipselect); -+ return -EINVAL; -+ } -+ -+ state = spi->controller_state; -+ if (!state) { -+ state = kzalloc(sizeof(*state), GFP_KERNEL); -+ if (!state) ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) + return -ENOMEM; + -+ spi->controller_state = state; -+ } ++ instance->state = state; ++ instance->pid = current->tgid; + -+ ret = bcm2708_setup_state(spi->master, &spi->dev, state, -+ spi->max_speed_hz, spi->chip_select, spi->mode, -+ spi->bits_per_word); -+ if (ret < 0) { -+ kfree(state); -+ spi->controller_state = NULL; -+ return ret; -+ } -+ -+ dev_dbg(&spi->dev, -+ "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n", -+ spi->chip_select, spi->max_speed_hz, spi->bits_per_word, -+ spi->mode, state->cs, state->cdiv); -+ -+ return 0; -+} -+ -+static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg) -+{ -+ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); -+ struct spi_transfer *xfer; -+ int ret; -+ unsigned long flags; -+ -+ if (unlikely(list_empty(&msg->transfers))) -+ return -EINVAL; -+ -+ if (bs->stopping) -+ return -ESHUTDOWN; -+ -+ list_for_each_entry(xfer, &msg->transfers, transfer_list) { -+ if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { -+ dev_dbg(&spi->dev, "missing rx or tx buf\n"); -+ return -EINVAL; ++ ret = vchiq_debugfs_add_instance(instance); ++ if (ret != 0) { ++ kfree(instance); ++ return ret; + } + -+ if (!xfer->bits_per_word || xfer->speed_hz) -+ continue; ++ sema_init(&instance->insert_event, 0); ++ sema_init(&instance->remove_event, 0); ++ mutex_init(&instance->completion_mutex); ++ mutex_init(&instance->bulk_waiter_list_mutex); ++ INIT_LIST_HEAD(&instance->bulk_waiter_list); + -+ ret = bcm2708_setup_state(spi->master, &spi->dev, NULL, -+ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, -+ spi->chip_select, spi->mode, -+ xfer->bits_per_word ? xfer->bits_per_word : -+ spi->bits_per_word); -+ if (ret) -+ return ret; -+ } ++ file->private_data = instance; ++ } break; + -+ msg->status = -EINPROGRESS; -+ msg->actual_length = 0; -+ -+ spin_lock_irqsave(&bs->lock, flags); -+ list_add_tail(&msg->queue, &bs->queue); -+ queue_work(bs->workq, &bs->work); -+ spin_unlock_irqrestore(&bs->lock, flags); -+ -+ return 0; -+} -+ -+static void bcm2708_spi_cleanup(struct spi_device *spi) -+{ -+ if (spi->controller_state) { -+ kfree(spi->controller_state); -+ spi->controller_state = NULL; -+ } -+} -+ -+static int bcm2708_spi_probe(struct platform_device *pdev) -+{ -+ struct resource *regs; -+ int irq, err = -ENOMEM; -+ struct clk *clk; -+ struct spi_master *master; -+ struct bcm2708_spi *bs; -+ -+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!regs) { -+ dev_err(&pdev->dev, "could not get IO memory\n"); ++ default: ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unknown minor device: %d", dev); + return -ENXIO; + } + -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(&pdev->dev, "could not get IRQ\n"); -+ return irq; ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vchiq_release ++* ++***************************************************************************/ ++ ++static int ++vchiq_release(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode) & 0x0f; ++ int ret = 0; ++ switch (dev) { ++ case VCHIQ_MINOR: { ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq_release: instance=%lx", ++ (unsigned long)instance); ++ ++ if (!state) { ++ ret = -EPERM; ++ goto out; ++ } ++ ++ /* Ensure videocore is awake to allow termination. */ ++ vchiq_use_internal(instance->state, NULL, ++ USE_TYPE_VCHIQ); ++ ++ mutex_lock(&instance->completion_mutex); ++ ++ /* Wake the completion thread and ask it to exit */ ++ instance->closing = 1; ++ up(&instance->insert_event); ++ ++ mutex_unlock(&instance->completion_mutex); ++ ++ /* Wake the slot handler if the completion queue is full. */ ++ up(&instance->remove_event); ++ ++ /* Mark all services for termination... */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ USER_SERVICE_T *user_service = service->base.userdata; ++ ++ /* Wake the slot handler if the msg queue is full. */ ++ up(&user_service->remove_event); ++ ++ vchiq_terminate_service_internal(service); ++ unlock_service(service); ++ } ++ ++ /* ...and wait for them to die */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, &i)) ++ != NULL) { ++ USER_SERVICE_T *user_service = service->base.userdata; ++ ++ down(&service->remove_event); ++ ++ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); ++ ++ spin_lock(&msg_queue_spinlock); ++ ++ while (user_service->msg_remove != ++ user_service->msg_insert) { ++ VCHIQ_HEADER_T *header = user_service-> ++ msg_queue[user_service->msg_remove & ++ (MSG_QUEUE_SIZE - 1)]; ++ user_service->msg_remove++; ++ spin_unlock(&msg_queue_spinlock); ++ ++ if (header) ++ vchiq_release_message( ++ service->handle, ++ header); ++ spin_lock(&msg_queue_spinlock); ++ } ++ ++ spin_unlock(&msg_queue_spinlock); ++ ++ unlock_service(service); ++ } ++ ++ /* Release any closed services */ ++ while (instance->completion_remove != ++ instance->completion_insert) { ++ VCHIQ_COMPLETION_DATA_T *completion; ++ VCHIQ_SERVICE_T *service; ++ completion = &instance->completions[ ++ instance->completion_remove & ++ (MAX_COMPLETIONS - 1)]; ++ service = completion->service_userdata; ++ if (completion->reason == VCHIQ_SERVICE_CLOSED) ++ { ++ USER_SERVICE_T *user_service = ++ service->base.userdata; ++ ++ /* Wake any blocked user-thread */ ++ if (instance->use_close_delivered) ++ up(&user_service->close_event); ++ unlock_service(service); ++ } ++ instance->completion_remove++; ++ } ++ ++ /* Release the PEER service count. */ ++ vchiq_release_internal(instance->state, NULL); ++ ++ { ++ struct list_head *pos, *next; ++ list_for_each_safe(pos, next, ++ &instance->bulk_waiter_list) { ++ struct bulk_waiter_node *waiter; ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ vchiq_log_info(vchiq_arm_log_level, ++ "bulk_waiter - cleaned up %x " ++ "for pid %d", ++ (unsigned int)waiter, waiter->pid); ++ kfree(waiter); ++ } ++ } ++ ++ vchiq_debugfs_remove_instance(instance); ++ ++ kfree(instance); ++ file->private_data = NULL; ++ } break; ++ ++ default: ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unknown minor device: %d", dev); ++ ret = -ENXIO; + } + -+ clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); -+ return PTR_ERR(clk); ++out: ++ return ret; ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump ++* ++***************************************************************************/ ++ ++void ++vchiq_dump(void *dump_context, const char *str, int len) ++{ ++ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; ++ ++ if (context->actual < context->space) { ++ int copy_bytes; ++ if (context->offset > 0) { ++ int skip_bytes = min(len, (int)context->offset); ++ str += skip_bytes; ++ len -= skip_bytes; ++ context->offset -= skip_bytes; ++ if (context->offset > 0) ++ return; ++ } ++ copy_bytes = min(len, (int)(context->space - context->actual)); ++ if (copy_bytes == 0) ++ return; ++ if (copy_to_user(context->buf + context->actual, str, ++ copy_bytes)) ++ context->actual = -EFAULT; ++ context->actual += copy_bytes; ++ len -= copy_bytes; ++ ++ /* If tne terminating NUL is included in the length, then it ++ ** marks the end of a line and should be replaced with a ++ ** carriage return. */ ++ if ((len == 0) && (str[copy_bytes - 1] == '\0')) { ++ char cr = '\n'; ++ if (copy_to_user(context->buf + context->actual - 1, ++ &cr, 1)) ++ context->actual = -EFAULT; ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_instance_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_instances(void *dump_context) ++{ ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ char buf[80]; ++ int len; ++ int i; ++ ++ /* There is no list of instances, so instead scan all services, ++ marking those that have been dumped. */ ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; ++ ++ if (service && (service->base.callback == service_callback)) { ++ instance = service->instance; ++ if (instance) ++ instance->mark = 0; ++ } + } + -+ bcm2708_init_pinmode(); ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; + -+ master = spi_alloc_master(&pdev->dev, sizeof(*bs)); -+ if (!master) { -+ dev_err(&pdev->dev, "spi_alloc_master() failed\n"); -+ goto out_clk_put; ++ if (service && (service->base.callback == service_callback)) { ++ instance = service->instance; ++ if (instance && !instance->mark) { ++ len = snprintf(buf, sizeof(buf), ++ "Instance %x: pid %d,%s completions " ++ "%d/%d", ++ (unsigned int)instance, instance->pid, ++ instance->connected ? " connected, " : ++ "", ++ instance->completion_insert - ++ instance->completion_remove, ++ MAX_COMPLETIONS); ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ instance->mark = 1; ++ } ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_service_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; ++ char buf[80]; ++ int len; ++ ++ len = snprintf(buf, sizeof(buf), " instance %x", ++ (unsigned int)service->instance); ++ ++ if ((service->base.callback == service_callback) && ++ user_service->is_vchi) { ++ len += snprintf(buf + len, sizeof(buf) - len, ++ ", %d/%d messages", ++ user_service->msg_insert - user_service->msg_remove, ++ MSG_QUEUE_SIZE); ++ ++ if (user_service->dequeue_pending) ++ len += snprintf(buf + len, sizeof(buf) - len, ++ " (dequeue pending)"); + } + -+ /* the spi->mode bits understood by this driver: */ -+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS; ++ vchiq_dump(dump_context, buf, len + 1); ++} + -+ master->bus_num = pdev->id; -+ master->num_chipselect = 3; -+ master->setup = bcm2708_spi_setup; -+ master->transfer = bcm2708_spi_transfer; -+ master->cleanup = bcm2708_spi_cleanup; -+ platform_set_drvdata(pdev, master); ++/**************************************************************************** ++* ++* dump_user_mem ++* ++***************************************************************************/ + -+ bs = spi_master_get_devdata(master); ++static void ++dump_phys_mem(void *virt_addr, uint32_t num_bytes) ++{ ++ int rc; ++ uint8_t *end_virt_addr = virt_addr + num_bytes; ++ int num_pages; ++ int offset; ++ int end_offset; ++ int page_idx; ++ int prev_idx; ++ struct page *page; ++ struct page **pages; ++ uint8_t *kmapped_virt_ptr; + -+ spin_lock_init(&bs->lock); -+ INIT_LIST_HEAD(&bs->queue); -+ init_completion(&bs->done); -+ INIT_WORK(&bs->work, bcm2708_work); ++ /* Align virtAddr and endVirtAddr to 16 byte boundaries. */ + -+ bs->base = ioremap(regs->start, resource_size(regs)); -+ if (!bs->base) { -+ dev_err(&pdev->dev, "could not remap memory\n"); -+ goto out_master_put; ++ virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL); ++ end_virt_addr = (void *)(((unsigned long)end_virt_addr + 15uL) & ++ ~0x0fuL); ++ ++ offset = (int)(long)virt_addr & (PAGE_SIZE - 1); ++ end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1); ++ ++ num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL); ++ if (pages == NULL) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocation memory for %d pages\n", ++ num_pages); ++ return; + } + -+ bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev)); -+ if (!bs->workq) { -+ dev_err(&pdev->dev, "could not create workqueue\n"); -+ goto out_iounmap; ++ down_read(¤t->mm->mmap_sem); ++ rc = get_user_pages(current, /* task */ ++ current->mm, /* mm */ ++ (unsigned long)virt_addr, /* start */ ++ num_pages, /* len */ ++ 0, /* write */ ++ 0, /* force */ ++ pages, /* pages (array of page pointers) */ ++ NULL); /* vmas */ ++ up_read(¤t->mm->mmap_sem); ++ ++ prev_idx = -1; ++ page = NULL; ++ ++ while (offset < end_offset) { ++ ++ int page_offset = offset % PAGE_SIZE; ++ page_idx = offset / PAGE_SIZE; ++ ++ if (page_idx != prev_idx) { ++ ++ if (page != NULL) ++ kunmap(page); ++ page = pages[page_idx]; ++ kmapped_virt_ptr = kmap(page); ++ ++ prev_idx = page_idx; ++ } ++ ++ if (vchiq_arm_log_level >= VCHIQ_LOG_TRACE) ++ vchiq_log_dump_mem("ph", ++ (uint32_t)(unsigned long)&kmapped_virt_ptr[ ++ page_offset], ++ &kmapped_virt_ptr[page_offset], 16); ++ ++ offset += 16; ++ } ++ if (page != NULL) ++ kunmap(page); ++ ++ for (page_idx = 0; page_idx < num_pages; page_idx++) ++ page_cache_release(pages[page_idx]); ++ ++ kfree(pages); ++} ++ ++/**************************************************************************** ++* ++* vchiq_read ++* ++***************************************************************************/ ++ ++static ssize_t ++vchiq_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ DUMP_CONTEXT_T context; ++ context.buf = buf; ++ context.actual = 0; ++ context.space = count; ++ context.offset = *ppos; ++ ++ vchiq_dump_state(&context, &g_state); ++ ++ *ppos += context.actual; ++ ++ return context.actual; ++} ++ ++VCHIQ_STATE_T * ++vchiq_get_state(void) ++{ ++ ++ if (g_state.remote == NULL) ++ printk(KERN_ERR "%s: g_state.remote == NULL\n", __func__); ++ else if (g_state.remote->initialised != 1) ++ printk(KERN_NOTICE "%s: g_state.remote->initialised != 1 (%d)\n", ++ __func__, g_state.remote->initialised); ++ ++ return ((g_state.remote != NULL) && ++ (g_state.remote->initialised == 1)) ? &g_state : NULL; ++} ++ ++static const struct file_operations ++vchiq_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = vchiq_ioctl, ++ .open = vchiq_open, ++ .release = vchiq_release, ++ .read = vchiq_read ++}; ++ ++/* ++ * Autosuspend related functionality ++ */ ++ ++int ++vchiq_videocore_wanted(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ if (!arm_state) ++ /* autosuspend not supported - always return wanted */ ++ return 1; ++ else if (arm_state->blocked_count) ++ return 1; ++ else if (!arm_state->videocore_use_count) ++ /* usage count zero - check for override unless we're forcing */ ++ if (arm_state->resume_blocked) ++ return 0; ++ else ++ return vchiq_platform_videocore_wanted(state); ++ else ++ /* non-zero usage count - videocore still required */ ++ return 1; ++} ++ ++static VCHIQ_STATUS_T ++vchiq_keepalive_vchiq_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T service_user, ++ void *bulk_user) ++{ ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s callback reason %d", __func__, reason); ++ return 0; ++} ++ ++static int ++vchiq_keepalive_thread_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ VCHIQ_STATUS_T status; ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_SERVICE_HANDLE_T ka_handle; ++ ++ VCHIQ_SERVICE_PARAMS_T params = { ++ .fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'), ++ .callback = vchiq_keepalive_vchiq_callback, ++ .version = KEEPALIVE_VER, ++ .version_min = KEEPALIVE_VER_MIN ++ }; ++ ++ status = vchiq_initialise(&instance); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_initialise failed %d", __func__, status); ++ goto exit; + } + -+ bs->irq = irq; -+ bs->clk = clk; -+ bs->stopping = false; -+ -+ err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev), -+ master); -+ if (err) { -+ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); -+ goto out_workqueue; ++ status = vchiq_connect(instance); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_connect failed %d", __func__, status); ++ goto shutdown; + } + -+ /* initialise the hardware */ -+ clk_enable(clk); -+ bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); -+ -+ err = spi_register_master(master); -+ if (err) { -+ dev_err(&pdev->dev, "could not register SPI master: %d\n", err); -+ goto out_free_irq; ++ status = vchiq_add_service(instance, ¶ms, &ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_open_service failed %d", __func__, status); ++ goto shutdown; + } + -+ dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n", -+ (unsigned long)regs->start, irq); ++ while (1) { ++ long rc = 0, uc = 0; ++ if (wait_for_completion_interruptible(&arm_state->ka_evt) ++ != 0) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s interrupted", __func__); ++ flush_signals(current); ++ continue; ++ } ++ ++ /* read and clear counters. Do release_count then use_count to ++ * prevent getting more releases than uses */ ++ rc = atomic_xchg(&arm_state->ka_release_count, 0); ++ uc = atomic_xchg(&arm_state->ka_use_count, 0); ++ ++ /* Call use/release service the requisite number of times. ++ * Process use before release so use counts don't go negative */ ++ while (uc--) { ++ atomic_inc(&arm_state->ka_use_ack_count); ++ status = vchiq_use_service(ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_use_service error %d", ++ __func__, status); ++ } ++ } ++ while (rc--) { ++ status = vchiq_release_service(ka_handle); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s vchiq_release_service error %d", ++ __func__, status); ++ } ++ } ++ } ++ ++shutdown: ++ vchiq_shutdown(instance); ++exit: ++ return 0; ++} ++ ++ ++ ++VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (arm_state) { ++ rwlock_init(&arm_state->susp_res_lock); ++ ++ init_completion(&arm_state->ka_evt); ++ atomic_set(&arm_state->ka_use_count, 0); ++ atomic_set(&arm_state->ka_use_ack_count, 0); ++ atomic_set(&arm_state->ka_release_count, 0); ++ ++ init_completion(&arm_state->vc_suspend_complete); ++ ++ init_completion(&arm_state->vc_resume_complete); ++ /* Initialise to 'done' state. We only want to block on resume ++ * completion while videocore is suspended. */ ++ set_resume_state(arm_state, VC_RESUME_RESUMED); ++ ++ init_completion(&arm_state->resume_blocker); ++ /* Initialise to 'done' state. We only want to block on this ++ * completion while resume is blocked */ ++ complete_all(&arm_state->resume_blocker); ++ ++ init_completion(&arm_state->blocked_blocker); ++ /* Initialise to 'done' state. We only want to block on this ++ * completion while things are waiting on the resume blocker */ ++ complete_all(&arm_state->blocked_blocker); ++ ++ arm_state->suspend_timer_timeout = SUSPEND_TIMER_TIMEOUT_MS; ++ arm_state->suspend_timer_running = 0; ++ init_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer.data = (unsigned long)(state); ++ arm_state->suspend_timer.function = suspend_timer_callback; ++ ++ arm_state->first_connect = 0; ++ ++ } ++ return status; ++} ++ ++/* ++** Functions to modify the state variables; ++** set_suspend_state ++** set_resume_state ++** ++** There are more state variables than we might like, so ensure they remain in ++** step. Suspend and resume state are maintained separately, since most of ++** these state machines can operate independently. However, there are a few ++** states where state transitions in one state machine cause a reset to the ++** other state machine. In addition, there are some completion events which ++** need to occur on state machine reset and end-state(s), so these are also ++** dealt with in these functions. ++** ++** In all states we set the state variable according to the input, but in some ++** cases we perform additional steps outlined below; ++** ++** VC_SUSPEND_IDLE - Initialise the suspend completion at the same time. ++** The suspend completion is completed after any suspend ++** attempt. When we reset the state machine we also reset ++** the completion. This reset occurs when videocore is ++** resumed, and also if we initiate suspend after a suspend ++** failure. ++** ++** VC_SUSPEND_IN_PROGRESS - This state is considered the point of no return for ++** suspend - ie from this point on we must try to suspend ++** before resuming can occur. We therefore also reset the ++** resume state machine to VC_RESUME_IDLE in this state. ++** ++** VC_SUSPEND_SUSPENDED - Suspend has completed successfully. Also call ++** complete_all on the suspend completion to notify ++** anything waiting for suspend to happen. ++** ++** VC_SUSPEND_REJECTED - Videocore rejected suspend. Videocore will also ++** initiate resume, so no need to alter resume state. ++** We call complete_all on the suspend completion to notify ++** of suspend rejection. ++** ++** VC_SUSPEND_FAILED - We failed to initiate videocore suspend. We notify the ++** suspend completion and reset the resume state machine. ++** ++** VC_RESUME_IDLE - Initialise the resume completion at the same time. The ++** resume completion is in it's 'done' state whenever ++** videcore is running. Therfore, the VC_RESUME_IDLE state ++** implies that videocore is suspended. ++** Hence, any thread which needs to wait until videocore is ++** running can wait on this completion - it will only block ++** if videocore is suspended. ++** ++** VC_RESUME_RESUMED - Resume has completed successfully. Videocore is running. ++** Call complete_all on the resume completion to unblock ++** any threads waiting for resume. Also reset the suspend ++** state machine to it's idle state. ++** ++** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. ++*/ ++ ++void ++set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_suspend_status new_state) ++{ ++ /* set the state in all cases */ ++ arm_state->vc_suspend_state = new_state; ++ ++ /* state specific additional actions */ ++ switch (new_state) { ++ case VC_SUSPEND_FORCE_CANCELED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_REJECTED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_FAILED: ++ complete_all(&arm_state->vc_suspend_complete); ++ arm_state->vc_resume_state = VC_RESUME_RESUMED; ++ complete_all(&arm_state->vc_resume_complete); ++ break; ++ case VC_SUSPEND_IDLE: ++ reinit_completion(&arm_state->vc_suspend_complete); ++ break; ++ case VC_SUSPEND_REQUESTED: ++ break; ++ case VC_SUSPEND_IN_PROGRESS: ++ set_resume_state(arm_state, VC_RESUME_IDLE); ++ break; ++ case VC_SUSPEND_SUSPENDED: ++ complete_all(&arm_state->vc_suspend_complete); ++ break; ++ default: ++ BUG(); ++ break; ++ } ++} ++ ++void ++set_resume_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_resume_status new_state) ++{ ++ /* set the state in all cases */ ++ arm_state->vc_resume_state = new_state; ++ ++ /* state specific additional actions */ ++ switch (new_state) { ++ case VC_RESUME_FAILED: ++ break; ++ case VC_RESUME_IDLE: ++ reinit_completion(&arm_state->vc_resume_complete); ++ break; ++ case VC_RESUME_REQUESTED: ++ break; ++ case VC_RESUME_IN_PROGRESS: ++ break; ++ case VC_RESUME_RESUMED: ++ complete_all(&arm_state->vc_resume_complete); ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ break; ++ default: ++ BUG(); ++ break; ++ } ++} ++ ++ ++/* should be called with the write lock held */ ++inline void ++start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ del_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer.expires = jiffies + ++ msecs_to_jiffies(arm_state-> ++ suspend_timer_timeout); ++ add_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer_running = 1; ++} ++ ++/* should be called with the write lock held */ ++static inline void ++stop_suspend_timer(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ if (arm_state->suspend_timer_running) { ++ del_timer(&arm_state->suspend_timer); ++ arm_state->suspend_timer_running = 0; ++ } ++} ++ ++static inline int ++need_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) && ++ (arm_state->vc_resume_state < VC_RESUME_REQUESTED) && ++ vchiq_videocore_wanted(state); ++} ++ ++static int ++block_resume(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ int status = VCHIQ_SUCCESS; ++ const unsigned long timeout_val = ++ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS); ++ int resume_count = 0; ++ ++ /* Allow any threads which were blocked by the last force suspend to ++ * complete if they haven't already. Only give this one shot; if ++ * blocked_count is incremented after blocked_blocker is completed ++ * (which only happens when blocked_count hits 0) then those threads ++ * will have to wait until next time around */ ++ if (arm_state->blocked_count) { ++ reinit_completion(&arm_state->blocked_blocker); ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s wait for previously " ++ "blocked clients", __func__); ++ if (wait_for_completion_interruptible_timeout( ++ &arm_state->blocked_blocker, timeout_val) ++ <= 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s wait for " ++ "previously blocked clients failed" , __func__); ++ status = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s previously blocked " ++ "clients resumed", __func__); ++ write_lock_bh(&arm_state->susp_res_lock); ++ } ++ ++ /* We need to wait for resume to complete if it's in process */ ++ while (arm_state->vc_resume_state != VC_RESUME_RESUMED && ++ arm_state->vc_resume_state > VC_RESUME_IDLE) { ++ if (resume_count > 1) { ++ status = VCHIQ_ERROR; ++ vchiq_log_error(vchiq_susp_log_level, "%s waited too " ++ "many times for resume" , __func__); ++ goto out; ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s wait for resume", ++ __func__); ++ if (wait_for_completion_interruptible_timeout( ++ &arm_state->vc_resume_complete, timeout_val) ++ <= 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s wait for " ++ "resume failed (%s)", __func__, ++ resume_state_names[arm_state->vc_resume_state + ++ VC_RESUME_NUM_OFFSET]); ++ status = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s resumed", __func__); ++ write_lock_bh(&arm_state->susp_res_lock); ++ resume_count++; ++ } ++ reinit_completion(&arm_state->resume_blocker); ++ arm_state->resume_blocked = 1; ++ ++out: ++ return status; ++} ++ ++static inline void ++unblock_resume(VCHIQ_ARM_STATE_T *arm_state) ++{ ++ complete_all(&arm_state->resume_blocker); ++ arm_state->resume_blocked = 0; ++} ++ ++/* Initiate suspend via slot handler. Should be called with the write lock ++ * held */ ++VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ status = VCHIQ_SUCCESS; ++ ++ ++ switch (arm_state->vc_suspend_state) { ++ case VC_SUSPEND_REQUESTED: ++ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already " ++ "requested", __func__); ++ break; ++ case VC_SUSPEND_IN_PROGRESS: ++ vchiq_log_info(vchiq_susp_log_level, "%s: suspend already in " ++ "progress", __func__); ++ break; ++ ++ default: ++ /* We don't expect to be in other states, so log but continue ++ * anyway */ ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s unexpected suspend state %s", __func__, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ /* fall through */ ++ case VC_SUSPEND_REJECTED: ++ case VC_SUSPEND_FAILED: ++ /* Ensure any idle state actions have been run */ ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ /* fall through */ ++ case VC_SUSPEND_IDLE: ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: suspending", __func__); ++ set_suspend_state(arm_state, VC_SUSPEND_REQUESTED); ++ /* kick the slot handler thread to initiate suspend */ ++ request_poll(state, NULL, 0); ++ break; ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); ++ return status; ++} ++ ++void ++vchiq_platform_check_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int susp = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state == VC_SUSPEND_REQUESTED && ++ arm_state->vc_resume_state == VC_RESUME_RESUMED) { ++ set_suspend_state(arm_state, VC_SUSPEND_IN_PROGRESS); ++ susp = 1; ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (susp) ++ vchiq_platform_suspend(state); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++} ++ ++ ++static void ++output_timeout_error(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ char service_err[50] = ""; ++ int vc_use_count = arm_state->videocore_use_count; ++ int active_services = state->unused_service; ++ int i; ++ ++ if (!arm_state->videocore_use_count) { ++ snprintf(service_err, 50, " Videocore usecount is 0"); ++ goto output_msg; ++ } ++ for (i = 0; i < active_services; i++) { ++ VCHIQ_SERVICE_T *service_ptr = state->services[i]; ++ if (service_ptr && service_ptr->service_use_count && ++ (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) { ++ snprintf(service_err, 50, " %c%c%c%c(%d) service has " ++ "use count %d%s", VCHIQ_FOURCC_AS_4CHARS( ++ service_ptr->base.fourcc), ++ service_ptr->client_id, ++ service_ptr->service_use_count, ++ service_ptr->service_use_count == ++ vc_use_count ? "" : " (+ more)"); ++ break; ++ } ++ } ++ ++output_msg: ++ vchiq_log_error(vchiq_susp_log_level, ++ "timed out waiting for vc suspend (%d).%s", ++ arm_state->autosuspend_override, service_err); ++ ++} ++ ++/* Try to get videocore into suspended state, regardless of autosuspend state. ++** We don't actually force suspend, since videocore may get into a bad state ++** if we force suspend at a bad time. Instead, we wait for autosuspend to ++** determine a good point to suspend. If this doesn't happen within 100ms we ++** report failure. ++** ++** Returns VCHIQ_SUCCESS if videocore suspended successfully, VCHIQ_RETRY if ++** videocore failed to suspend in time or VCHIQ_ERROR if interrupted. ++*/ ++VCHIQ_STATUS_T ++vchiq_arm_force_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ long rc = 0; ++ int repeat = -1; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ ++ status = block_resume(arm_state); ++ if (status != VCHIQ_SUCCESS) ++ goto unlock; ++ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { ++ /* Already suspended - just block resume and exit */ ++ vchiq_log_info(vchiq_susp_log_level, "%s already suspended", ++ __func__); ++ status = VCHIQ_SUCCESS; ++ goto unlock; ++ } else if (arm_state->vc_suspend_state <= VC_SUSPEND_IDLE) { ++ /* initiate suspend immediately in the case that we're waiting ++ * for the timeout */ ++ stop_suspend_timer(arm_state); ++ if (!vchiq_videocore_wanted(state)) { ++ vchiq_log_info(vchiq_susp_log_level, "%s videocore " ++ "idle, initiating suspend", __func__); ++ status = vchiq_arm_vcsuspend(state); ++ } else if (arm_state->autosuspend_override < ++ FORCE_SUSPEND_FAIL_MAX) { ++ vchiq_log_info(vchiq_susp_log_level, "%s letting " ++ "videocore go idle", __func__); ++ status = VCHIQ_SUCCESS; ++ } else { ++ vchiq_log_warning(vchiq_susp_log_level, "%s failed too " ++ "many times - attempting suspend", __func__); ++ status = vchiq_arm_vcsuspend(state); ++ } ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, "%s videocore suspend " ++ "in progress - wait for completion", __func__); ++ status = VCHIQ_SUCCESS; ++ } ++ ++ /* Wait for suspend to happen due to system idle (not forced..) */ ++ if (status != VCHIQ_SUCCESS) ++ goto unblock_resume; ++ ++ do { ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ rc = wait_for_completion_interruptible_timeout( ++ &arm_state->vc_suspend_complete, ++ msecs_to_jiffies(FORCE_SUSPEND_TIMEOUT_MS)); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (rc < 0) { ++ vchiq_log_warning(vchiq_susp_log_level, "%s " ++ "interrupted waiting for suspend", __func__); ++ status = VCHIQ_ERROR; ++ goto unblock_resume; ++ } else if (rc == 0) { ++ if (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) { ++ /* Repeat timeout once if in progress */ ++ if (repeat < 0) { ++ repeat = 1; ++ continue; ++ } ++ } ++ arm_state->autosuspend_override++; ++ output_timeout_error(state); ++ ++ status = VCHIQ_RETRY; ++ goto unblock_resume; ++ } ++ } while (0 < (repeat--)); ++ ++ /* Check and report state in case we need to abort ARM suspend */ ++ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED) { ++ status = VCHIQ_RETRY; ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s videocore suspend failed (state %s)", __func__, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ /* Reset the state only if it's still in an error state. ++ * Something could have already initiated another suspend. */ ++ if (arm_state->vc_suspend_state < VC_SUSPEND_IDLE) ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ ++ goto unblock_resume; ++ } ++ ++ /* successfully suspended - unlock and exit */ ++ goto unlock; ++ ++unblock_resume: ++ /* all error states need to unblock resume before exit */ ++ unblock_resume(arm_state); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, status); ++ return status; ++} ++ ++void ++vchiq_check_suspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state != VC_SUSPEND_SUSPENDED && ++ arm_state->first_connect && ++ !vchiq_videocore_wanted(state)) { ++ vchiq_arm_vcsuspend(state); ++ } ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++} ++ ++ ++int ++vchiq_arm_allow_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int resume = 0; ++ int ret = -1; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ unblock_resume(arm_state); ++ resume = vchiq_check_resume(state); ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (resume) { ++ if (wait_for_completion_interruptible( ++ &arm_state->vc_resume_complete) < 0) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s interrupted", __func__); ++ /* failed, cannot accurately derive suspend ++ * state, so exit early. */ ++ goto out; ++ } ++ } ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->vc_suspend_state == VC_SUSPEND_SUSPENDED) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: Videocore remains suspended", __func__); ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: Videocore resumed", __func__); ++ ret = 0; ++ } ++ read_unlock_bh(&arm_state->susp_res_lock); ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++/* This function should be called with the write lock held */ ++int ++vchiq_check_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int resume = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (need_resume(state)) { ++ set_resume_state(arm_state, VC_RESUME_REQUESTED); ++ request_poll(state, NULL, 0); ++ resume = 1; ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return resume; ++} ++ ++void ++vchiq_platform_check_resume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int res = 0; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (arm_state->wake_address == 0) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: already awake", __func__); ++ goto unlock; ++ } ++ if (arm_state->vc_resume_state == VC_RESUME_IN_PROGRESS) { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s: already resuming", __func__); ++ goto unlock; ++ } ++ ++ if (arm_state->vc_resume_state == VC_RESUME_REQUESTED) { ++ set_resume_state(arm_state, VC_RESUME_IN_PROGRESS); ++ res = 1; ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s: not resuming (resume state %s)", __func__, ++ resume_state_names[arm_state->vc_resume_state + ++ VC_RESUME_NUM_OFFSET]); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (res) ++ vchiq_platform_resume(state); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit", __func__); ++ return; ++ ++} ++ ++ ++ ++VCHIQ_STATUS_T ++vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ enum USE_TYPE_E use_type) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[16]; ++ int *entity_uc; ++ int local_uc, local_entity_uc; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (use_type == USE_TYPE_VCHIQ) { ++ sprintf(entity, "VCHIQ: "); ++ entity_uc = &arm_state->peer_use_count; ++ } else if (service) { ++ sprintf(entity, "%c%c%c%c:%03d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id); ++ entity_uc = &service->service_use_count; ++ } else { ++ vchiq_log_error(vchiq_susp_log_level, "%s null service " ++ "ptr", __func__); ++ ret = VCHIQ_ERROR; ++ goto out; ++ } ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ while (arm_state->resume_blocked) { ++ /* If we call 'use' while force suspend is waiting for suspend, ++ * then we're about to block the thread which the force is ++ * waiting to complete, so we're bound to just time out. In this ++ * case, set the suspend state such that the wait will be ++ * canceled, so we can complete as quickly as possible. */ ++ if (arm_state->resume_blocked && arm_state->vc_suspend_state == ++ VC_SUSPEND_IDLE) { ++ set_suspend_state(arm_state, VC_SUSPEND_FORCE_CANCELED); ++ break; ++ } ++ /* If suspend is already in progress then we need to block */ ++ if (!try_wait_for_completion(&arm_state->resume_blocker)) { ++ /* Indicate that there are threads waiting on the resume ++ * blocker. These need to be allowed to complete before ++ * a _second_ call to force suspend can complete, ++ * otherwise low priority threads might never actually ++ * continue */ ++ arm_state->blocked_count++; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " ++ "blocked - waiting...", __func__, entity); ++ if (wait_for_completion_killable( ++ &arm_state->resume_blocker) != 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s %s " ++ "wait for resume blocker interrupted", ++ __func__, entity); ++ ret = VCHIQ_ERROR; ++ write_lock_bh(&arm_state->susp_res_lock); ++ arm_state->blocked_count--; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resume " ++ "unblocked", __func__, entity); ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (--arm_state->blocked_count == 0) ++ complete_all(&arm_state->blocked_blocker); ++ } ++ } ++ ++ stop_suspend_timer(arm_state); ++ ++ local_uc = ++arm_state->videocore_use_count; ++ local_entity_uc = ++(*entity_uc); ++ ++ /* If there's a pending request which hasn't yet been serviced then ++ * just clear it. If we're past VC_SUSPEND_REQUESTED state then ++ * vc_resume_complete will block until we either resume or fail to ++ * suspend */ ++ if (arm_state->vc_suspend_state <= VC_SUSPEND_REQUESTED) ++ set_suspend_state(arm_state, VC_SUSPEND_IDLE); ++ ++ if ((use_type != USE_TYPE_SERVICE_NO_RESUME) && need_resume(state)) { ++ set_resume_state(arm_state, VC_RESUME_REQUESTED); ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, local_entity_uc, local_uc); ++ request_poll(state, NULL, 0); ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, *entity_uc, local_uc); ++ ++ ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++ /* Completion is in a done state when we're not suspended, so this won't ++ * block for the non-suspended case. */ ++ if (!try_wait_for_completion(&arm_state->vc_resume_complete)) { ++ vchiq_log_info(vchiq_susp_log_level, "%s %s wait for resume", ++ __func__, entity); ++ if (wait_for_completion_killable( ++ &arm_state->vc_resume_complete) != 0) { ++ vchiq_log_error(vchiq_susp_log_level, "%s %s wait for " ++ "resume interrupted", __func__, entity); ++ ret = VCHIQ_ERROR; ++ goto out; ++ } ++ vchiq_log_info(vchiq_susp_log_level, "%s %s resumed", __func__, ++ entity); ++ } ++ ++ if (ret == VCHIQ_SUCCESS) { ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0); ++ while (ack_cnt && (status == VCHIQ_SUCCESS)) { ++ /* Send the use notify to videocore */ ++ status = vchiq_send_remote_use_active(state); ++ if (status == VCHIQ_SUCCESS) ++ ack_cnt--; ++ else ++ atomic_add(ack_cnt, ++ &arm_state->ka_use_ack_count); ++ } ++ } ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[16]; ++ int *entity_uc; ++ int local_uc, local_entity_uc; ++ ++ if (!arm_state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ if (service) { ++ sprintf(entity, "%c%c%c%c:%03d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id); ++ entity_uc = &service->service_use_count; ++ } else { ++ sprintf(entity, "PEER: "); ++ entity_uc = &arm_state->peer_use_count; ++ } ++ ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (!arm_state->videocore_use_count || !(*entity_uc)) { ++ /* Don't use BUG_ON - don't allow user thread to crash kernel */ ++ WARN_ON(!arm_state->videocore_use_count); ++ WARN_ON(!(*entity_uc)); ++ ret = VCHIQ_ERROR; ++ goto unlock; ++ } ++ local_uc = --arm_state->videocore_use_count; ++ local_entity_uc = --(*entity_uc); ++ ++ if (!vchiq_videocore_wanted(state)) { ++ if (vchiq_platform_use_suspend_timer() && ++ !arm_state->resume_blocked) { ++ /* Only use the timer if we're not trying to force ++ * suspend (=> resume_blocked) */ ++ start_suspend_timer(arm_state); ++ } else { ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s %s count %d, state count %d - suspending", ++ __func__, entity, *entity_uc, ++ arm_state->videocore_use_count); ++ vchiq_arm_vcsuspend(state); ++ } ++ } else ++ vchiq_log_trace(vchiq_susp_log_level, ++ "%s %s count %d, state count %d", ++ __func__, entity, *entity_uc, ++ arm_state->videocore_use_count); ++ ++unlock: ++ write_unlock_bh(&arm_state->susp_res_lock); ++ ++out: ++ vchiq_log_trace(vchiq_susp_log_level, "%s exit %d", __func__, ret); ++ return ret; ++} ++ ++void ++vchiq_on_remote_use(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ atomic_inc(&arm_state->ka_use_count); ++ complete(&arm_state->ka_evt); ++} ++ ++void ++vchiq_on_remote_release(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ atomic_inc(&arm_state->ka_release_count); ++ complete(&arm_state->ka_evt); ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ return vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ return vchiq_release_internal(service->state, service); ++} ++ ++VCHIQ_DEBUGFS_NODE_T * ++vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance) ++{ ++ return &instance->debugfs_node; ++} ++ ++int ++vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int use_count = 0, i; ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ use_count += service->service_use_count; ++ unlock_service(service); ++ } ++ return use_count; ++} ++ ++int ++vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->pid; ++} ++ ++int ++vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->trace; ++} ++ ++void ++vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ i = 0; ++ while ((service = next_service_by_instance(instance->state, ++ instance, &i)) != NULL) { ++ service->trace = trace; ++ unlock_service(service); ++ } ++ instance->trace = (trace != 0); ++} ++ ++static void suspend_timer_callback(unsigned long context) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ if (!arm_state) ++ goto out; ++ vchiq_log_info(vchiq_susp_log_level, ++ "%s - suspend timer expired - check suspend", __func__); ++ vchiq_check_suspend(state); ++out: ++ return; ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_use_internal(service->state, service, ++ USE_TYPE_SERVICE_NO_RESUME); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_use_internal(service->state, service, ++ USE_TYPE_SERVICE); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ if (service) { ++ ret = vchiq_release_internal(service->state, service); ++ unlock_service(service); ++ } ++ return ret; ++} ++ ++void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int i, j = 0; ++ /* Only dump 64 services */ ++ static const int local_max_services = 64; ++ /* If there's more than 64 services, only dump ones with ++ * non-zero counts */ ++ int only_nonzero = 0; ++ static const char *nz = "<-- preventing suspend"; ++ ++ enum vc_suspend_status vc_suspend_state; ++ enum vc_resume_status vc_resume_state; ++ int peer_count; ++ int vc_use_count; ++ int active_services; ++ struct service_data_struct { ++ int fourcc; ++ int clientid; ++ int use_count; ++ } service_data[local_max_services]; ++ ++ if (!arm_state) ++ return; ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ vc_suspend_state = arm_state->vc_suspend_state; ++ vc_resume_state = arm_state->vc_resume_state; ++ peer_count = arm_state->peer_use_count; ++ vc_use_count = arm_state->videocore_use_count; ++ active_services = state->unused_service; ++ if (active_services > local_max_services) ++ only_nonzero = 1; ++ ++ for (i = 0; (i < active_services) && (j < local_max_services); i++) { ++ VCHIQ_SERVICE_T *service_ptr = state->services[i]; ++ if (!service_ptr) ++ continue; ++ ++ if (only_nonzero && !service_ptr->service_use_count) ++ continue; ++ ++ if (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE) { ++ service_data[j].fourcc = service_ptr->base.fourcc; ++ service_data[j].clientid = service_ptr->client_id; ++ service_data[j++].use_count = service_ptr-> ++ service_use_count; ++ } ++ } ++ ++ read_unlock_bh(&arm_state->susp_res_lock); ++ ++ vchiq_log_warning(vchiq_susp_log_level, ++ "-- Videcore suspend state: %s --", ++ suspend_state_names[vc_suspend_state + VC_SUSPEND_NUM_OFFSET]); ++ vchiq_log_warning(vchiq_susp_log_level, ++ "-- Videcore resume state: %s --", ++ resume_state_names[vc_resume_state + VC_RESUME_NUM_OFFSET]); ++ ++ if (only_nonzero) ++ vchiq_log_warning(vchiq_susp_log_level, "Too many active " ++ "services (%d). Only dumping up to first %d services " ++ "with non-zero use-count", active_services, ++ local_max_services); ++ ++ for (i = 0; i < j; i++) { ++ vchiq_log_warning(vchiq_susp_log_level, ++ "----- %c%c%c%c:%d service count %d %s", ++ VCHIQ_FOURCC_AS_4CHARS(service_data[i].fourcc), ++ service_data[i].clientid, ++ service_data[i].use_count, ++ service_data[i].use_count ? nz : ""); ++ } ++ vchiq_log_warning(vchiq_susp_log_level, ++ "----- VCHIQ use count count %d", peer_count); ++ vchiq_log_warning(vchiq_susp_log_level, ++ "--- Overall vchiq instance use count %d", vc_use_count); ++ ++ vchiq_dump_platform_use_state(state); ++} ++ ++VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state; ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ ++ if (!service || !service->state) ++ goto out; ++ ++ vchiq_log_trace(vchiq_susp_log_level, "%s", __func__); ++ ++ arm_state = vchiq_platform_get_arm_state(service->state); ++ ++ read_lock_bh(&arm_state->susp_res_lock); ++ if (service->service_use_count) ++ ret = VCHIQ_SUCCESS; ++ read_unlock_bh(&arm_state->susp_res_lock); ++ ++ if (ret == VCHIQ_ERROR) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "%s ERROR - %c%c%c%c:%d service count %d, " ++ "state count %d, videocore suspend state %s", __func__, ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->client_id, service->service_use_count, ++ arm_state->videocore_use_count, ++ suspend_state_names[arm_state->vc_suspend_state + ++ VC_SUSPEND_NUM_OFFSET]); ++ vchiq_dump_service_use_state(service->state); ++ } ++out: ++ return ret; ++} ++ ++/* stub functions */ ++void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) ++{ ++ (void)state; ++} ++ ++void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, ++ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id, ++ get_conn_state_name(oldstate), get_conn_state_name(newstate)); ++ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) { ++ write_lock_bh(&arm_state->susp_res_lock); ++ if (!arm_state->first_connect) { ++ char threadname[10]; ++ arm_state->first_connect = 1; ++ write_unlock_bh(&arm_state->susp_res_lock); ++ snprintf(threadname, sizeof(threadname), "VCHIQka-%d", ++ state->id); ++ arm_state->ka_thread = kthread_create( ++ &vchiq_keepalive_thread_func, ++ (void *)state, ++ threadname); ++ if (arm_state->ka_thread == NULL) { ++ vchiq_log_error(vchiq_susp_log_level, ++ "vchiq: FATAL: couldn't create thread %s", ++ threadname); ++ } else { ++ wake_up_process(arm_state->ka_thread); ++ } ++ } else ++ write_unlock_bh(&arm_state->susp_res_lock); ++ } ++} ++ ++ ++/**************************************************************************** ++* ++* vchiq_init - called when the module is loaded. ++* ++***************************************************************************/ ++ ++static int __init ++vchiq_init(void) ++{ ++ int err; ++ void *ptr_err; ++ ++ /* create debugfs entries */ ++ err = vchiq_debugfs_init(); ++ if (err != 0) ++ goto failed_debugfs_init; ++ ++ err = alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1, DEVICE_NAME); ++ if (err != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to allocate device number"); ++ goto failed_alloc_chrdev; ++ } ++ cdev_init(&vchiq_cdev, &vchiq_fops); ++ vchiq_cdev.owner = THIS_MODULE; ++ err = cdev_add(&vchiq_cdev, vchiq_devid, 1); ++ if (err != 0) { ++ vchiq_log_error(vchiq_arm_log_level, ++ "Unable to register device"); ++ goto failed_cdev_add; ++ } ++ ++ /* create sysfs entries */ ++ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); ++ ptr_err = vchiq_class; ++ if (IS_ERR(ptr_err)) ++ goto failed_class_create; ++ ++ vchiq_dev = device_create(vchiq_class, NULL, ++ vchiq_devid, NULL, "vchiq"); ++ ptr_err = vchiq_dev; ++ if (IS_ERR(ptr_err)) ++ goto failed_device_create; ++ ++ err = vchiq_platform_init(&g_state); ++ if (err != 0) ++ goto failed_platform_init; ++ ++ vchiq_log_info(vchiq_arm_log_level, ++ "vchiq: initialised - version %d (min %d), device %d.%d", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN, ++ MAJOR(vchiq_devid), MINOR(vchiq_devid)); + + return 0; + -+out_free_irq: -+ free_irq(bs->irq, master); -+out_workqueue: -+ destroy_workqueue(bs->workq); -+out_iounmap: -+ iounmap(bs->base); -+out_master_put: -+ spi_master_put(master); -+out_clk_put: -+ clk_put(clk); ++failed_platform_init: ++ device_destroy(vchiq_class, vchiq_devid); ++failed_device_create: ++ class_destroy(vchiq_class); ++failed_class_create: ++ cdev_del(&vchiq_cdev); ++ err = PTR_ERR(ptr_err); ++failed_cdev_add: ++ unregister_chrdev_region(vchiq_devid, 1); ++failed_alloc_chrdev: ++ vchiq_debugfs_deinit(); ++failed_debugfs_init: ++ vchiq_log_warning(vchiq_arm_log_level, "could not load vchiq"); + return err; +} + -+static int bcm2708_spi_remove(struct platform_device *pdev) ++/**************************************************************************** ++* ++* vchiq_exit - called when the module is unloaded. ++* ++***************************************************************************/ ++ ++static void __exit ++vchiq_exit(void) +{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm2708_spi *bs = spi_master_get_devdata(master); -+ -+ /* reset the hardware and block queue progress */ -+ spin_lock_irq(&bs->lock); -+ bs->stopping = true; -+ bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); -+ spin_unlock_irq(&bs->lock); -+ -+ flush_work(&bs->work); -+ -+ clk_disable(bs->clk); -+ clk_put(bs->clk); -+ free_irq(bs->irq, master); -+ iounmap(bs->base); -+ -+ spi_unregister_master(master); -+ -+ return 0; ++ vchiq_platform_exit(&g_state); ++ device_destroy(vchiq_class, vchiq_devid); ++ class_destroy(vchiq_class); ++ cdev_del(&vchiq_cdev); ++ unregister_chrdev_region(vchiq_devid, 1); +} + -+static struct platform_driver bcm2708_spi_driver = { -+ .driver = { -+ .name = DRV_NAME, -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm2708_spi_probe, -+ .remove = bcm2708_spi_remove, -+}; -+ -+ -+static int __init bcm2708_spi_init(void) -+{ -+ return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe); -+} -+module_init(bcm2708_spi_init); -+ -+static void __exit bcm2708_spi_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_spi_driver); -+} -+module_exit(bcm2708_spi_exit); -+ -+ -+//module_platform_driver(bcm2708_spi_driver); -+ -+MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708"); -+MODULE_AUTHOR("Chris Boot "); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:" DRV_NAME); - -From 682719078e0d0e6ac1abe5845faa65e5f0b579bb Mon Sep 17 00:00:00 2001 -From: cbeytas -Date: Mon, 24 Jun 2013 00:05:40 -0400 -Subject: [PATCH 021/172] Perform I2C combined transactions when possible - -Perform I2C combined transactions whenever possible, within the -restrictions of the Broadcomm Serial Controller. - -Disable DONE interrupt during TA poll - -Prevent interrupt from being triggered if poll is missed and transfer -starts and finishes. - -i2c: Make combined transactions optional and disabled by default ---- - drivers/i2c/busses/i2c-bcm2708.c | 31 ++++++++++++++++++++++++++++++- - 1 file changed, 30 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -index 09203c0..7d385a3 100644 ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -74,6 +74,9 @@ static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; - module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - MODULE_PARM_DESC(baudrate, "The I2C baudrate"); - -+static bool combined = false; -+module_param(combined, bool, 0644); -+MODULE_PARM_DESC(combined, "Use combined transactions"); - - struct bcm2708_i2c { - struct i2c_adapter adapter; -@@ -150,7 +153,7 @@ static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) - static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) - { - unsigned long bus_hz; -- u32 cdiv; -+ u32 cdiv, s; - u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; - - bus_hz = clk_get_rate(bi->clk); -@@ -166,6 +169,32 @@ static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) - bcm2708_wr(bi, BSC_DIV, cdiv); - bcm2708_wr(bi, BSC_A, bi->msg->addr); - bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ if (combined) -+ { -+ /* Do the next two messages meet combined transaction criteria? -+ - Current message is a write, next message is a read -+ - Both messages to same slave address -+ - Write message can fit inside FIFO (16 bytes or less) */ -+ if ( (bi->nmsgs > 1) && -+ !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && -+ (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { -+ /* Fill FIFO with entire write message (16 byte FIFO) */ -+ while (bi->pos < bi->msg->len) -+ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); -+ /* Start write transfer (no interrupts, don't clear FIFO) */ -+ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); -+ /* poll for transfer start bit (should only take 1-20 polls) */ -+ do { -+ s = bcm2708_rd(bi, BSC_S); -+ } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE))); -+ /* Send next read message before the write transfer finishes. */ -+ bi->nmsgs--; -+ bi->msg++; -+ bi->pos = 0; -+ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); -+ c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; -+ } -+ } - bcm2708_wr(bi, BSC_C, c); - } - - -From db6f4cb59c89e54db302266e6c827137f3da5d96 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 8 May 2013 11:46:50 +0100 -Subject: [PATCH 022/172] enabling the realtime clock 1-wire chip DS1307 and - 1-wire on GPIO4 (as a module) - -1-wire: Add support for configuring pin for w1-gpio kernel module -See: https://github.com/raspberrypi/linux/pull/457 - -Add bitbanging pullups, use them for w1-gpio - -Allows parasite power to work, uses module option pullup=1 - -bcm2708: Ensure 1-wire pullup is disabled by default, and expose as module parameter - -Signed-off-by: Alex J Lennon - -w1-gpio: Add gpiopin module parameter and correctly free up gpio pull-up pin, if set - -Signed-off-by: Alex J Lennon ---- - arch/arm/mach-bcm2708/bcm2708.c | 29 ++++++++++++++++++++++ - drivers/w1/masters/w1-gpio.c | 55 +++++++++++++++++++++++++++++++++++++---- - drivers/w1/w1.h | 6 +++++ - drivers/w1/w1_int.c | 14 +++++++++++ - drivers/w1/w1_io.c | 18 +++++++++++--- - 5 files changed, 114 insertions(+), 8 deletions(-) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 82f56fb..7dd89a7f 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -32,6 +32,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -76,12 +77,19 @@ - */ - #define DMA_MASK_BITS_COMMON 32 - -+// use GPIO 4 for the one-wire GPIO pin, if enabled -+#define W1_GPIO 4 -+// ensure one-wire GPIO pullup is disabled by default -+#define W1_PULLUP -1 -+ - /* command line parameters */ - static unsigned boardrev, serial; - static unsigned uart_clock; - static unsigned disk_led_gpio = 16; - static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; -+static unsigned w1_gpio_pin = W1_GPIO; -+static unsigned w1_gpio_pullup = W1_PULLUP; - - static void __init bcm2708_init_led(void); - -@@ -258,6 +266,20 @@ static struct platform_device bcm2708_dmaman_device = { - .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), - }; - -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+static struct w1_gpio_platform_data w1_gpio_pdata = { -+ .pin = W1_GPIO, -+ .ext_pullup_enable_pin = W1_PULLUP, -+ .is_open_drain = 0, -+}; -+ -+static struct platform_device w1_device = { -+ .name = "w1-gpio", -+ .id = -1, -+ .dev.platform_data = &w1_gpio_pdata, -+}; -+#endif -+ - static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_fb_device = { -@@ -652,6 +674,11 @@ void __init bcm2708_init(void) - #ifdef CONFIG_BCM2708_GPIO - bcm_register_device(&bcm2708_gpio_device); - #endif -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+ w1_gpio_pdata.pin = w1_gpio_pin; -+ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -+ platform_device_register(&w1_device); -+#endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); - bcm_register_device(&bcm2708_usb_device); -@@ -853,3 +880,5 @@ module_param(uart_clock, uint, 0644); - module_param(disk_led_gpio, uint, 0644); - module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); -+module_param(w1_gpio_pin, uint, 0644); -+module_param(w1_gpio_pullup, uint, 0644); -diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c -index b99a932..3ad15cc 100644 ---- a/drivers/w1/masters/w1-gpio.c -+++ b/drivers/w1/masters/w1-gpio.c -@@ -23,6 +23,15 @@ - #include "../w1.h" - #include "../w1_int.h" - -+static int w1_gpio_pullup = -1; -+static int w1_gpio_pullup_orig = -1; -+module_param_named(pullup, w1_gpio_pullup, int, 0); -+MODULE_PARM_DESC(pullup, "GPIO pin pullup number"); -+static int w1_gpio_pin = -1; -+static int w1_gpio_pin_orig = -1; -+module_param_named(gpiopin, w1_gpio_pin, int, 0); -+MODULE_PARM_DESC(gpiopin, "GPIO pin number"); -+ - static u8 w1_gpio_set_pullup(void *data, int delay) - { - struct w1_gpio_platform_data *pdata = data; -@@ -67,6 +76,16 @@ static u8 w1_gpio_read_bit(void *data) - return gpio_get_value(pdata->pin) ? 1 : 0; - } - -+static void w1_gpio_bitbang_pullup(void *data, u8 on) -+{ -+ struct w1_gpio_platform_data *pdata = data; -+ -+ if (on) -+ gpio_direction_output(pdata->pin, 1); -+ else -+ gpio_direction_input(pdata->pin); -+} -+ - #if defined(CONFIG_OF) - static struct of_device_id w1_gpio_dt_ids[] = { - { .compatible = "w1-gpio" }, -@@ -113,13 +132,15 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) - static int w1_gpio_probe(struct platform_device *pdev) - { - struct w1_bus_master *master; -- struct w1_gpio_platform_data *pdata; -+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; - int err; - -- if (of_have_populated_dt()) { -- err = w1_gpio_probe_dt(pdev); -- if (err < 0) -- return err; -+ if(pdata == NULL) { -+ if (of_have_populated_dt()) { -+ err = w1_gpio_probe_dt(pdev); -+ if (err < 0) -+ return err; -+ } - } - - pdata = dev_get_platdata(&pdev->dev); -@@ -136,6 +157,19 @@ static int w1_gpio_probe(struct platform_device *pdev) - return -ENOMEM; - } - -+ w1_gpio_pin_orig = pdata->pin; -+ w1_gpio_pullup_orig = pdata->ext_pullup_enable_pin; -+ -+ if(gpio_is_valid(w1_gpio_pin)) { -+ pdata->pin = w1_gpio_pin; -+ pdata->ext_pullup_enable_pin = -1; -+ } -+ if(gpio_is_valid(w1_gpio_pullup)) { -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup; -+ } -+ -+ dev_info(&pdev->dev, "gpio pin %d, gpio pullup pin %d\n", pdata->pin, pdata->ext_pullup_enable_pin); -+ - err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); - if (err) { - dev_err(&pdev->dev, "gpio_request (pin) failed\n"); -@@ -165,6 +199,14 @@ static int w1_gpio_probe(struct platform_device *pdev) - master->set_pullup = w1_gpio_set_pullup; - } - -+ if (gpio_is_valid(w1_gpio_pullup)) { -+ if (pdata->is_open_drain) -+ printk(KERN_ERR "w1-gpio 'pullup' option " -+ "doesn't work with open drain GPIO\n"); -+ else -+ master->bitbang_pullup = w1_gpio_bitbang_pullup; -+ } -+ - err = w1_add_master_device(master); - if (err) { - dev_err(&pdev->dev, "w1_add_master device failed\n"); -@@ -195,6 +237,9 @@ static int w1_gpio_remove(struct platform_device *pdev) - - w1_remove_master_device(master); - -+ pdata->pin = w1_gpio_pin_orig; -+ pdata->ext_pullup_enable_pin = w1_gpio_pullup_orig; -+ - return 0; - } - -diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h -index 56a49ba..881d728 100644 ---- a/drivers/w1/w1.h -+++ b/drivers/w1/w1.h -@@ -171,6 +171,12 @@ struct w1_bus_master - - u8 (*set_pullup)(void *, int); - -+ /** -+ * Turns the pullup on/off in bitbanging mode, takes an on/off argument. -+ * @return -1=Error, 0=completed -+ */ -+ void (*bitbang_pullup) (void *, u8); -+ - void (*search)(void *, struct w1_master *, - u8, w1_slave_found_callback); - }; -diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c -index 47249a3..a4b4a8d 100644 ---- a/drivers/w1/w1_int.c -+++ b/drivers/w1/w1_int.c -@@ -123,6 +123,20 @@ int w1_add_master_device(struct w1_bus_master *master) - return(-EINVAL); - } - -+ /* bitbanging hardware uses bitbang_pullup, other hardware uses set_pullup -+ * and takes care of timing itself */ -+ if (!master->write_byte && !master->touch_bit && master->set_pullup) { -+ printk(KERN_ERR "w1_add_master_device: set_pullup requires " -+ "write_byte or touch_bit, disabling\n"); -+ master->set_pullup = NULL; -+ } -+ -+ if (master->set_pullup && master->bitbang_pullup) { -+ printk(KERN_ERR "w1_add_master_device: set_pullup should not " -+ "be set when bitbang_pullup is used, disabling\n"); -+ master->set_pullup = NULL; -+ } -+ - /* Lock until the device is added (or not) to w1_masters. */ - mutex_lock(&w1_mlock); - /* Search for the first available id (starting at 1). */ -diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c -index 2820924..fd0550f 100644 ---- a/drivers/w1/w1_io.c -+++ b/drivers/w1/w1_io.c -@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_master *dev) - static void w1_post_write(struct w1_master *dev) - { - if (dev->pullup_duration) { -- if (dev->enable_pullup && dev->bus_master->set_pullup) -- dev->bus_master->set_pullup(dev->bus_master->data, 0); -- else -+ if (dev->enable_pullup) { -+ if (dev->bus_master->set_pullup) { -+ dev->bus_master->set_pullup(dev-> -+ bus_master->data, -+ 0); -+ } else if (dev->bus_master->bitbang_pullup) { -+ dev->bus_master-> -+ bitbang_pullup(dev->bus_master->data, 1); -+ msleep(dev->pullup_duration); -+ dev->bus_master-> -+ bitbang_pullup(dev->bus_master->data, 0); -+ } -+ } else { - msleep(dev->pullup_duration); -+ } -+ - dev->pullup_duration = 0; - } - } - -From 8160010b3d9ff17f29c3e332e9f3c37c527e26c0 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Fri, 12 Apr 2013 23:58:47 +0100 -Subject: [PATCH 024/172] config: add missing options from 3.6.y kernel - ---- - arch/arm/configs/bcmrpi_defconfig | 692 ++++++++++++++++++++++++++++++++++++-- - 1 file changed, 667 insertions(+), 25 deletions(-) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 6d2eae1..6b39872 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -1,3 +1,5 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0 - # CONFIG_LOCALVERSION_AUTO is not set - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y -@@ -7,6 +9,10 @@ CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y - CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y - CONFIG_IKCONFIG=y - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y -@@ -16,28 +22,41 @@ CONFIG_RESOURCE_COUNTERS=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y -+CONFIG_BLK_DEV_INITRD=y - CONFIG_EMBEDDED=y - # CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y - CONFIG_PROFILING=y - CONFIG_OPROFILE=m - CONFIG_KPROBES=y -+CONFIG_JUMP_LABEL=y - CONFIG_MODULES=y - CONFIG_MODULE_UNLOAD=y - CONFIG_MODVERSIONS=y - CONFIG_MODULE_SRCVERSION_ALL=y --# CONFIG_BLK_DEV_BSG is not set - CONFIG_BLK_DEV_THROTTLING=y - CONFIG_PARTITION_ADVANCED=y - CONFIG_MAC_PARTITION=y - CONFIG_CFQ_GROUP_IOSCHED=y - CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y - CONFIG_AEABI=y -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y - CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" - CONFIG_KEXEC=y -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_STAT=m -+CONFIG_CPU_FREQ_STAT_DETAILS=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y - CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_BINFMT_MISC=m -@@ -48,19 +67,278 @@ CONFIG_XFRM_USER=y - CONFIG_NET_KEY=m - CONFIG_INET=y - CONFIG_IP_MULTICAST=y -+CONFIG_IP_ADVANCED_ROUTER=y -+CONFIG_IP_MULTIPLE_TABLES=y -+CONFIG_IP_ROUTE_MULTIPATH=y -+CONFIG_IP_ROUTE_VERBOSE=y - CONFIG_IP_PNP=y - CONFIG_IP_PNP_DHCP=y - CONFIG_IP_PNP_RARP=y -+CONFIG_NET_IPIP=m -+CONFIG_NET_IPGRE_DEMUX=m -+CONFIG_NET_IPGRE=m -+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_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set -+CONFIG_INET_AH=m -+CONFIG_INET_ESP=m -+CONFIG_INET_IPCOMP=m -+CONFIG_INET_XFRM_MODE_TRANSPORT=m -+CONFIG_INET_XFRM_MODE_TUNNEL=m -+CONFIG_INET_XFRM_MODE_BEET=m -+CONFIG_INET_LRO=m -+CONFIG_INET_DIAG=m -+CONFIG_INET6_AH=m -+CONFIG_INET6_ESP=m -+CONFIG_INET6_IPCOMP=m -+CONFIG_IPV6_TUNNEL=m -+CONFIG_IPV6_MULTIPLE_TABLES=y -+CONFIG_IPV6_MROUTE=y -+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y -+CONFIG_IPV6_PIMSM_V2=y -+CONFIG_NETFILTER=y -+CONFIG_NF_CONNTRACK=m -+CONFIG_NF_CONNTRACK_ZONES=y -+CONFIG_NF_CONNTRACK_EVENTS=y -+CONFIG_NF_CONNTRACK_TIMESTAMP=y -+CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m -+CONFIG_NETFILTER_XT_TARGET_AUDIT=m -+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m -+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -+CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m -+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -+CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 -+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_ESP=m -+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m -+CONFIG_NETFILTER_XT_MATCH_HELPER=m -+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m -+CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y -+CONFIG_IP_VS_PROTO_UDP=y -+CONFIG_IP_VS_PROTO_ESP=y -+CONFIG_IP_VS_PROTO_AH=y -+CONFIG_IP_VS_PROTO_SCTP=y -+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 -+CONFIG_IP_VS_FTP=m -+CONFIG_IP_VS_PE_SIP=m -+CONFIG_NF_CONNTRACK_IPV4=m -+CONFIG_IP_NF_IPTABLES=m -+CONFIG_IP_NF_MATCH_AH=m -+CONFIG_IP_NF_MATCH_ECN=m -+CONFIG_IP_NF_MATCH_TTL=m -+CONFIG_IP_NF_FILTER=m -+CONFIG_IP_NF_TARGET_REJECT=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 -+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_RT=m -+CONFIG_IP6_NF_TARGET_HL=m -+CONFIG_IP6_NF_FILTER=m -+CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y -+CONFIG_ATM=m -+CONFIG_L2TP=m -+CONFIG_L2TP_V3=y -+CONFIG_L2TP_IP=m -+CONFIG_L2TP_ETH=m -+CONFIG_BRIDGE=m -+CONFIG_VLAN_8021Q=m -+CONFIG_VLAN_8021Q_GVRP=y -+CONFIG_ATALK=m -+CONFIG_NET_SCHED=y -+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 -+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_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_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_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_BATMAN_ADV=m -+CONFIG_OPENVSWITCH=m - CONFIG_NET_PKTGEN=m -+CONFIG_HAMRADIO=y -+CONFIG_AX25=m -+CONFIG_NETROM=m -+CONFIG_ROSE=m -+CONFIG_MKISS=m -+CONFIG_6PACK=m -+CONFIG_BPQETHER=m -+CONFIG_BAYCOM_SER_FDX=m -+CONFIG_BAYCOM_SER_HDX=m -+CONFIG_YAM=m - CONFIG_IRDA=m - CONFIG_IRLAN=m -+CONFIG_IRNET=m - CONFIG_IRCOMM=m - CONFIG_IRDA_ULTRA=y - CONFIG_IRDA_CACHE_LAST_LSAP=y -@@ -87,45 +365,88 @@ CONFIG_BT_HCIVHCI=m - CONFIG_BT_MRVL=m - CONFIG_BT_MRVL_SDIO=m - CONFIG_BT_ATH3K=m --CONFIG_CFG80211=m -+CONFIG_BT_WILINK=m -+CONFIG_CFG80211_WEXT=y - CONFIG_MAC80211=m - CONFIG_MAC80211_MESH=y - CONFIG_WIMAX=m -+CONFIG_RFKILL=m -+CONFIG_RFKILL_INPUT=y - CONFIG_NET_9P=m - CONFIG_NFC=m - CONFIG_NFC_PN533=m --CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=5 - CONFIG_BLK_DEV_LOOP=y - CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set --CONFIG_BLK_DEV_SD=m -+CONFIG_BLK_DEV_SD=y -+CONFIG_CHR_DEV_ST=m -+CONFIG_CHR_DEV_OSST=m - CONFIG_BLK_DEV_SR=m --# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_CHR_DEV_SG=m -+CONFIG_SCSI_ISCSI_ATTRS=y -+CONFIG_ISCSI_TCP=m -+CONFIG_ISCSI_BOOT_SYSFS=m - CONFIG_MD=y -+CONFIG_MD_LINEAR=m -+CONFIG_MD_RAID0=m -+CONFIG_BLK_DEV_DM=m -+CONFIG_DM_CRYPT=m -+CONFIG_DM_SNAPSHOT=m -+CONFIG_DM_MIRROR=m -+CONFIG_DM_LOG_USERSPACE=m -+CONFIG_DM_RAID=m -+CONFIG_DM_ZERO=m -+CONFIG_DM_DELAY=m - CONFIG_NETDEVICES=y -+CONFIG_BONDING=m -+CONFIG_DUMMY=m -+CONFIG_IFB=m -+CONFIG_MACVLAN=m - CONFIG_NETCONSOLE=m - CONFIG_TUN=m -+CONFIG_VETH=m -+CONFIG_ENC28J60=m - CONFIG_MDIO_BITBANG=m - CONFIG_PPP=m - CONFIG_PPP_BSDCOMP=m - CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_FILTER=y -+CONFIG_PPP_MPPE=m -+CONFIG_PPP_MULTILINK=y -+CONFIG_PPPOATM=m -+CONFIG_PPPOE=m -+CONFIG_PPPOL2TP=m - CONFIG_PPP_ASYNC=m - CONFIG_PPP_SYNC_TTY=m - CONFIG_SLIP=m - CONFIG_SLIP_COMPRESSED=y -+CONFIG_SLIP_SMART=y - CONFIG_USB_CATC=m - CONFIG_USB_KAWETH=m - CONFIG_USB_PEGASUS=m - CONFIG_USB_RTL8150=m -+CONFIG_USB_RTL8152=m - CONFIG_USB_USBNET=y - 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=y - CONFIG_USB_NET_GL620A=m -@@ -135,10 +456,13 @@ CONFIG_USB_NET_MCS7830=m - CONFIG_USB_NET_CDC_SUBSET=m - CONFIG_USB_ALI_M5632=y - CONFIG_USB_AN2720=y -+CONFIG_USB_EPSON2888=y - CONFIG_USB_KC2190=y --# CONFIG_USB_NET_ZAURUS is not set -+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 -@@ -150,8 +474,18 @@ CONFIG_USB_ZD1201=m - CONFIG_USB_NET_RNDIS_WLAN=m - CONFIG_RTL8187=m - CONFIG_MAC80211_HWSIM=m -+CONFIG_ATH_CARDS=m -+CONFIG_ATH9K=m -+CONFIG_ATH9K_HTC=m -+CONFIG_CARL9170=m -+CONFIG_ATH6KL=m -+CONFIG_ATH6KL_USB=m -+CONFIG_AR5523=m - CONFIG_B43=m -+# CONFIG_B43_PHY_N is not set - CONFIG_B43LEGACY=m -+CONFIG_BRCMFMAC=m -+CONFIG_BRCMFMAC_USB=y - CONFIG_HOSTAP=m - CONFIG_LIBERTAS=m - CONFIG_LIBERTAS_USB=m -@@ -162,7 +496,10 @@ CONFIG_RT2X00=m - CONFIG_RT2500USB=m - CONFIG_RT73USB=m - CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT3573=y - CONFIG_RT2800USB_RT53XX=y -+CONFIG_RT2800USB_RT55XX=y -+CONFIG_RT2800USB_UNKNOWN=y - CONFIG_RTL8192CU=m - CONFIG_ZD1211RW=m - CONFIG_MWIFIEX=m -@@ -174,6 +511,13 @@ CONFIG_INPUT_JOYDEV=m - CONFIG_INPUT_EVDEV=m - # CONFIG_INPUT_KEYBOARD is not set - # CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_JOYSTICK=y -+CONFIG_JOYSTICK_IFORCE=m -+CONFIG_JOYSTICK_IFORCE_USB=y -+CONFIG_JOYSTICK_XPAD=m -+CONFIG_JOYSTICK_XPAD_FF=y -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -182,6 +526,7 @@ CONFIG_INPUT_POWERMATE=m - CONFIG_INPUT_YEALINK=m - CONFIG_INPUT_CM109=m - CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m - CONFIG_INPUT_ADXL34X=m - CONFIG_INPUT_CMA3000=m - CONFIG_SERIO=m -@@ -189,22 +534,223 @@ CONFIG_SERIO_RAW=m - CONFIG_GAMEPORT=m - CONFIG_GAMEPORT_NS558=m - CONFIG_GAMEPORT_L4=m -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y - # CONFIG_LEGACY_PTYS is not set - # CONFIG_DEVKMEM is not set - CONFIG_SERIAL_AMBA_PL011=y - CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --# CONFIG_HW_RANDOM is not set -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=m - CONFIG_RAW_DRIVER=y -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VC_SM=y - CONFIG_I2C=y - CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2708=m --CONFIG_SPI_SPIDEV=m -+CONFIG_SPI_SPIDEV=y -+CONFIG_PPS=m -+CONFIG_PPS_CLIENT_LDISC=m -+CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_ARIZONA=m -+CONFIG_W1=m -+CONFIG_W1_MASTER_DS2490=m -+CONFIG_W1_MASTER_DS2482=m -+CONFIG_W1_MASTER_DS1WM=m -+CONFIG_W1_MASTER_GPIO=m -+CONFIG_W1_SLAVE_THERM=m -+CONFIG_W1_SLAVE_SMEM=m -+CONFIG_W1_SLAVE_DS2408=m -+CONFIG_W1_SLAVE_DS2413=m -+CONFIG_W1_SLAVE_DS2406=m -+CONFIG_W1_SLAVE_DS2423=m -+CONFIG_W1_SLAVE_DS2431=m -+CONFIG_W1_SLAVE_DS2433=m -+CONFIG_W1_SLAVE_DS2760=m -+CONFIG_W1_SLAVE_DS2780=m -+CONFIG_W1_SLAVE_DS2781=m -+CONFIG_W1_SLAVE_DS28E04=m -+CONFIG_W1_SLAVE_BQ27000=m -+CONFIG_BATTERY_DS2760=m - # CONFIG_HWMON is not set -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=m -+CONFIG_UCB1400_CORE=m -+CONFIG_MFD_ARIZONA_I2C=m -+CONFIG_MFD_ARIZONA_SPI=m -+CONFIG_MFD_WM5102=y -+CONFIG_MEDIA_SUPPORT=m -+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_LIRC=m -+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_MEDIA_USB_SUPPORT=y -+CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m -+CONFIG_USB_ZR364XX=m -+CONFIG_USB_STKWEBCAM=m -+CONFIG_USB_S2255=m -+CONFIG_VIDEO_USBTV=m -+CONFIG_VIDEO_PVRUSB2=m -+CONFIG_VIDEO_HDPVR=m -+CONFIG_VIDEO_TLG2300=m -+CONFIG_VIDEO_USBVISION=m -+CONFIG_VIDEO_STK1160_COMMON=m -+CONFIG_VIDEO_STK1160_AC97=y -+CONFIG_VIDEO_GO7007=m -+CONFIG_VIDEO_GO7007_USB=m -+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m -+CONFIG_VIDEO_AU0828=m -+CONFIG_VIDEO_AU0828_RC=y -+CONFIG_VIDEO_CX231XX=m -+CONFIG_VIDEO_CX231XX_ALSA=m -+CONFIG_VIDEO_CX231XX_DVB=m -+CONFIG_VIDEO_TM6000=m -+CONFIG_VIDEO_TM6000_ALSA=m -+CONFIG_VIDEO_TM6000_DVB=m -+CONFIG_DVB_USB=m -+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_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_AS102=m -+CONFIG_VIDEO_EM28XX=m -+CONFIG_VIDEO_EM28XX_V4L2=m -+CONFIG_VIDEO_EM28XX_ALSA=m -+CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_RADIO_SI470X=y -+CONFIG_USB_SI470X=m -+CONFIG_I2C_SI470X=m -+CONFIG_RADIO_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_MA901=m -+CONFIG_RADIO_TEA5764=m -+CONFIG_RADIO_SAA7706H=m -+CONFIG_RADIO_TEF6862=m -+CONFIG_RADIO_WL1273=m -+CONFIG_RADIO_WL128X=m -+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -+CONFIG_VIDEO_UDA1342=m -+CONFIG_VIDEO_SONY_BTF_MPX=m -+CONFIG_VIDEO_TVP5150=m -+CONFIG_VIDEO_TW2804=m -+CONFIG_VIDEO_TW9903=m -+CONFIG_VIDEO_TW9906=m -+CONFIG_VIDEO_OV7640=m -+CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+# CONFIG_BACKLIGHT_GENERIC is not set - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set -@@ -227,8 +773,10 @@ CONFIG_SND_BCM2835=m - CONFIG_SND_USB_AUDIO=m - CONFIG_SND_USB_UA101=m - CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_CAIAQ_INPUT=y - CONFIG_SND_USB_6FIRE=m - CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y - CONFIG_HID_A4TECH=m - CONFIG_HID_ACRUX=m - CONFIG_HID_APPLE=m -@@ -239,6 +787,7 @@ CONFIG_HID_CYPRESS=m - CONFIG_HID_DRAGONRISE=m - CONFIG_HID_EMS_FF=m - CONFIG_HID_ELECOM=m -+CONFIG_HID_ELO=m - CONFIG_HID_EZKEY=m - CONFIG_HID_HOLTEK=m - CONFIG_HID_KEYTOUCH=m -@@ -267,9 +816,11 @@ CONFIG_HID_SUNPLUS=m - CONFIG_HID_GREENASIA=m - CONFIG_HID_SMARTJOYPLUS=m - CONFIG_HID_TOPSEED=m -+CONFIG_HID_THINGM=m - CONFIG_HID_THRUSTMASTER=m - CONFIG_HID_WACOM=m - CONFIG_HID_WIIMOTE=m -+CONFIG_HID_XINMO=m - CONFIG_HID_ZEROPLUS=m - CONFIG_HID_ZYDACRON=m - CONFIG_HID_PID=y -@@ -277,6 +828,8 @@ CONFIG_USB_HIDDEV=y - CONFIG_USB=y - CONFIG_USB_ANNOUNCE_NEW_DEVICES=y - CONFIG_USB_MON=m -+CONFIG_USB_DWCOTG=y -+CONFIG_USB_PRINTER=m - CONFIG_USB_STORAGE=y - CONFIG_USB_STORAGE_REALTEK=m - CONFIG_USB_STORAGE_DATAFAB=m -@@ -294,6 +847,9 @@ CONFIG_USB_STORAGE_ENE_UB6250=m - CONFIG_USB_UAS=m - CONFIG_USB_MDC800=m - CONFIG_USB_MICROTEK=m -+CONFIG_USBIP_CORE=m -+CONFIG_USBIP_VHCI_HCD=m -+CONFIG_USBIP_HOST=m - CONFIG_USB_SERIAL=m - CONFIG_USB_SERIAL_GENERIC=y - CONFIG_USB_SERIAL_AIRCABLE=m -@@ -311,6 +867,7 @@ 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 -@@ -319,6 +876,7 @@ CONFIG_USB_SERIAL_KEYSPAN=m - 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_NAVMAN=m -@@ -336,7 +894,10 @@ CONFIG_USB_SERIAL_XIRCOM=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 - CONFIG_USB_EMI62=m - CONFIG_USB_EMI26=m -@@ -357,12 +918,81 @@ CONFIG_USB_IOWARRIOR=m - CONFIG_USB_TEST=m - CONFIG_USB_ISIGHTFW=m - CONFIG_USB_YUREX=m -+CONFIG_USB_ATM=m -+CONFIG_USB_SPEEDTOUCH=m -+CONFIG_USB_CXACRU=m -+CONFIG_USB_UEAGLEATM=m -+CONFIG_USB_XUSBATM=m - CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SPI=m -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_GPIO=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=y -+CONFIG_LEDS_TRIGGER_GPIO=y -+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y -+CONFIG_LEDS_TRIGGER_TRANSIENT=m -+CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_RTC_CLASS=y -+# CONFIG_RTC_HCTOSYS is not set -+CONFIG_RTC_DRV_DS1307=m -+CONFIG_RTC_DRV_DS1374=m -+CONFIG_RTC_DRV_DS1672=m -+CONFIG_RTC_DRV_DS3232=m -+CONFIG_RTC_DRV_MAX6900=m -+CONFIG_RTC_DRV_RS5C372=m -+CONFIG_RTC_DRV_ISL1208=m -+CONFIG_RTC_DRV_ISL12022=m -+CONFIG_RTC_DRV_ISL12057=m -+CONFIG_RTC_DRV_X1205=m -+CONFIG_RTC_DRV_PCF2127=m -+CONFIG_RTC_DRV_PCF8523=m -+CONFIG_RTC_DRV_PCF8563=m -+CONFIG_RTC_DRV_PCF8583=m -+CONFIG_RTC_DRV_M41T80=m -+CONFIG_RTC_DRV_BQ32K=m -+CONFIG_RTC_DRV_S35390A=m -+CONFIG_RTC_DRV_FM3130=m -+CONFIG_RTC_DRV_RX8581=m -+CONFIG_RTC_DRV_RX8025=m -+CONFIG_RTC_DRV_EM3027=m -+CONFIG_RTC_DRV_RV3029C2=m -+CONFIG_RTC_DRV_M41T93=m -+CONFIG_RTC_DRV_M41T94=m -+CONFIG_RTC_DRV_DS1305=m -+CONFIG_RTC_DRV_DS1390=m -+CONFIG_RTC_DRV_MAX6902=m -+CONFIG_RTC_DRV_R9701=m -+CONFIG_RTC_DRV_RS5C348=m -+CONFIG_RTC_DRV_DS3234=m -+CONFIG_RTC_DRV_PCF2123=m -+CONFIG_RTC_DRV_RX4581=m - CONFIG_UIO=m - CONFIG_UIO_PDRV_GENIRQ=m -+CONFIG_STAGING=y -+CONFIG_PRISM2_USB=m -+CONFIG_R8712U=m -+CONFIG_R8188EU=m -+CONFIG_R8723AU=m -+CONFIG_VT6656=m -+CONFIG_SPEAKUP=m -+CONFIG_SPEAKUP_SYNTH_SOFT=m -+CONFIG_STAGING_MEDIA=y -+CONFIG_LIRC_STAGING=y -+CONFIG_LIRC_IGORPLUGUSB=m -+CONFIG_LIRC_IMON=m -+CONFIG_LIRC_RPI=m -+CONFIG_LIRC_SASEM=m -+CONFIG_LIRC_SERIAL=m - # CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m - CONFIG_EXT4_FS=y - CONFIG_EXT4_FS_POSIX_ACL=y - CONFIG_EXT4_FS_SECURITY=y -@@ -384,6 +1014,8 @@ CONFIG_BTRFS_FS=m - CONFIG_BTRFS_FS_POSIX_ACL=y - CONFIG_NILFS2_FS=m - CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m - CONFIG_AUTOFS4_FS=y - CONFIG_FUSE_FS=m - CONFIG_CUSE=m -@@ -399,18 +1031,27 @@ CONFIG_MSDOS_FS=y - CONFIG_VFAT_FS=y - CONFIG_FAT_DEFAULT_IOCHARSET="ascii" - CONFIG_NTFS_FS=m -+CONFIG_NTFS_RW=y - CONFIG_TMPFS=y - CONFIG_TMPFS_POSIX_ACL=y - CONFIG_CONFIGFS_FS=y -+CONFIG_ECRYPT_FS=m -+CONFIG_HFS_FS=m -+CONFIG_HFSPLUS_FS=m - CONFIG_SQUASHFS=m - CONFIG_SQUASHFS_XATTR=y - CONFIG_SQUASHFS_LZO=y - CONFIG_SQUASHFS_XZ=y -+CONFIG_F2FS_FS=y - CONFIG_NFS_FS=y - CONFIG_NFS_V3_ACL=y - CONFIG_NFS_V4=y -+CONFIG_NFS_SWAP=y - CONFIG_ROOT_NFS=y - CONFIG_NFS_FSCACHE=y -+CONFIG_NFSD=m -+CONFIG_NFSD_V3_ACL=y -+CONFIG_NFSD_V4=y - CONFIG_CIFS=m - CONFIG_CIFS_WEAK_PW_HASH=y - CONFIG_CIFS_XATTR=y -@@ -455,36 +1096,37 @@ CONFIG_NLS_ISO8859_14=m - CONFIG_NLS_ISO8859_15=m - CONFIG_NLS_KOI8_R=m - CONFIG_NLS_KOI8_U=m --CONFIG_NLS_UTF8=m -+CONFIG_DLM=m - CONFIG_PRINTK_TIME=y - CONFIG_BOOT_PRINTK_DELAY=y --CONFIG_DEBUG_INFO=y --CONFIG_DEBUG_STACK_USAGE=y - CONFIG_DEBUG_MEMORY_INIT=y - CONFIG_DETECT_HUNG_TASK=y - CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set - CONFIG_LATENCYTOP=y - CONFIG_IRQSOFF_TRACER=y - CONFIG_SCHED_TRACER=y - CONFIG_STACK_TRACER=y - CONFIG_BLK_DEV_IO_TRACE=y -+# CONFIG_KPROBE_EVENT is not set - CONFIG_FUNCTION_PROFILER=y - CONFIG_KGDB=y - CONFIG_KGDB_KDB=y - CONFIG_KDB_KEYBOARD=y --CONFIG_STRICT_DEVMEM=y --CONFIG_CRYPTO_AUTHENC=m -+CONFIG_CRYPTO_USER=m -+CONFIG_CRYPTO_NULL=m -+CONFIG_CRYPTO_CRYPTD=m - CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_CTS=m -+CONFIG_CRYPTO_XTS=m - CONFIG_CRYPTO_XCBC=m --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_SHA1_ARM=m - CONFIG_CRYPTO_SHA512=m - CONFIG_CRYPTO_TGR192=m - CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_AES_ARM=m - CONFIG_CRYPTO_CAST5=m - CONFIG_CRYPTO_DES=y --CONFIG_CRYPTO_DEFLATE=m - # CONFIG_CRYPTO_ANSI_CPRNG is not set - # CONFIG_CRYPTO_HW is not set - CONFIG_CRC_ITU_T=y - -From 9546603deded7c808fd5a04a691377a0cdf2fa25 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Dec 2013 22:16:19 +0000 -Subject: [PATCH 025/172] config: Enable CONFIG_MEMCG, but leave it disabled - (due to memory cost). Enable with cgroup_enable=memory. - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - kernel/cgroup.c | 23 +++++++++++++++++++++++ - mm/memcontrol.c | 1 + - 3 files changed, 25 insertions(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 6b39872..ef20985 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -19,6 +19,7 @@ CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y - CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y - CONFIG_SCHED_AUTOGROUP=y -diff --git a/kernel/cgroup.c b/kernel/cgroup.c -index 04cfe8a..399cea3 100644 ---- a/kernel/cgroup.c -+++ b/kernel/cgroup.c -@@ -5389,6 +5389,29 @@ static int __init cgroup_disable(char *str) - } - __setup("cgroup_disable=", cgroup_disable); - -+static int __init cgroup_enable(char *str) -+{ -+ struct cgroup_subsys *ss; -+ char *token; -+ int i; -+ -+ while ((token = strsep(&str, ",")) != NULL) { -+ if (!*token) -+ continue; -+ -+ for_each_subsys(ss, i) { -+ if (!strcmp(token, ss->name)) { -+ ss->disabled = 0; -+ printk(KERN_INFO "Enabling %s control group" -+ " subsystem\n", ss->name); -+ break; -+ } -+ } -+ } -+ return 1; -+} -+__setup("cgroup_enable=", cgroup_enable); -+ - static int __init cgroup_set_legacy_files_on_dfl(char *str) - { - printk("cgroup: using legacy files on the default hierarchy\n"); -diff --git a/mm/memcontrol.c b/mm/memcontrol.c -index 2f6893c..8778eba 100644 ---- a/mm/memcontrol.c -+++ b/mm/memcontrol.c -@@ -5398,6 +5398,7 @@ struct cgroup_subsys memory_cgrp_subsys = { - .bind = mem_cgroup_bind, - .legacy_cftypes = mem_cgroup_files, - .early_init = 0, -+ .disabled = 1, - }; - - #ifdef CONFIG_MEMCG_SWAP - -From c73af020d71c020a59390d693ce357e0052cf457 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:46:42 +0100 -Subject: [PATCH 026/172] Add FIQ patch to dwc_otg driver. Enable with - dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks - to Gordon and Costas - -Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005. - -Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh - -Make sure we wait for the reset to finish - -dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel - memory corruption, escalating to OOPS under high USB load. - -dwc_otg: Fix unsafe access of QTD during URB enqueue - -In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the -transaction could complete almost immediately after the qtd was assigned -to a host channel during URB enqueue, which meant the qtd pointer was no -longer valid having been completed and removed. Usually, this resulted in -an OOPS during URB submission. By predetermining whether transactions -need to be queued or not, this unsafe pointer access is avoided. - -This bug was only evident on the Pi model A where a device was attached -that had no periodic endpoints (e.g. USB pendrive or some wlan devices). - -dwc_otg: Fix incorrect URB allocation error handling - -If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS -because for some reason a member of the *unallocated* struct was set to -zero. Error handling changed to fail correctly. - -dwc_otg: fix potential use-after-free case in interrupt handler - -If a transaction had previously aborted, certain interrupts are -enabled to track error counts and reset where necessary. On IN -endpoints the host generates an ACK interrupt near-simultaneously -with completion of transfer. In the case where this transfer had -previously had an error, this results in a use-after-free on -the QTD memory space with a 1-byte length being overwritten to -0x00. - -dwc_otg: add handling of SPLIT transaction data toggle errors - -Previously a data toggle error on packets from a USB1.1 device behind -a TT would result in the Pi locking up as the driver never handled -the associated interrupt. Patch adds basic retry mechanism and -interrupt acknowledgement to cater for either a chance toggle error or -for devices that have a broken initial toggle state (FT8U232/FT232BM). - -dwc_otg: implement tasklet for returning URBs to usbcore hcd layer - -The dwc_otg driver interrupt handler for transfer completion will spend -a very long time with interrupts disabled when a URB is completed - -this is because usb_hcd_giveback_urb is called from within the handler -which for a USB device driver with complicated processing (e.g. webcam) -will take an exorbitant amount of time to complete. This results in -missed completion interrupts for other USB packets which lead to them -being dropped due to microframe overruns. - -This patch splits returning the URB to the usb hcd layer into a -high-priority tasklet. This will have most benefit for isochronous IN -transfers but will also have incidental benefit where multiple periodic -devices are active at once. - -dwc_otg: fix NAK holdoff and allow on split transactions only - -This corrects a bug where if a single active non-periodic endpoint -had at least one transaction in its qh, on frnum == MAX_FRNUM the qh -would get skipped and never get queued again. This would result in -a silent device until error detection (automatic or otherwise) would -either reset the device or flush and requeue the URBs. - -Additionally the NAK holdoff was enabled for all transactions - this -would potentially stall a HS endpoint for 1ms if a previous error state -enabled this interrupt and the next response was a NAK. Fix so that -only split transactions get held off. - -dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler - -usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it -asynchronously in the tasklet was not safe (regression in -c4564d4a1a0a9b10d4419e48239f5d99e88d2667). - -This change unlinks it from the endpoint prior to queueing it for handling in -the tasklet, and also adds a check to ensure the urb is OK to be unlinked -before doing so. - -NULL pointer dereference kernel oopses had been observed in usb_hcd_giveback_urb -when a USB device was unplugged/replugged during data transfer. This effect -was reproduced using automated USB port power control, hundreds of replug -events were performed during active transfers to confirm that the problem was -eliminated. - -USB fix using a FIQ to implement split transactions - -This commit adds a FIQ implementaion that schedules -the split transactions using a FIQ so we don't get -held off by the interrupt latency of Linux - -dwc_otg: fix device attributes and avoid kernel warnings on boot - -dcw_otg: avoid logging function that can cause panics - -See: https://github.com/raspberrypi/firmware/issues/21 -Thanks to cleverca22 for fix - -dwc_otg: mask correct interrupts after transaction error recovery - -The dwc_otg driver will unmask certain interrupts on a transaction -that previously halted in the error state in order to reset the -QTD error count. The various fine-grained interrupt handlers do not -consider that other interrupts besides themselves were unmasked. - -By disabling the two other interrupts only ever enabled in DMA mode -for this purpose, we can avoid unnecessary function calls in the -IRQ handler. This will also prevent an unneccesary FIQ interrupt -from being generated if the FIQ is enabled. - -dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ - -In the case of a transaction to a device that had previously aborted -due to an error, several interrupts are enabled to reset the error -count when a device responds. This has the side-effect of making the -FIQ thrash because the hardware will generate multiple instances of -a NAK on an IN bulk/interrupt endpoint and multiple instances of ACK -on an OUT bulk/interrupt endpoint. Make the FIQ mask and clear the -associated interrupts. - -Additionally, on non-split transactions make sure that only unmasked -interrupts are cleared. This caused a hard-to-trigger but serious -race condition when you had the combination of an endpoint awaiting -error recovery and a transaction completed on an endpoint - due to -the sequencing and timing of interrupts generated by the dwc_otg core, -it was possible to confuse the IRQ handler. - -Fix function tracing - -dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue - -dwc_otg: prevent OOPSes during device disconnects - -The dwc_otg_urb_enqueue function is thread-unsafe. In particular the -access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and -friends does not occur within a critical section and so if a device -was unplugged during activity there was a high chance that the -usbcore hub_thread would try to disable the endpoint with partially- -formed entries in the URB queue. This would result in BUG() or null -pointer dereferences. - -Fix so that access of urb->hcpriv, enqueuing to the hardware and -adding to usbcore endpoint URB lists is contained within a single -critical section. - -dwc_otg: prevent BUG() in TT allocation if hub address is > 16 - -A fixed-size array is used to track TT allocation. This was -previously set to 16 which caused a crash because -dwc_otg_hcd_allocate_port would read past the end of the array. - -This was hit if a hub was plugged in which enumerated as addr > 16, -due to previous device resets or unplugs. - -Also add #ifdef FIQ_DEBUG around hcd->hub_port_alloc[], which grows -to a large size if 128 hub addresses are supported. This field is -for debug only for tracking which frame an allocate happened in. - -dwc_otg: make channel halts with unknown state less damaging - -If the IRQ received a channel halt interrupt through the FIQ -with no other bits set, the IRQ would not release the host -channel and never complete the URB. - -Add catchall handling to treat as a transaction error and retry. - -dwc_otg: fiq_split: use TTs with more granularity - -This fixes certain issues with split transaction scheduling. - -- Isochronous multi-packet OUT transactions now hog the TT until - they are completed - this prevents hubs aborting transactions - if they get a periodic start-split out-of-order -- Don't perform TT allocation on non-periodic endpoints - this - allows simultaneous use of the TT's bulk/control and periodic - transaction buffers - -This commit will mainly affect USB audio playback. - -dwc_otg: fix potential sleep while atomic during urb enqueue - -Fixes a regression introduced with eb1b482a. Kmalloc called from -dwc_otg_hcd_qtd_add / dwc_otg_hcd_qtd_create did not always have -the GPF_ATOMIC flag set. Force this flag when inside the larger -critical section. - -dwc_otg: make fiq_split_enable imply fiq_fix_enable - -Failing to set up the FIQ correctly would result in -"IRQ 32: nobody cared" errors in dmesg. - -dwc_otg: prevent crashes on host port disconnects - -Fix several issues resulting in crashes or inconsistent state -if a Model A root port was disconnected. - -- Clean up queue heads properly in kill_urbs_in_qh_list by - removing the empty QHs from the schedule lists -- Set the halt status properly to prevent IRQ handlers from - using freed memory -- Add fiq_split related cleanup for saved registers -- Make microframe scheduling reclaim host channels if - active during a disconnect -- Abort URBs with -ESHUTDOWN status response, informing - device drivers so they respond in a more correct fashion - and don't try to resubmit URBs -- Prevent IRQ handlers from attempting to handle channel - interrupts if the associated URB was dequeued (and the - driver state was cleared) - -dwc_otg: prevent leaking URBs during enqueue - -A dwc_otg_urb would get leaked if the HCD enqueue function -failed for any reason. Free the URB at the appropriate points. - -dwc_otg: Enable NAK holdoff for control split transactions - -Certain low-speed devices take a very long time to complete a -data or status stage of a control transaction, producing NAK -responses until they complete internal processing - the USB2.0 -spec limit is up to 500mS. This causes the same type of interrupt -storm as seen with USB-serial dongles prior to c8edb238. - -In certain circumstances, usually while booting, this interrupt -storm could cause SD card timeouts. - -dwc_otg: Fix for occasional lockup on boot when doing a USB reset - -dwc_otg: Don't issue traffic to LS devices in FS mode - -Issuing low-speed packets when the root port is in full-speed mode -causes the root port to stop responding. Explicitly fail when -enqueuing URBs to a LS endpoint on a FS bus. - -Fix ARM architecture issue with local_irq_restore() - -If local_fiq_enable() is called before a local_irq_restore(flags) where -the flags variable has the F bit set, the FIQ will be erroneously disabled. - -Fixup arch_local_irq_restore to avoid trampling the F bit in CPSR. - -Also fix some of the hacks previously implemented for previous dwc_otg -incarnations. ---- - arch/arm/Kconfig | 1 + - arch/arm/include/asm/irqflags.h | 16 +- - arch/arm/kernel/fiqasm.S | 4 + - arch/arm/mach-bcm2708/armctrl.c | 19 +- - arch/arm/mach-bcm2708/bcm2708.c | 29 +- - arch/arm/mach-bcm2708/include/mach/irqs.h | 153 ++--- - .../usb/host/dwc_common_port/dwc_common_linux.c | 11 + - drivers/usb/host/dwc_common_port/dwc_list.h | 14 +- - drivers/usb/host/dwc_common_port/dwc_os.h | 2 + - drivers/usb/host/dwc_otg/Makefile | 1 + - drivers/usb/host/dwc_otg/dwc_otg_attr.c | 14 +- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +- - drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 1 + - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 52 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 303 +++++++-- - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 37 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 3 +- - drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 5 + - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 705 ++++++++++++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 159 +++-- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 53 +- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 ++++ - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 ++ - drivers/usb/host/dwc_otg/dwc_otg_os_dep.h | 3 + - drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 2 +- - 25 files changed, 1544 insertions(+), 251 deletions(-) - create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c - create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index fdfb230..f607868 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -381,6 +381,7 @@ config ARCH_BCM2708 - select ARM_ERRATA_411920 - select MACH_BCM2708 - select VC4 -+ select FIQ - help - This enables support for Broadcom BCM2708 boards. - -diff --git a/arch/arm/include/asm/irqflags.h b/arch/arm/include/asm/irqflags.h -index 3b763d6..5770408 100644 ---- a/arch/arm/include/asm/irqflags.h -+++ b/arch/arm/include/asm/irqflags.h -@@ -145,12 +145,22 @@ static inline unsigned long arch_local_save_flags(void) - } - - /* -- * restore saved IRQ & FIQ state -+ * restore saved IRQ state - */ - static inline void arch_local_irq_restore(unsigned long flags) - { -- asm volatile( -- " msr " IRQMASK_REG_NAME_W ", %0 @ local_irq_restore" -+ unsigned long temp = 0; -+ flags &= ~(1 << 6); -+ asm volatile ( -+ " mrs %0, cpsr" -+ : "=r" (temp) -+ : -+ : "memory", "cc"); -+ /* Preserve FIQ bit */ -+ temp &= (1 << 6); -+ flags = flags | temp; -+ asm volatile ( -+ " msr cpsr_c, %0 @ local_irq_restore" - : - : "r" (flags) - : "memory", "cc"); -diff --git a/arch/arm/kernel/fiqasm.S b/arch/arm/kernel/fiqasm.S -index 8dd26e1..eef4847 100644 ---- a/arch/arm/kernel/fiqasm.S -+++ b/arch/arm/kernel/fiqasm.S -@@ -47,3 +47,7 @@ ENTRY(__get_fiq_regs) - mov r0, r0 @ avoid hazard prior to ARMv4 - ret lr - ENDPROC(__get_fiq_regs) -+ -+ENTRY(__FIQ_Branch) -+ mov pc, r8 -+ENDPROC(__FIQ_Branch) -diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c -index ef1c8d5..96fa9b9 100644 ---- a/arch/arm/mach-bcm2708/armctrl.c -+++ b/arch/arm/mach-bcm2708/armctrl.c -@@ -52,8 +52,12 @@ static void armctrl_mask_irq(struct irq_data *d) - 0 - }; - -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -- writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ if (d->irq >= FIQ_START) { -+ writel(0, __io_address(ARM_IRQ_FAST)); -+ } else { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } - } - - static void armctrl_unmask_irq(struct irq_data *d) -@@ -65,8 +69,14 @@ static void armctrl_unmask_irq(struct irq_data *d) - 0 - }; - -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -- writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ if (d->irq >= FIQ_START) { -+ unsigned int data = -+ (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); -+ } else { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } - } - - #if defined(CONFIG_PM) -@@ -204,5 +214,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, - } - - armctrl_pm_register(base, irq_start, resume_sources); -+ init_FIQ(FIQ_START); - return 0; - } -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 7dd89a7f..dc59a6b 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -321,12 +321,32 @@ static struct resource bcm2708_usb_resources[] = { - .flags = IORESOURCE_MEM, - }, - [1] = { -- .start = IRQ_USB, -- .end = IRQ_USB, -+ .start = MPHI_BASE, -+ .end = MPHI_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [2] = { -+ .start = IRQ_HOSTPORT, -+ .end = IRQ_HOSTPORT, - .flags = IORESOURCE_IRQ, - }, - }; - -+bool fiq_fix_enable = true; -+ -+static struct resource bcm2708_usb_resources_no_fiq_fix[] = { -+ [0] = { -+ .start = USB_BASE, -+ .end = USB_BASE + SZ_128K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ - static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_usb_device = { -@@ -681,6 +701,11 @@ void __init bcm2708_init(void) - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -+ if (!fiq_fix_enable) -+ { -+ bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix; -+ bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix); -+ } - bcm_register_device(&bcm2708_usb_device); - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); -diff --git a/arch/arm/mach-bcm2708/include/mach/irqs.h b/arch/arm/mach-bcm2708/include/mach/irqs.h -index 3a88a1a..45152ed 100644 ---- a/arch/arm/mach-bcm2708/include/mach/irqs.h -+++ b/arch/arm/mach-bcm2708/include/mach/irqs.h -@@ -106,87 +106,90 @@ - #define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) - #define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) - -+#define FIQ_START HARD_IRQS -+ - /* - * FIQ interrupts definitions are the same as the INT definitions. - */ --#define FIQ_TIMER0 INT_TIMER0 --#define FIQ_TIMER1 INT_TIMER1 --#define FIQ_TIMER2 INT_TIMER2 --#define FIQ_TIMER3 INT_TIMER3 --#define FIQ_CODEC0 INT_CODEC0 --#define FIQ_CODEC1 INT_CODEC1 --#define FIQ_CODEC2 INT_CODEC2 --#define FIQ_JPEG INT_JPEG --#define FIQ_ISP INT_ISP --#define FIQ_USB INT_USB --#define FIQ_3D INT_3D --#define FIQ_TRANSPOSER INT_TRANSPOSER --#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 --#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 --#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 --#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 --#define FIQ_DMA0 INT_DMA0 --#define FIQ_DMA1 INT_DMA1 --#define FIQ_DMA2 INT_DMA2 --#define FIQ_DMA3 INT_DMA3 --#define FIQ_DMA4 INT_DMA4 --#define FIQ_DMA5 INT_DMA5 --#define FIQ_DMA6 INT_DMA6 --#define FIQ_DMA7 INT_DMA7 --#define FIQ_DMA8 INT_DMA8 --#define FIQ_DMA9 INT_DMA9 --#define FIQ_DMA10 INT_DMA10 --#define FIQ_DMA11 INT_DMA11 --#define FIQ_DMA12 INT_DMA12 --#define FIQ_AUX INT_AUX --#define FIQ_ARM INT_ARM --#define FIQ_VPUDMA INT_VPUDMA --#define FIQ_HOSTPORT INT_HOSTPORT --#define FIQ_VIDEOSCALER INT_VIDEOSCALER --#define FIQ_CCP2TX INT_CCP2TX --#define FIQ_SDC INT_SDC --#define FIQ_DSI0 INT_DSI0 --#define FIQ_AVE INT_AVE --#define FIQ_CAM0 INT_CAM0 --#define FIQ_CAM1 INT_CAM1 --#define FIQ_HDMI0 INT_HDMI0 --#define FIQ_HDMI1 INT_HDMI1 --#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 --#define FIQ_I2CSPISLV INT_I2CSPISLV --#define FIQ_DSI1 INT_DSI1 --#define FIQ_PWA0 INT_PWA0 --#define FIQ_PWA1 INT_PWA1 --#define FIQ_CPR INT_CPR --#define FIQ_SMI INT_SMI --#define FIQ_GPIO0 INT_GPIO0 --#define FIQ_GPIO1 INT_GPIO1 --#define FIQ_GPIO2 INT_GPIO2 --#define FIQ_GPIO3 INT_GPIO3 --#define FIQ_I2C INT_I2C --#define FIQ_SPI INT_SPI --#define FIQ_I2SPCM INT_I2SPCM --#define FIQ_SDIO INT_SDIO --#define FIQ_UART INT_UART --#define FIQ_SLIMBUS INT_SLIMBUS --#define FIQ_VEC INT_VEC --#define FIQ_CPG INT_CPG --#define FIQ_RNG INT_RNG --#define FIQ_ARASANSDIO INT_ARASANSDIO --#define FIQ_AVSPMON INT_AVSPMON -+#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) -+#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) -+#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) -+#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) -+#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) -+#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) -+#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) -+#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) -+#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) -+#define FIQ_USB (FIQ_START+INTERRUPT_USB) -+#define FIQ_3D (FIQ_START+INTERRUPT_3D) -+#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) -+#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) -+#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) -+#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) -+#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) -+#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) -+#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) -+#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) -+#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) -+#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) -+#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) -+#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) -+#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) -+#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) -+#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) -+#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) -+#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) -+#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) -+#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) -+#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) -+#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) -+#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) -+#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) -+#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) -+#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) -+#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) -+#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) -+#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) -+#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) -+#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) -+#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) -+#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) -+#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) -+#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) -+#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) -+#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) -+#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) -+#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) -+#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) -+#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) -+#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) -+#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) -+#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) -+#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) -+#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) -+#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) -+#define FIQ_UART (FIQ_START+INTERRUPT_UART) -+#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) -+#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) -+#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) -+#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) -+#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) -+#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) - --#define FIQ_ARM_TIMER INT_ARM_TIMER --#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX --#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 --#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 --#define FIQ_VPU0_HALTED INT_VPU0_HALTED --#define FIQ_VPU1_HALTED INT_VPU1_HALTED --#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 --#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 --#define FIQ_PENDING1 INT_PENDING1 --#define FIQ_PENDING2 INT_PENDING2 -+#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) -+#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) -+#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) -+#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) -+#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) -+#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) -+#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) -+#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) -+#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) -+#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) - - #define HARD_IRQS (64 + 21) --#define GPIO_IRQ_START (HARD_IRQS) -+#define FIQ_IRQS (64 + 21) -+#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) - #define GPIO_IRQS (32*5) - #define SPARE_ALLOC_IRQS 64 - #define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) -diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -index 1668f10..5c50a8b 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c -+++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -580,7 +580,13 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) - - void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) - { -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ local_fiq_disable(); - writel((readl(reg) & ~clear_mask) | set_mask, reg); -+ local_fiq_enable(); -+ local_irq_restore(flags); - } - - #if 0 -@@ -995,6 +1001,11 @@ void DWC_TASK_SCHEDULE(dwc_tasklet_t *task) - tasklet_schedule(&task->t); - } - -+void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task) -+{ -+ tasklet_hi_schedule(&task->t); -+} -+ - - /* workqueues - - run in process context (can sleep) -diff --git a/drivers/usb/host/dwc_common_port/dwc_list.h b/drivers/usb/host/dwc_common_port/dwc_list.h -index 89cc325..4ce560d 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_list.h -+++ b/drivers/usb/host/dwc_common_port/dwc_list.h -@@ -384,17 +384,17 @@ struct { \ - #define DWC_TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - #define DWC_TAILQ_EMPTY(head) \ -- (TAILQ_FIRST(head) == TAILQ_END(head)) -+ (DWC_TAILQ_FIRST(head) == DWC_TAILQ_END(head)) - - #define DWC_TAILQ_FOREACH(var, head, field) \ -- for((var) = TAILQ_FIRST(head); \ -- (var) != TAILQ_END(head); \ -- (var) = TAILQ_NEXT(var, field)) -+ for ((var) = DWC_TAILQ_FIRST(head); \ -+ (var) != DWC_TAILQ_END(head); \ -+ (var) = DWC_TAILQ_NEXT(var, field)) - - #define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ -- for((var) = TAILQ_LAST(head, headname); \ -- (var) != TAILQ_END(head); \ -- (var) = TAILQ_PREV(var, headname, field)) -+ for ((var) = DWC_TAILQ_LAST(head, headname); \ -+ (var) != DWC_TAILQ_END(head); \ -+ (var) = DWC_TAILQ_PREV(var, headname, field)) - - /* - * Tail queue functions. -diff --git a/drivers/usb/host/dwc_common_port/dwc_os.h b/drivers/usb/host/dwc_common_port/dwc_os.h -index 8117731..a2bbe23 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_os.h -+++ b/drivers/usb/host/dwc_common_port/dwc_os.h -@@ -982,6 +982,8 @@ extern void DWC_TASK_FREE(dwc_tasklet_t *task); - extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); - #define dwc_task_schedule DWC_TASK_SCHEDULE - -+extern void DWC_TASK_HI_SCHEDULE(dwc_tasklet_t *task); -+#define dwc_task_hi_schedule DWC_TASK_HI_SCHEDULE - - /** @name Timer - * -diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile -index 236c47c..a56f193 100644 ---- a/drivers/usb/host/dwc_otg/Makefile -+++ b/drivers/usb/host/dwc_otg/Makefile -@@ -36,6 +36,7 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o - dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o - dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o - dwc_otg-objs += dwc_otg_adp.o -+dwc_otg-objs += dwc_otg_mphi_fix.o - ifneq ($(CFI),) - dwc_otg-objs += dwc_otg_cfi.o - endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_attr.c b/drivers/usb/host/dwc_otg/dwc_otg_attr.c -index fab2961..9da0c92 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_attr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c -@@ -909,7 +909,7 @@ static ssize_t regdump_show(struct device *_dev, - return sprintf(buf, "Register Dump\n"); - } - --DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); -+DEVICE_ATTR(regdump, S_IRUGO, regdump_show, 0); - - /** - * Dump global registers and either host or device registers (depending on the -@@ -920,12 +920,12 @@ static ssize_t spramdump_show(struct device *_dev, - { - dwc_otg_device_t *otg_dev = dwc_otg_drvdev(_dev); - -- dwc_otg_dump_spram(otg_dev->core_if); -+ //dwc_otg_dump_spram(otg_dev->core_if); - - return sprintf(buf, "SPRAM Dump\n"); - } - --DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); -+DEVICE_ATTR(spramdump, S_IRUGO, spramdump_show, 0); - - /** - * Dump the current hcd state. -@@ -940,7 +940,7 @@ static ssize_t hcddump_show(struct device *_dev, - return sprintf(buf, "HCD Dump\n"); - } - --DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); -+DEVICE_ATTR(hcddump, S_IRUGO, hcddump_show, 0); - - /** - * Dump the average frame remaining at SOF. This can be used to -@@ -958,7 +958,7 @@ static ssize_t hcd_frrem_show(struct device *_dev, - return sprintf(buf, "HCD Dump Frame Remaining\n"); - } - --DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); -+DEVICE_ATTR(hcd_frrem, S_IRUGO, hcd_frrem_show, 0); - - /** - * Displays the time required to read the GNPTXFSIZ register many times (the -@@ -986,7 +986,7 @@ static ssize_t rd_reg_test_show(struct device *_dev, - RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); - } - --DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); -+DEVICE_ATTR(rd_reg_test, S_IRUGO, rd_reg_test_show, 0); - - /** - * Displays the time required to write the GNPTXFSIZ register many times (the -@@ -1014,7 +1014,7 @@ static ssize_t wr_reg_test_show(struct device *_dev, - RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); - } - --DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); -+DEVICE_ATTR(wr_reg_test, S_IRUGO, wr_reg_test_show, 0); - - #ifdef CONFIG_USB_DWC_OTG_LPM - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 59fc862..2f8b3bd 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -45,6 +45,7 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_pcd.h" - #include "dwc_otg_hcd.h" -+#include "dwc_otg_mphi_fix.h" - - #ifdef DEBUG - inline const char *op_state_str(dwc_otg_core_if_t * core_if) -@@ -1318,7 +1319,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) - /** - * This function returns the Core Interrupt register. - */ --static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk) - { - gahbcfg_data_t gahbcfg = {.d32 = 0 }; - gintsts_data_t gintsts; -@@ -1335,26 +1336,45 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) - gintmsk_common.b.lpmtranrcvd = 1; - #endif - gintmsk_common.b.restoredone = 1; -- /** @todo: The port interrupt occurs while in device -- * mode. Added code to CIL to clear the interrupt for now! -- */ -- gintmsk_common.b.portintr = 1; -- -+ if(dwc_otg_is_device_mode(core_if)) -+ { -+ /** @todo: The port interrupt occurs while in device -+ * mode. Added code to CIL to clear the interrupt for now! -+ */ -+ gintmsk_common.b.portintr = 1; -+ } - gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); - gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -+ { -+ unsigned long flags; -+ -+ // Re-enable the saved interrupts -+ local_irq_save(flags); -+ local_fiq_disable(); -+ gintmsk.d32 |= gintmsk_common.d32; -+ gintsts_saved.d32 &= ~gintmsk_common.d32; -+ reenable_gintmsk->d32 = gintmsk.d32; -+ local_irq_restore(flags); -+ } -+ - gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); - - #ifdef DEBUG - /* if any common interrupts set */ - if (gintsts.d32 & gintmsk_common.d32) { -- DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", -+ DWC_DEBUGPL(DBG_ANY, "common_intr: gintsts=%08x gintmsk=%08x\n", - gintsts.d32, gintmsk.d32); - } - #endif -- if (gahbcfg.b.glblintrmsk) -+ if (!fiq_fix_enable){ -+ if (gahbcfg.b.glblintrmsk) -+ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -+ else -+ return 0; -+ } -+ else { - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); -- else -- return 0; -+ } - - } - -@@ -1386,6 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - { - int retval = 0; - gintsts_data_t gintsts; -+ gintmsk_data_t reenable_gintmsk; - gpwrdn_data_t gpwrdn = {.d32 = 0 }; - dwc_otg_device_t *otg_dev = dev; - dwc_otg_core_if_t *core_if = otg_dev->core_if; -@@ -1407,7 +1428,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - - if (core_if->hibernation_suspend <= 0) { -- gintsts.d32 = dwc_otg_read_common_intr(core_if); -+ gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk); - - if (gintsts.b.modemismatch) { - retval |= dwc_otg_handle_mode_mismatch_intr(core_if); -@@ -1504,8 +1525,12 @@ int32_t dwc_otg_handle_common_intr(void *dev) - gintsts.b.portintr = 1; - DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); - retval |= 1; -+ reenable_gintmsk.b.portintr = 1; - - } -+ -+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32); -+ - } else { - DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -index 8900318..ccc24e0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h -@@ -49,6 +49,7 @@ static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new) - return old; - } - -+#define DBG_USER (0x1) - /** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ - #define DBG_CIL (0x2) - /** When debug level has the DBG_CILV bit set, display CIL Verbose debug -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -index ac2c846..f06c3d22 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -64,6 +64,8 @@ bool microframe_schedule=true; - - static const char dwc_driver_name[] = "dwc_otg"; - -+extern void* dummy_send; -+ - extern int pcd_init( - #ifdef LM_INTERFACE - struct lm_device *_dev -@@ -238,6 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = { - .adp_enable = -1, - }; - -+//Global variable to switch the fiq fix on or off (declared in bcm2708.c) -+extern bool fiq_fix_enable; -+// Global variable to enable the split transaction fix -+bool fiq_split_enable = true; -+//Global variable to switch the nak holdoff on or off -+bool nak_holdoff_enable = true; -+ -+ - /** - * This function shows the Driver Version. - */ -@@ -779,17 +789,33 @@ static int dwc_otg_driver_probe( - _dev->resource->start, - _dev->resource->end - _dev->resource->start + 1); - #if 1 -- if (!request_mem_region(_dev->resource->start, -- _dev->resource->end - _dev->resource->start + 1, -+ if (!request_mem_region(_dev->resource[0].start, -+ _dev->resource[0].end - _dev->resource[0].start + 1, - "dwc_otg")) { - dev_dbg(&_dev->dev, "error reserving mapped memory\n"); - retval = -EFAULT; - goto fail; - } - -- dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource->start, -- _dev->resource->end - -- _dev->resource->start+1); -+ dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, -+ _dev->resource[0].end - -+ _dev->resource[0].start+1); -+ if (fiq_fix_enable) -+ { -+ if (!request_mem_region(_dev->resource[1].start, -+ _dev->resource[1].end - _dev->resource[1].start + 1, -+ "dwc_otg")) { -+ dev_dbg(&_dev->dev, "error reserving mapped memory\n"); -+ retval = -EFAULT; -+ goto fail; -+ } -+ -+ dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, -+ _dev->resource[1].end - -+ _dev->resource[1].start + 1); -+ dummy_send = (void *) kmalloc(16, GFP_ATOMIC); -+ } -+ - #else - { - struct map_desc desc = { -@@ -1044,6 +1070,12 @@ static int __init dwc_otg_driver_init(void) - int retval = 0; - int error; - struct device_driver *drv; -+ -+ if(fiq_split_enable && !fiq_fix_enable) { -+ printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n"); -+ fiq_fix_enable = 1; -+ } -+ - printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, - DWC_DRIVER_VERSION, - #ifdef LM_INTERFACE -@@ -1063,6 +1095,9 @@ static int __init dwc_otg_driver_init(void) - printk(KERN_ERR "%s retval=%d\n", __func__, retval); - return retval; - } -+ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled"); - - error = driver_create_file(drv, &driver_attr_version); - #ifdef DEBUG -@@ -1343,6 +1378,13 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); - module_param(microframe_schedule, bool, 0444); - MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); - -+module_param(fiq_fix_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix"); -+module_param(nak_holdoff_enable, bool, 0444); -+MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff"); -+module_param(fiq_split_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions"); -+ - /** @page "Module Parameters" - * - * The following parameters may be specified when starting the module. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 1e89549..986d361 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -40,10 +40,14 @@ - * header file. - */ - -+#include -+#include -+ - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" - --extern bool microframe_schedule; -+extern bool microframe_schedule, nak_holdoff_enable; - - //#define DEBUG_HOST_CHANNELS - #ifdef DEBUG_HOST_CHANNELS -@@ -53,6 +57,13 @@ static int last_sel_trans_num_avail_hc_at_start = 0; - static int last_sel_trans_num_avail_hc_at_end = 0; - #endif /* DEBUG_HOST_CHANNELS */ - -+extern int g_next_sched_frame, g_np_count, g_np_sent; -+ -+extern haint_data_t haint_saved; -+extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; -+extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; -+extern gintsts_data_t ginsts_saved; -+ - dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) - { - return DWC_ALLOC(sizeof(dwc_otg_hcd_t)); -@@ -162,31 +173,43 @@ static void del_timers(dwc_otg_hcd_t * hcd) - - /** - * Processes all the URBs in a single list of QHs. Completes them with -- * -ETIMEDOUT and frees the QTD. -+ * -ESHUTDOWN and frees the QTD. - */ - static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) - { -- dwc_list_link_t *qh_item; -+ dwc_list_link_t *qh_item, *qh_tmp; - dwc_otg_qh_t *qh; - dwc_otg_qtd_t *qtd, *qtd_tmp; - -- DWC_LIST_FOREACH(qh_item, qh_list) { -+ DWC_LIST_FOREACH_SAFE(qh_item, qh_tmp, qh_list) { - qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); - DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, - &qh->qtd_list, qtd_list_entry) { - qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - if (qtd->urb != NULL) { - hcd->fops->complete(hcd, qtd->urb->priv, -- qtd->urb, -DWC_E_TIMEOUT); -+ qtd->urb, -DWC_E_SHUTDOWN); - dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); - } - - } -+ if(qh->channel) { -+ /* Using hcchar.chen == 1 is not a reliable test. -+ * It is possible that the channel has already halted -+ * but not yet been through the IRQ handler. -+ */ -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); -+ if(microframe_schedule) -+ hcd->available_host_channels++; -+ qh->channel = NULL; -+ } -+ dwc_otg_hcd_qh_remove(hcd, qh); - } - } - - /** -- * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic -+ * Responds with an error status of ESHUTDOWN to all URBs in the non-periodic - * and periodic schedules. The QTD associated with each URB is removed from - * the schedule and freed. This function may be called when a disconnect is - * detected or when the HCD is being stopped. -@@ -272,7 +295,8 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - */ - dwc_otg_hcd->flags.b.port_connect_status_change = 1; - dwc_otg_hcd->flags.b.port_connect_status = 0; -- -+ if(fiq_fix_enable) -+ local_fiq_disable(); - /* - * Shutdown any transfers in process by clearing the Tx FIFO Empty - * interrupt mask and status bits and disabling subsequent host -@@ -368,8 +392,22 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - channel->qh = NULL; - } - } -+ if(fiq_split_enable) { -+ for(i=0; i < 128; i++) { -+ dwc_otg_hcd->hub_port[i] = 0; -+ } -+ haint_saved.d32 = 0; -+ for(i=0; i < MAX_EPS_CHANNELS; i++) { -+ hcint_saved[i].d32 = 0; -+ hcintmsk_saved[i].d32 = 0; -+ } -+ } -+ - } - -+ if(fiq_fix_enable) -+ local_fiq_enable(); -+ - if (dwc_otg_hcd->fops->disconnect) { - dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); - } -@@ -407,6 +445,7 @@ static int dwc_otg_hcd_sleep_cb(void *p) - } - #endif - -+ - /** - * HCD Callback function for Remote Wakeup. - * -@@ -457,10 +496,12 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle, - int atomic_alloc) - { -- dwc_irqflags_t flags; - int retval = 0; -+ uint8_t needs_scheduling = 0; -+ dwc_otg_transaction_type_e tr_type; - dwc_otg_qtd_t *qtd; - gintmsk_data_t intr_mask = {.d32 = 0 }; -+ hprt0_data_t hprt0 = { .d32 = 0 }; - - #ifdef DEBUG /* integrity checks (Broadcom) */ - if (NULL == hcd->core_if) { -@@ -475,6 +516,16 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - return -DWC_E_NO_DEVICE; - } - -+ /* Some core configurations cannot support LS traffic on a FS root port */ -+ if ((hcd->fops->speed(hcd, dwc_otg_urb->priv) == USB_SPEED_LOW) && -+ (hcd->core_if->hwcfg2.b.fs_phy_type == 1) && -+ (hcd->core_if->hwcfg2.b.hs_phy_type == 1)) { -+ hprt0.d32 = DWC_READ_REG32(hcd->core_if->host_if->hprt0); -+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) { -+ return -DWC_E_NO_DEVICE; -+ } -+ } -+ - qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb, atomic_alloc); - if (qtd == NULL) { - DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); -@@ -490,32 +541,27 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - return -DWC_E_NO_MEMORY; - } - #endif -- retval = -- dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); -+ intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -+ if(!intr_mask.b.sofintr) needs_scheduling = 1; -+ if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) -+ /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -+ needs_scheduling = 0; -+ -+ retval = dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle, atomic_alloc); - // creates a new queue in ep_handle if it doesn't exist already - if (retval < 0) { - DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " - "Error status %d\n", retval); - dwc_otg_hcd_qtd_free(qtd); -- } else { -- qtd->qh = *ep_handle; -+ return retval; - } -- intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -- if (!intr_mask.b.sofintr && retval == 0) { -- dwc_otg_transaction_type_e tr_type; -- if ((qtd->qh->ep_type == UE_BULK) -- && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { -- /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ -- return 0; -- } -- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ -+ if(needs_scheduling) { - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); - } -- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); - } -- - return retval; - } - -@@ -524,6 +570,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - { - dwc_otg_qh_t *qh; - dwc_otg_qtd_t *urb_qtd; -+ BUG_ON(!hcd); -+ BUG_ON(!dwc_otg_urb); - - #ifdef DEBUG /* integrity checks (Broadcom) */ - -@@ -540,14 +588,17 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - return -DWC_E_INVALID; - } - urb_qtd = dwc_otg_urb->qtd; -+ BUG_ON(!urb_qtd); - if (urb_qtd->qh == NULL) { - DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); - return -DWC_E_INVALID; - } - #else - urb_qtd = dwc_otg_urb->qtd; -+ BUG_ON(!urb_qtd); - #endif - qh = urb_qtd->qh; -+ BUG_ON(!qh); - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - if (urb_qtd->in_process) { - dump_channel_info(hcd, qh); -@@ -571,6 +622,8 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - */ - dwc_otg_hc_halt(hcd->core_if, qh->channel, - DWC_OTG_HC_XFER_URB_DEQUEUE); -+ -+ dwc_otg_hcd_release_port(hcd, qh); - } - } - -@@ -687,6 +740,33 @@ static void reset_tasklet_func(void *data) - dwc_otg_hcd->flags.b.port_reset_change = 1; - } - -+static void completion_tasklet_func(void *ptr) -+{ -+ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) ptr; -+ struct urb *urb; -+ urb_tq_entry_t *item; -+ dwc_irqflags_t flags; -+ -+ /* This could just be spin_lock_irq */ -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ while (!DWC_TAILQ_EMPTY(&hcd->completed_urb_list)) { -+ item = DWC_TAILQ_FIRST(&hcd->completed_urb_list); -+ urb = item->urb; -+ DWC_TAILQ_REMOVE(&hcd->completed_urb_list, item, -+ urb_tq_entries); -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ DWC_FREE(item); -+ -+ usb_hcd_giveback_urb(hcd->priv, urb, urb->status); -+ -+ fiq_print(FIQDBG_PORTHUB, "COMPLETE"); -+ -+ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); -+ } -+ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -+ return; -+} -+ - static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) - { - dwc_list_link_t *item; -@@ -819,12 +899,14 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) - } else if (dwc_otg_hcd->status_buf != NULL) { - DWC_FREE(dwc_otg_hcd->status_buf); - } -+ DWC_SPINLOCK_FREE(dwc_otg_hcd->channel_lock); - DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); - /* Set core_if's lock pointer to NULL */ - dwc_otg_hcd->core_if->lock = NULL; - - DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); - DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); -+ DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); - - #ifdef DWC_DEV_SRPCAP - if (dwc_otg_hcd->core_if->power_down == 2 && -@@ -874,7 +956,7 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - DWC_LIST_INIT(&hcd->periodic_sched_ready); - DWC_LIST_INIT(&hcd->periodic_sched_assigned); - DWC_LIST_INIT(&hcd->periodic_sched_queued); -- -+ DWC_TAILQ_INIT(&hcd->completed_urb_list); - /* - * Create a host channel descriptor for each host channel implemented - * in the controller. Initialize the channel descriptor array. -@@ -912,6 +994,9 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - - /* Initialize reset tasklet. */ - hcd->reset_tasklet = DWC_TASK_ALLOC("reset_tasklet", reset_tasklet_func, hcd); -+ -+ hcd->completion_tasklet = DWC_TASK_ALLOC("completion_tasklet", -+ completion_tasklet_func, hcd); - #ifdef DWC_DEV_SRPCAP - if (hcd->core_if->power_down == 2) { - /* Initialize Power on timer for Host power up in case hibernation */ -@@ -944,6 +1029,12 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - hcd->frame_list = NULL; - hcd->frame_list_dma = 0; - hcd->periodic_qh_count = 0; -+ -+ DWC_MEMSET(hcd->hub_port, 0, sizeof(hcd->hub_port)); -+#ifdef FIQ_DEBUG -+ DWC_MEMSET(hcd->hub_port_alloc, -1, sizeof(hcd->hub_port_alloc)); -+#endif -+ - out: - return retval; - } -@@ -1089,7 +1180,12 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - uint32_t hub_addr, port_addr; - hc->do_split = 1; - hc->xact_pos = qtd->isoc_split_pos; -- hc->complete_split = qtd->complete_split; -+ /* We don't need to do complete splits anymore */ -+ if(fiq_split_enable) -+ hc->complete_split = qtd->complete_split = 0; -+ else -+ hc->complete_split = qtd->complete_split; -+ - hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); - hc->hub_addr = (uint8_t) hub_addr; - hc->port_addr = (uint8_t) port_addr; -@@ -1236,6 +1332,65 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->qh = qh; - } - -+/* -+** Check the transaction to see if the port / hub has already been assigned for -+** a split transaction -+** -+** Return 0 - Port is already in use -+*/ -+int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+{ -+ uint32_t hub_addr, port_addr; -+ -+ if(!fiq_split_enable) -+ return 0; -+ -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ -+ if(hcd->hub_port[hub_addr] & (1 << port_addr)) -+ { -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count); -+ -+ qh->skip_count++; -+ -+ if(qh->skip_count > 40000) -+ { -+ printk_once(KERN_ERR "Error: Having to skip port allocation"); -+ local_fiq_disable(); -+ BUG(); -+ return 0; -+ } -+ return 1; -+ } -+ else -+ { -+ qh->skip_count = 0; -+ hcd->hub_port[hub_addr] |= 1 << port_addr; -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd); -+#endif -+ return 0; -+ } -+} -+void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+{ -+ uint32_t hub_addr, port_addr; -+ -+ if(!fiq_split_enable) -+ return; -+ -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ -+ hcd->hub_port[hub_addr] &= ~(1 << port_addr); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1; -+#endif -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+ -+} -+ -+ - /** - * This function selects transactions from the HCD transfer schedule and - * assigns them to available host channels. It is called from HCD interrupt -@@ -1249,9 +1404,10 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - { - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -+ dwc_otg_qtd_t *qtd; - int num_channels; - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; - dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; - - #ifdef DEBUG_SOF -@@ -1269,11 +1425,29 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - - while (qh_ptr != &hcd->periodic_sched_ready && - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { -+ -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ -+ if(qh->do_split) { -+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ if(!(qh->ep_type == UE_ISOCHRONOUS && -+ (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || -+ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) { -+ if(dwc_otg_hcd_allocate_port(hcd, qh)) -+ { -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1); -+ continue; -+ } -+ } -+ } -+ - if (microframe_schedule) { - // Make sure we leave one channel for non periodic transactions. - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels <= 1) { - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -+ if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh); - break; - } - hcd->available_host_channels--; -@@ -1294,8 +1468,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -- -- ret_val = DWC_OTG_TRANSACTION_PERIODIC; - } - - /* -@@ -1310,6 +1482,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - num_channels - hcd->periodic_channels) && - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { - -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ -+ /* -+ * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission -+ * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed -+ * cheeky devices that just hold off using NAKs -+ */ -+ if (nak_holdoff_enable && qh->do_split) { -+ if (qh->nak_frame != 0xffff && -+ dwc_full_frame_num(qh->nak_frame) == -+ dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) { -+ /* -+ * Revisit: Need to avoid trampling on periodic scheduling. -+ * Currently we are safe because g_np_count != g_np_sent whenever we hit this, -+ * but if this behaviour is changed then periodic endpoints will get a slower -+ * polling rate. -+ */ -+ g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM; -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ continue; -+ } else { -+ qh->nak_frame = 0xffff; -+ } -+ } -+ - if (microframe_schedule) { - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels < 1) { -@@ -1322,7 +1519,6 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - last_sel_trans_num_nonper_scheduled++; - #endif /* DEBUG_HOST_CHANNELS */ - } -- qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - - assign_and_init_hc(hcd, qh); - -@@ -1336,21 +1532,22 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - -- if (ret_val == DWC_OTG_TRANSACTION_NONE) { -- ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; -- } else { -- ret_val = DWC_OTG_TRANSACTION_ALL; -- } -+ g_np_sent++; - - if (!microframe_schedule) - hcd->non_periodic_channels++; - } - -+ if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) -+ ret_val |= DWC_OTG_TRANSACTION_PERIODIC; -+ -+ if(!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) -+ ret_val |= DWC_OTG_TRANSACTION_NON_PERIODIC; -+ -+ - #ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_avail_hc_at_end = hcd->available_host_channels; - #endif /* DEBUG_HOST_CHANNELS */ -- -- DWC_SPINLOCK_FREE(channel_lock); - return ret_val; - } - -@@ -1464,6 +1661,15 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - -+ // Do not send a split start transaction any later than frame .6 -+ // Note, we have to schedule a periodic in .5 to make it go in .6 -+ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ { -+ qh_ptr = qh_ptr->next; -+ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ continue; -+ } -+ - /* - * Set a flag if we're queuing high-bandwidth in slave mode. - * The flag prevents any halts to get into the request queue in -@@ -1593,6 +1799,15 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, - qh_list_entry); -+ -+ // Do not send a split start transaction any later than frame .5 -+ // non periodic transactions will start immediately in this uframe -+ if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ { -+ g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ break; -+ } -+ - status = - queue_transaction(hcd, qh->channel, - tx_status.b.nptxfspcavail); -@@ -3118,17 +3333,13 @@ dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, - else - dwc_otg_urb = DWC_ALLOC(size); - -- if (NULL != dwc_otg_urb) -- dwc_otg_urb->packet_count = iso_desc_count; -+ if (dwc_otg_urb) -+ dwc_otg_urb->packet_count = iso_desc_count; - else { -- dwc_otg_urb->packet_count = 0; -- if (size != 0) { -- DWC_ERROR("**** DWC OTG HCD URB alloc - " -- "%salloc of %db failed\n", -- atomic_alloc?"atomic ":"", size); -- } -- } -- -+ DWC_ERROR("**** DWC OTG HCD URB alloc - " -+ "%salloc of %db failed\n", -+ atomic_alloc?"atomic ":"", size); -+ } - return dwc_otg_urb; - } - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -index bb4f67a..0007fa1 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -168,10 +168,10 @@ typedef enum dwc_otg_control_phase { - - /** Transaction types. */ - typedef enum dwc_otg_transaction_type { -- DWC_OTG_TRANSACTION_NONE, -- DWC_OTG_TRANSACTION_PERIODIC, -- DWC_OTG_TRANSACTION_NON_PERIODIC, -- DWC_OTG_TRANSACTION_ALL -+ DWC_OTG_TRANSACTION_NONE = 0, -+ DWC_OTG_TRANSACTION_PERIODIC = 1, -+ DWC_OTG_TRANSACTION_NON_PERIODIC = 2, -+ DWC_OTG_TRANSACTION_ALL = DWC_OTG_TRANSACTION_PERIODIC + DWC_OTG_TRANSACTION_NON_PERIODIC - } dwc_otg_transaction_type_e; - - struct dwc_otg_qh; -@@ -321,6 +321,11 @@ typedef struct dwc_otg_qh { - */ - uint16_t sched_frame; - -+ /* -+ ** Frame a NAK was received on this queue head, used to minimise NAK retransmission -+ */ -+ uint16_t nak_frame; -+ - /** (micro)frame at which last start split was initialized. */ - uint16_t start_split_frame; - -@@ -365,10 +370,19 @@ typedef struct dwc_otg_qh { - - uint16_t speed; - uint16_t frame_usecs[8]; -+ -+ uint32_t skip_count; - } dwc_otg_qh_t; - - DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); - -+typedef struct urb_tq_entry { -+ struct urb *urb; -+ DWC_TAILQ_ENTRY(urb_tq_entry) urb_tq_entries; -+} urb_tq_entry_t; -+ -+DWC_TAILQ_HEAD(urb_list, urb_tq_entry); -+ - /** - * This structure holds the state of the HCD, including the non-periodic and - * periodic schedules. -@@ -546,9 +560,12 @@ struct dwc_otg_hcd { - /* Tasket to do a reset */ - dwc_tasklet_t *reset_tasklet; - -+ dwc_tasklet_t *completion_tasklet; -+ struct urb_list completed_urb_list; -+ - /* */ - dwc_spinlock_t *lock; -- -+ dwc_spinlock_t *channel_lock; - /** - * Private data that could be used by OS wrapper. - */ -@@ -559,6 +576,12 @@ struct dwc_otg_hcd { - /** Frame List */ - uint32_t *frame_list; - -+ /** Hub - Port assignment */ -+ int hub_port[128]; -+#ifdef FIQ_DEBUG -+ int hub_port_alloc[2048]; -+#endif -+ - /** Frame List DMA address */ - dma_addr_t frame_list_dma; - -@@ -589,6 +612,10 @@ extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t - extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - dwc_otg_transaction_type_e tr_type); - -+int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); -+void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); -+ -+ - /** @} */ - - /** @name Interrupt Handler Functions */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -index 274967b..ee920c4 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c -@@ -276,7 +276,7 @@ void dump_frame_list(dwc_otg_hcd_t * hcd) - static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - { - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; - - dwc_hc_t *hc = qh->channel; - if (dwc_qh_is_non_per(qh)) { -@@ -306,7 +306,6 @@ static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - dwc_memset(qh->desc_list, 0x00, - sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); - } -- DWC_SPINLOCK_FREE(channel_lock); - } - - /** -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -index 4823167..fb57db0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h -@@ -113,6 +113,11 @@ extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd); - */ - extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); - -+/** This function is used to handle the fast interrupt -+ * -+ */ -+extern void __attribute__ ((naked)) dwc_otg_hcd_handle_fiq(void); -+ - /** - * Returns private data set by - * dwc_otg_hcd_set_priv_data function. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index b41e164..64d33a5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -34,6 +34,12 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" -+ -+#include -+#include -+#include -+ - - extern bool microframe_schedule; - -@@ -41,38 +47,487 @@ extern bool microframe_schedule; - * This file contains the implementation of the HCD Interrupt handlers. - */ - -+/* -+ * Some globals to communicate between the FIQ and INTERRUPT -+ */ -+ -+void * dummy_send; -+mphi_regs_t c_mphi_regs; -+volatile void *dwc_regs_base; -+int fiq_done, int_done; -+ -+gintsts_data_t gintsts_saved = {.d32 = 0}; -+hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; -+hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; -+int split_out_xfersize[MAX_EPS_CHANNELS]; -+haint_data_t haint_saved; -+ -+int g_next_sched_frame, g_np_count, g_np_sent; -+static int mphi_int_count = 0 ; -+ -+hcchar_data_t nak_hcchar; -+hctsiz_data_t nak_hctsiz; -+hcsplt_data_t nak_hcsplt; -+int nak_count; -+ -+int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; -+int split_start_frame[MAX_EPS_CHANNELS]; -+int queued_port[MAX_EPS_CHANNELS]; -+ -+#ifdef FIQ_DEBUG -+char buffer[1000*16]; -+int wptr; -+void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) -+{ -+ FIQDBG_T dbg_lvl_req = FIQDBG_PORTHUB; -+ va_list args; -+ char text[17]; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ local_fiq_disable(); -+ if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) -+ { -+ snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); -+ va_start(args, fmt); -+ vsnprintf(text+8, 9, fmt, args); -+ va_end(args); -+ -+ memcpy(buffer + wptr, text, 16); -+ wptr = (wptr + 16) % sizeof(buffer); -+ } -+ local_irq_restore(flags); -+} -+#endif -+ -+void notrace fiq_queue_request(int channel, int odd_frame) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) }; -+ -+ if(hcsplt.b.spltena == 0) -+ { -+ fiq_print(FIQDBG_ERR, "SPLTENA "); -+ BUG(); -+ } -+ -+ if(hcchar.b.epdir == 1) -+ { -+ fiq_print(FIQDBG_SCHED, "IN Ch %d", channel); -+ } -+ else -+ { -+ hctsiz.b.xfersize = 0; -+ fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel); -+ } -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32); -+ -+ hcsplt.b.compsplt = 1; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32); -+ -+ // Send the Split complete -+ hcchar.b.chen = 1; -+ hcchar.b.oddfrm = odd_frame ? 1 : 0; -+ -+ // Post this for transmit on the next frame for periodic or this frame for non-periodic -+ fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN"); -+ -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32); -+} -+ -+static int last_sof = -1; -+ -+/* -+** Function to handle the start of frame interrupt, choose whether we need to do anything and -+** therefore trigger the main interrupt -+** -+** returns int != 0 - interrupt has been handled -+*/ -+int diff; -+ -+int notrace fiq_sof_handle(hfnum_data_t hfnum) -+{ -+ int handled = 0; -+ int i; -+ -+ // Just check that once we're running we don't miss a SOF -+ /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff))) -+ { -+ fiq_print(FIQDBG_ERR, "LASTSOF "); -+ fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7); -+ fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7); -+ BUG(); -+ }*/ -+ -+ // Only start remembering the last sof when the interrupt has been -+ // enabled (we don't check the mask to come in here...) -+ if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3)) -+ last_sof = hfnum.b.frnum; -+ -+ for(i = 0; i < MAX_EPS_CHANNELS; i++) -+ { -+ if(complete_sched[i] != -1) -+ { -+ if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0)) -+ { -+ fiq_queue_request(i, hfnum.b.frnum & 1); -+ complete_sched[i] = -1; -+ } -+ } -+ -+ if(complete_sched[i] != -1) -+ { -+ // This is because we've seen a split complete occur with no start... -+ // most likely because missed the complete 0x3fff frames ago! -+ -+ diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ; -+ if(diff > 32 && diff < 0x3f00) -+ { -+ fiq_print(FIQDBG_ERR, "SPLTMISS"); -+ BUG(); -+ } -+ } -+ } -+ -+ if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -+ { -+ /* -+ * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet -+ * g_next_sched_frame is the next frame we have periodic packets for -+ * -+ * if neither of these are required for this frame then just clear the interrupt -+ */ -+ handled = 1; -+ -+ } -+ -+ return handled; -+} -+ -+int notrace port_id(hcsplt_data_t hcsplt) -+{ -+ return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8); -+} -+ -+int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -+ hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) }; -+ hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)}; -+ -+ hcint_saved[channel].d32 |= hcint.d32; -+ hcintmsk_saved[channel].d32 = hcintmsk.d32; -+ -+ if(hcsplt.b.spltena) -+ { -+ fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt)); -+ if(hcint.b.chhltd) -+ { -+ fiq_print(FIQDBG_SCHED, "CH HLT %d", channel); -+ fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]); -+ } -+ if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_ERR, "CHAN ERR"); -+ } -+ if(hcint.b.xfercomp) -+ { -+ // Clear the port allocation and transmit anything also on this port -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "XFERCOMP"); -+ } -+ if(hcint.b.nak) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "NAK"); -+ } -+ if(hcint.b.ack && !hcsplt.b.compsplt) -+ { -+ int i; -+ -+ // Do not complete isochronous out transactions -+ if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0) -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_SCHED, "ISOC_OUT"); -+ } -+ else -+ { -+ // Make sure we check the port / hub combination that we sent this split on. -+ // Do not queue a second request to the same port -+ for(i = 0; i < MAX_EPS_CHANNELS; i++) -+ { -+ if(port_id(hcsplt) == queued_port[i]) -+ { -+ fiq_print(FIQDBG_ERR, "PORTERR "); -+ //BUG(); -+ } -+ } -+ -+ split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7; -+ -+ // Note, the size of an OUT is in the start split phase, not -+ // the complete split -+ split_out_xfersize[channel] = hctsiz.b.xfersize; -+ -+ hcint_saved[channel].b.chhltd = 0; -+ hcint_saved[channel].b.ack = 0; -+ -+ queued_port[channel] = port_id(hcsplt); -+ -+ if(hcchar.b.eptype & 1) -+ { -+ // Send the periodic complete in the same oddness frame as the ACK went... -+ fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -+ // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -+ } -+ else -+ { -+ // Schedule the split complete to occur later -+ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2); -+ fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -+ } -+ } -+ } -+ if(hcint.b.nyet) -+ { -+ fiq_print(FIQDBG_ERR, "NYETERR1"); -+ //BUG(); -+ // Can transmit a split complete up to uframe .0 of the next frame -+ if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8)) -+ { -+ // Send it next frame -+ if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc -+ { -+ fiq_print(FIQDBG_SCHED, "NYT:SEND"); -+ fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -+ } -+ else -+ { -+ // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP) -+ complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -+ fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -+ } -+ hcint_saved[channel].b.chhltd = 0; -+ hcint_saved[channel].b.nyet = 0; -+ } -+ else -+ { -+ queued_port[channel] = 0; -+ fiq_print(FIQDBG_ERR, "NYETERR2"); -+ //BUG(); -+ } -+ } -+ } -+ else -+ { -+ /* -+ * If we have any of NAK, ACK, Datatlgerr active on a -+ * non-split channel, the sole reason is to reset error -+ * counts for a previously broken transaction. The FIQ -+ * will thrash on NAK IN and ACK OUT in particular so -+ * handle it "once" and allow the IRQ to do the rest. -+ */ -+ hcint.d32 &= hcintmsk.d32; -+ if(hcint.b.nak) -+ { -+ hcintmsk.b.nak = 0; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -+ } -+ if (hcint.b.ack) -+ { -+ hcintmsk.b.ack = 0; -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -+ } -+ } -+ -+ // Clear the interrupt, this will also clear the HAINT bit -+ FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32); -+ return hcint_saved[channel].d32 == 0; -+} -+ -+gintsts_data_t gintsts; -+gintmsk_data_t gintmsk; -+// triggered: The set of interrupts that were triggered -+// handled: The set of interrupts that have been handled (no IRQ is -+// required) -+// keep: The set of interrupts we want to keep unmasked even though we -+// want to trigger an IRQ to handle it (SOF and HCINTR) -+gintsts_data_t triggered, handled, keep; -+hfnum_data_t hfnum; -+ -+void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void) -+{ -+ -+ /* entry takes care to store registers we will be treading on here */ -+ asm __volatile__ ( -+ "mov ip, sp ;" -+ /* stash FIQ and normal regs */ -+ "stmdb sp!, {r0-r12, lr};" -+ /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ -+ "sub fp, ip, #512 ;" -+ ); -+ -+ // Cannot put local variables at the beginning of the function -+ // because otherwise 'C' will play with the stack pointer. any locals -+ // need to be inside the following block -+ do -+ { -+ fiq_done++; -+ gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14); -+ gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18); -+ hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408); -+ triggered.d32 = gintsts.d32 & gintmsk.d32; -+ handled.d32 = 0; -+ keep.d32 = 0; -+ fiq_print(FIQDBG_INT, "FIQ "); -+ fiq_print(FIQDBG_INT, "%08x", gintsts.d32); -+ fiq_print(FIQDBG_INT, "%08x", gintmsk.d32); -+ if(gintsts.d32) -+ { -+ // If port enabled -+ if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5) -+ { -+ if(gintsts.b.sofintr) -+ { -+ if(fiq_sof_handle(hfnum)) -+ { -+ handled.b.sofintr = 1; /* Handled in FIQ */ -+ } -+ else -+ { -+ /* Keer interrupt unmasked */ -+ keep.b.sofintr = 1; -+ } -+ { -+ // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing -+ // a start of frame interrupt -+ gintsts_data_t gintsts = { .b.sofintr = 1 }; -+ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -+ } -+ } -+ -+ if(fiq_split_enable && gintsts.b.hcintr) -+ { -+ int i; -+ haint_data_t haint; -+ haintmsk_data_t haintmsk; -+ -+ haint.d32 = FIQ_READ(dwc_regs_base + 0x414); -+ haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418); -+ haint.d32 &= haintmsk.d32; -+ haint_saved.d32 |= haint.d32; -+ -+ fiq_print(FIQDBG_INT, "hcintr"); -+ fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414)); -+ -+ // Go through each channel that has an enabled interrupt -+ for(i = 0; i < 16; i++) -+ if((haint.d32 >> i) & 1) -+ if(fiq_hcintr_handle(i, hfnum)) -+ haint_saved.d32 &= ~(1 << i); /* this was handled */ -+ -+ /* If we've handled all host channel interrupts then don't trigger the interrupt */ -+ if(haint_saved.d32 == 0) -+ { -+ handled.b.hcintr = 1; -+ } -+ else -+ { -+ /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */ -+ keep.b.hcintr = 1; -+ } -+ -+ { -+ gintsts_data_t gintsts = { .b.hcintr = 1 }; -+ -+ // Always clear the channel interrupt -+ FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -+ } -+ } -+ } -+ else -+ { -+ last_sof = -1; -+ } -+ } -+ -+ // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep -+ gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32)); -+ // Save those that were triggered but not handled -+ gintsts_saved.d32 |= triggered.d32 & ~handled.d32; -+ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -+ -+ // Clear and save any unhandled interrupts and trigger the interrupt -+ if(gintsts_saved.d32) -+ { -+ /* To enable the MPHI interrupt (INT 32) -+ */ -+ FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); -+ FIQ_WRITE( c_mphi_regs.outddb, (1 << 29)); -+ -+ mphi_int_count++; -+ } -+ } -+ while(0); -+ -+ mb(); -+ -+ /* exit back to normal mode restoring everything */ -+ asm __volatile__ ( -+ /* return FIQ regs back to pristine state -+ * and get normal regs back -+ */ -+ "ldmia sp!, {r0-r12, lr};" -+ -+ /* return */ -+ "subs pc, lr, #4;" -+ ); -+} -+ - /** This function handles interrupts for the HCD. */ - int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int retval = 0; -+ static int last_time; - - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - gintsts_data_t gintsts; -+ gintmsk_data_t gintmsk; -+ hfnum_data_t hfnum; -+ - #ifdef DEBUG - dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; - -- //GRAYG: debugging -- if (NULL == global_regs) { -- DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p " -- "core_if=%p\n", -- dwc_otg_hcd, global_regs); -- return retval; -- } - #endif - -+ gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); -+ gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -+ - /* Exit from ISR if core is hibernated */ - if (core_if->hibernation_suspend == 1) { -- return retval; -+ goto exit_handler_routine; - } - DWC_SPINLOCK(dwc_otg_hcd->lock); - /* Check if HOST Mode */ - if (dwc_otg_is_host_mode(core_if)) { -- gintsts.d32 = dwc_otg_read_core_intr(core_if); -+ local_fiq_disable(); -+ gintmsk.d32 |= gintsts_saved.d32; -+ gintsts.d32 |= gintsts_saved.d32; -+ gintsts_saved.d32 = 0; -+ local_fiq_enable(); - if (!gintsts.d32) { -- DWC_SPINUNLOCK(dwc_otg_hcd->lock); -- return 0; -+ goto exit_handler_routine; - } -+ gintsts.d32 &= gintmsk.d32; -+ - #ifdef DEBUG -+ // We should be OK doing this because the common interrupts should already have been serviced - /* Don't print debug message in the interrupt handler on SOF */ - #ifndef DEBUG_SOF - if (gintsts.d32 != DWC_SOF_INTR_MASK) -@@ -88,10 +543,16 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n", - gintsts.d32, core_if); - #endif -- -- if (gintsts.b.sofintr) { -+ hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); -+ if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -+ { -+ /* Note, we should never get here if the FIQ is doing it's job properly*/ - retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); - } -+ else if (gintsts.b.sofintr) { -+ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); -+ } -+ - if (gintsts.b.rxstsqlvl) { - retval |= - dwc_otg_hcd_handle_rx_status_q_level_intr -@@ -106,7 +567,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - /** @todo Implement i2cintr handler. */ - } - if (gintsts.b.portintr) { -+ -+ gintmsk_data_t gintmsk = { .b.portintr = 1}; - retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); -+ DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); - } - if (gintsts.b.hcintr) { - retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); -@@ -138,11 +602,48 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - #endif - - } -+ -+exit_handler_routine: -+ -+ if (fiq_fix_enable) -+ { -+ local_fiq_disable(); -+ // Make sure that we don't clear the interrupt if we've still got pending work to do -+ if(gintsts_saved.d32 == 0) -+ { -+ /* Clear the MPHI interrupt */ -+ DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16)); -+ if (mphi_int_count >= 60) -+ { -+ DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16))); -+ while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17))) -+ ; -+ DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31)); -+ mphi_int_count = 0; -+ } -+ int_done++; -+ } -+ -+ // Unmask handled interrupts -+ FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -+ //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1); -+ -+ local_fiq_enable(); -+ -+ if((jiffies / HZ) > last_time) -+ { -+ /* Once a second output the fiq and irq numbers, useful for debug */ -+ last_time = jiffies / HZ; -+ DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done); -+ } -+ } -+ - DWC_SPINUNLOCK(dwc_otg_hcd->lock); - return retval; - } - - #ifdef DWC_TRACK_MISSED_SOFS -+ - #warning Compiling code to track missed SOFs - #define FRAME_NUM_ARRAY_SIZE 1000 - /** -@@ -188,7 +689,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - dwc_list_link_t *qh_entry; - dwc_otg_qh_t *qh; - dwc_otg_transaction_type_e tr_type; -- gintsts_data_t gintsts = {.d32 = 0 }; -+ int did_something = 0; -+ int32_t next_sched_frame = -1; - - hfnum.d32 = - DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -@@ -212,17 +714,31 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); - qh_entry = qh_entry->next; - if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { -+ - /* - * Move QH to the ready list to be executed next - * (micro)frame. - */ - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); -+ -+ did_something = 1; -+ } -+ else -+ { -+ if(next_sched_frame < 0 || dwc_frame_num_le(qh->sched_frame, next_sched_frame)) -+ { -+ next_sched_frame = qh->sched_frame; -+ } - } - } -+ -+ g_next_sched_frame = next_sched_frame; -+ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); -+ did_something = 1; - } - - /* Clear interrupt */ -@@ -511,6 +1027,15 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - - haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); - -+ // Overwrite with saved interrupts from fiq handler -+ if(fiq_split_enable) -+ { -+ local_fiq_disable(); -+ haint.d32 = haint_saved.d32; -+ haint_saved.d32 = 0; -+ local_fiq_enable(); -+ } -+ - for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { - if (haint.b2.chint & (1 << i)) { - retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i); -@@ -551,7 +1076,10 @@ static uint32_t get_actual_xfer_length(dwc_hc_t * hc, - *short_read = (hctsiz.b.xfersize != 0); - } - } else if (hc->qh->do_split) { -- length = qtd->ssplit_out_xfer_count; -+ if(fiq_split_enable) -+ length = split_out_xfersize[hc->hc_num]; -+ else -+ length = qtd->ssplit_out_xfer_count; - } else { - length = hc->xfer_len; - } -@@ -595,7 +1123,6 @@ static int update_urb_state_xfer_comp(dwc_hc_t * hc, - DWC_OTG_HC_XFER_COMPLETE, - &short_read); - -- - /* non DWORD-aligned buffer case handling. */ - if (hc->align_buff && xfer_length && hc->ep_is_in) { - dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, -@@ -797,11 +1324,24 @@ static void release_channel(dwc_otg_hcd_t * hcd, - dwc_otg_transaction_type_e tr_type; - int free_qtd; - dwc_irqflags_t flags; -- dwc_spinlock_t *channel_lock = DWC_SPINLOCK_ALLOC(); -+ dwc_spinlock_t *channel_lock = hcd->channel_lock; -+#ifdef FIQ_DEBUG -+ int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0; -+#endif -+ int hog_port = 0; - - DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", - __func__, hc->hc_num, halt_status, hc->xfer_len); - -+ if(fiq_split_enable && hc->do_split) { -+ if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { -+ if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || -+ hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { -+ hog_port = 1; -+ } -+ } -+ } -+ - switch (halt_status) { - case DWC_OTG_HC_XFER_URB_COMPLETE: - free_qtd = 1; -@@ -876,15 +1416,32 @@ static void release_channel(dwc_otg_hcd_t * hcd, - - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - hcd->available_host_channels++; -+ fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - } - -+ if(fiq_split_enable && hc->do_split) -+ { -+ if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr))) -+ { -+ fiq_print(FIQDBG_ERR, "PRTNOTAL"); -+ //BUG(); -+ } -+ if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC || -+ hc->ep_type == DWC_OTG_EP_TYPE_INTR)) { -+ hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr); -+#ifdef FIQ_DEBUG -+ hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1; -+#endif -+ fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp); -+ } -+ } -+ - /* Try to queue more transfers now that there's a free channel. */ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { - dwc_otg_hcd_queue_transactions(hcd, tr_type); - } -- DWC_SPINLOCK_FREE(channel_lock); - } - - /** -@@ -1295,6 +1852,17 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - "NAK Received--\n", hc->hc_num); - - /* -+ * When we get bulk NAKs then remember this so we holdoff on this qh until -+ * the beginning of the next frame -+ */ -+ switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { -+ case UE_BULK: -+ case UE_CONTROL: -+ if (nak_holdoff_enable) -+ hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); -+ } -+ -+ /* - * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and - * interrupt. Re-start the SSPLIT transfer. - */ -@@ -1316,7 +1884,11 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - * transfers in DMA mode for the sole purpose of - * resetting the error count after a transaction error - * occurs. The core will continue transferring data. -+ * Disable other interrupts unmasked for the same -+ * reason. - */ -+ disable_hc_int(hc_regs, datatglerr); -+ disable_hc_int(hc_regs, ack); - qtd->error_count = 0; - goto handle_nak_done; - } -@@ -1428,6 +2000,15 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd, - halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); - } - } else { -+ /* -+ * An unmasked ACK on a non-split DMA transaction is -+ * for the sole purpose of resetting error counts. Disable other -+ * interrupts unmasked for the same reason. -+ */ -+ if(hcd->core_if->dma_enable) { -+ disable_hc_int(hc_regs, datatglerr); -+ disable_hc_int(hc_regs, nak); -+ } - qtd->error_count = 0; - - if (hc->qh->ping_state) { -@@ -1490,8 +2071,10 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, - hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { - int frnum = dwc_otg_hcd_get_frame_number(hcd); - -+ // With the FIQ running we only ever see the failed NYET - if (dwc_full_frame_num(frnum) != -- dwc_full_frame_num(hc->qh->sched_frame)) { -+ dwc_full_frame_num(hc->qh->sched_frame) || -+ fiq_split_enable) { - /* - * No longer in the same full speed frame. - * Treat this as a transaction error. -@@ -1778,13 +2361,28 @@ static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd, - dwc_otg_qtd_t * qtd) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " -- "Data Toggle Error--\n", hc->hc_num); -+ "Data Toggle Error on %s transfer--\n", -+ hc->hc_num, (hc->ep_is_in ? "IN" : "OUT")); - -- if (hc->ep_is_in) { -+ /* Data toggles on split transactions cause the hc to halt. -+ * restart transfer */ -+ if(hc->qh->do_split) -+ { -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ } else if (hc->ep_is_in) { -+ /* An unmasked data toggle error on a non-split DMA transaction is -+ * for the sole purpose of resetting error counts. Disable other -+ * interrupts unmasked for the same reason. -+ */ -+ if(hcd->core_if->dma_enable) { -+ disable_hc_int(hc_regs, ack); -+ disable_hc_int(hc_regs, nak); -+ } - qtd->error_count = 0; -- } else { -- DWC_ERROR("Data Toggle Error on OUT transfer," -- "channel %d\n", hc->hc_num); - } - - disable_hc_int(hc_regs, datatglerr); -@@ -1862,10 +2460,10 @@ static inline int halt_status_ok(dwc_otg_hcd_t * hcd, - static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd) -+ dwc_otg_qtd_t * qtd, -+ hcint_data_t hcint, -+ hcintmsk_data_t hcintmsk) - { -- hcint_data_t hcint; -- hcintmsk_data_t hcintmsk; - int out_nak_enh = 0; - - /* For core with OUT NAK enhancement, the flow for high- -@@ -1897,8 +2495,11 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - } - - /* Read the HCINTn register to determine the cause for the halt. */ -- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ if(!fiq_split_enable) -+ { -+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -+ } - - if (hcint.b.xfercomp) { - /** @todo This is here because of a possible hardware bug. Spec -@@ -1937,6 +2538,8 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - handle_hc_babble_intr(hcd, hc, hc_regs, qtd); - } else if (hcint.b.frmovrun) { - handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.datatglerr) { -+ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); - } else if (!out_nak_enh) { - if (hcint.b.nyet) { - /* -@@ -1986,12 +2589,24 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - DWC_READ_REG32(&hcd-> - core_if->core_global_regs-> - gintsts)); -+ /* Failthrough: use 3-strikes rule */ -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); - } - - } - } else { - DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", - hcint.d32); -+ /* Failthrough: use 3-strikes rule */ -+ qtd->error_count++; -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ update_urb_state_xfer_intr(hc, hc_regs, -+ qtd->urb, qtd, DWC_OTG_HC_XFER_XACT_ERR); -+ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); - } - } - -@@ -2009,13 +2624,15 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd) -+ dwc_otg_qtd_t * qtd, -+ hcint_data_t hcint, -+ hcintmsk_data_t hcintmsk) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " - "Channel Halted--\n", hc->hc_num); - - if (hcd->core_if->dma_enable) { -- handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); -+ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk); - } else { - #ifdef DEBUG - if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { -@@ -2032,7 +2649,7 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - { - int retval = 0; -- hcint_data_t hcint; -+ hcint_data_t hcint, hcint_orig; - hcintmsk_data_t hcintmsk; - dwc_hc_t *hc; - dwc_otg_hc_regs_t *hc_regs; -@@ -2042,15 +2659,33 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - - hc = dwc_otg_hcd->hc_ptr_array[num]; - hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; -+ if(hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { -+ /* We are responding to a channel disable. Driver -+ * state is cleared - our qtd has gone away. -+ */ -+ release_channel(dwc_otg_hcd, hc, NULL, hc->halt_status); -+ return 1; -+ } - qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); - - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcint_orig = hcint; - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); - DWC_DEBUGPL(DBG_HCDV, - " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", - hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); - hcint.d32 = hcint.d32 & hcintmsk.d32; - -+ if(fiq_split_enable) -+ { -+ // replace with the saved interrupts from the fiq handler -+ local_fiq_disable(); -+ hcint_orig.d32 = hcint_saved[num].d32; -+ hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32; -+ hcint_saved[num].d32 = 0; -+ local_fiq_enable(); -+ } -+ - if (!dwc_otg_hcd->core_if->dma_enable) { - if (hcint.b.chhltd && hcint.d32 != 0x2) { - hcint.b.chhltd = 0; -@@ -2068,7 +2703,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - hcint.b.nyet = 0; - } - if (hcint.b.chhltd) { -- retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); -+ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]); - } - if (hcint.b.ahberr) { - retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); -@@ -2080,7 +2715,8 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.ack) { -- retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); -+ if(!hcint.b.chhltd) -+ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.nyet) { - retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); -@@ -2102,5 +2738,4 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - - return retval; - } -- - #endif /* DWC_DEVICE_ONLY */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index e4787f5..ee8eec9 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -1,3 +1,4 @@ -+ - /* ========================================================================== - * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ - * $Revision: #20 $ -@@ -50,6 +51,7 @@ - #include - #include - #include -+#include - #include - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) - #include <../drivers/usb/core/hcd.h> -@@ -67,6 +69,8 @@ - #include "dwc_otg_dbg.h" - #include "dwc_otg_driver.h" - #include "dwc_otg_hcd.h" -+#include "dwc_otg_mphi_fix.h" -+ - /** - * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is - * qualified with its direction (possible 32 endpoints per device). -@@ -76,6 +80,8 @@ - - static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; - -+extern bool fiq_fix_enable; -+ - /** @name Linux HC Driver API Functions */ - /** @{ */ - /* manage i/o requests, device state */ -@@ -259,13 +265,15 @@ static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, - - /** - * Sets the final status of an URB and returns it to the device driver. Any -- * required cleanup of the URB is performed. -+ * required cleanup of the URB is performed. The HCD lock should be held on -+ * entry. - */ - static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) - { - struct urb *urb = (struct urb *)urb_handle; -- -+ urb_tq_entry_t *new_entry; -+ int rc = 0; - if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { - DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", - __func__, urb, usb_pipedevice(urb->pipe), -@@ -279,7 +287,7 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - } - } - } -- -+ new_entry = DWC_ALLOC_ATOMIC(sizeof(urb_tq_entry_t)); - urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); - /* Convert status value. */ - switch (status) { -@@ -301,6 +309,9 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - case -DWC_E_OVERFLOW: - status = -EOVERFLOW; - break; -+ case -DWC_E_SHUTDOWN: -+ status = -ESHUTDOWN; -+ break; - default: - if (status) { - DWC_PRINTF("Uknown urb status %d\n", status); -@@ -342,18 +353,33 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - } - - DWC_FREE(dwc_otg_urb); -- -+ if (!new_entry) { -+ DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); -+ urb->status = -EPROTO; -+ /* don't schedule the tasklet - -+ * directly return the packet here with error. */ - #if USB_URB_EP_LINKING -- usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); - #endif -- DWC_SPINUNLOCK(hcd->lock); - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) -- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); - #else -- usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); -+ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); - #endif -- DWC_SPINLOCK(hcd->lock); -- -+ } else { -+ new_entry->urb = urb; -+#if USB_URB_EP_LINKING -+ rc = usb_hcd_check_unlink_urb(dwc_otg_hcd_to_hcd(hcd), urb, urb->status); -+ if(0 == rc) { -+ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); -+ } -+#endif -+ if(0 == rc) { -+ DWC_TAILQ_INSERT_TAIL(&hcd->completed_urb_list, new_entry, -+ urb_tq_entries); -+ DWC_TASK_HI_SCHEDULE(hcd->completion_tasklet); -+ } -+ } - return 0; - } - -@@ -366,6 +392,16 @@ static struct dwc_otg_hcd_function_ops hcd_fops = { - .get_b_hnp_enable = _get_b_hnp_enable, - }; - -+static struct fiq_handler fh = { -+ .name = "usb_fiq", -+}; -+struct fiq_stack_s { -+ int magic1; -+ uint8_t stack[2048]; -+ int magic2; -+} fiq_stack; -+ -+extern mphi_regs_t c_mphi_regs; - /** - * Initializes the HCD. This function allocates memory for and initializes the - * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the -@@ -379,6 +415,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); - int retval = 0; - u64 dmamask; -+ struct pt_regs regs; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); - -@@ -396,6 +433,20 @@ int hcd_init(dwc_bus_dev_t *_dev) - pci_set_consistent_dma_mask(_dev, dmamask); - #endif - -+ if (fiq_fix_enable) -+ { -+ // Set up fiq -+ claim_fiq(&fh); -+ set_fiq_handler(__FIQ_Branch, 4); -+ memset(®s,0,sizeof(regs)); -+ regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq; -+ regs.ARM_r9 = (long)0; -+ regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4; -+ set_fiq_regs(®s); -+ fiq_stack.magic1 = 0xdeadbeef; -+ fiq_stack.magic2 = 0xaa995566; -+ } -+ - /* - * Allocate memory for the base HCD plus the DWC OTG HCD. - * Initialize the base HCD. -@@ -415,6 +466,30 @@ int hcd_init(dwc_bus_dev_t *_dev) - - hcd->regs = otg_dev->os_dep.base; - -+ if (fiq_fix_enable) -+ { -+ volatile extern void *dwc_regs_base; -+ -+ //Set the mphi periph to the required registers -+ c_mphi_regs.base = otg_dev->os_dep.mphi_base; -+ c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ -+ dwc_regs_base = otg_dev->os_dep.base; -+ -+ //Enable mphi peripheral -+ writel((1<<31),c_mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(c_mphi_regs.ctrl) & 0x80000000) -+ DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n"); -+ else -+ DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ } - /* Initialize the DWC OTG HCD. */ - dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); - if (!dwc_otg_hcd) { -@@ -607,9 +682,7 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) - struct usb_host_endpoint *ep = urb->ep; - #endif --#if USB_URB_EP_LINKING - dwc_irqflags_t irqflags; --#endif - void **ref_ep_hcpriv = &ep->hcpriv; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); - dwc_otg_hcd_urb_t *dwc_otg_urb; -@@ -661,9 +734,8 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - if(dwc_otg_urb == NULL) - return -ENOMEM; - -- urb->hcpriv = dwc_otg_urb; -- if (!dwc_otg_urb && urb->number_of_packets) -- return -ENOMEM; -+ if (!dwc_otg_urb && urb->number_of_packets) -+ return -ENOMEM; - - dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), ep_type, -@@ -703,37 +775,42 @@ static int dwc_otg_urb_enqueue(struct usb_hcd *hcd, - iso_frame_desc[i].length); - } - -+ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -+ urb->hcpriv = dwc_otg_urb; - #if USB_URB_EP_LINKING -- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); - retval = usb_hcd_link_urb_to_ep(hcd, urb); -- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); - if (0 == retval) - #endif -- { -- retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -- /*(dwc_otg_qh_t **)*/ -- ref_ep_hcpriv, -- mem_flags == GFP_ATOMIC ? 1 : 0); -- if (0 == retval) { -- if (alloc_bandwidth) { -- allocate_bus_bandwidth(hcd, -- dwc_otg_hcd_get_ep_bandwidth( -- dwc_otg_hcd, *ref_ep_hcpriv), -- urb); -- } -- } else { -+ { -+ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, -+ /*(dwc_otg_qh_t **)*/ -+ ref_ep_hcpriv, 1); -+ if (0 == retval) { -+ if (alloc_bandwidth) { -+ allocate_bus_bandwidth(hcd, -+ dwc_otg_hcd_get_ep_bandwidth( -+ dwc_otg_hcd, *ref_ep_hcpriv), -+ urb); -+ } -+ } else { -+ DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); - #if USB_URB_EP_LINKING -- dwc_irqflags_t irqflags; -- DWC_DEBUGPL(DBG_HCD, "DWC OTG dwc_otg_hcd_urb_enqueue failed rc %d\n", retval); -- DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &irqflags); -- usb_hcd_unlink_urb_from_ep(hcd, urb); -- DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); -+ usb_hcd_unlink_urb_from_ep(hcd, urb); - #endif -- if (retval == -DWC_E_NO_DEVICE) { -- retval = -ENODEV; -- } -- } -- } -+ DWC_FREE(dwc_otg_urb); -+ urb->hcpriv = NULL; -+ if (retval == -DWC_E_NO_DEVICE) -+ retval = -ENODEV; -+ } -+ } -+#if USB_URB_EP_LINKING -+ else -+ { -+ DWC_FREE(dwc_otg_urb); -+ urb->hcpriv = NULL; -+ } -+#endif -+ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, irqflags); - return retval; - } - -@@ -777,6 +854,8 @@ static int dwc_otg_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) - usb_hcd_unlink_urb_from_ep(hcd, urb); - #endif - DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); -+ -+ - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28) - usb_hcd_giveback_urb(hcd, urb); - #else -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 0a1cbb7..5c22b6c 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -41,6 +41,7 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" -+#include "dwc_otg_mphi_fix.h" - - extern bool microframe_schedule; - -@@ -182,6 +183,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) - if (microframe_schedule) - qh->speed = dev_speed; - -+ qh->nak_frame = 0xffff; - - if (((dev_speed == USB_SPEED_LOW) || - (dev_speed == USB_SPEED_FULL)) && -@@ -191,6 +193,7 @@ void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, dwc_otg_hcd_urb_t * urb) - dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, - hub_port); - qh->do_split = 1; -+ qh->skip_count = 0; - } - - if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { -@@ -573,6 +576,9 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - return status; - } - -+ -+extern int g_next_sched_frame, g_np_count, g_np_sent; -+ - /** - * Schedules an interrupt or isochronous transfer in the periodic schedule. - * -@@ -631,8 +637,13 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); - } - else { -- /* Always start in the inactive schedule. */ -- DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); -+ if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame)) -+ { -+ g_next_sched_frame = qh->sched_frame; -+ -+ } -+ /* Always start in the inactive schedule. */ -+ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); - } - - if (!microframe_schedule) { -@@ -646,6 +657,7 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - return status; - } - -+ - /** - * This function adds a QH to either the non periodic or periodic schedule if - * it is not already in the schedule. If the QH is already in the schedule, no -@@ -668,6 +680,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - /* Always start in the inactive schedule. */ - DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, - &qh->qh_list_entry); -+ g_np_count++; - } else { - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { -@@ -727,6 +740,9 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->non_periodic_qh_ptr->next; - } - DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); -+ -+ // If we've removed the last non-periodic entry then there are none left! -+ g_np_count = g_np_sent; - } else { - deschedule_periodic(hcd, qh); - hcd->periodic_qh_count--; -@@ -755,6 +771,24 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - int sched_next_periodic_split) - { - if (dwc_qh_is_non_per(qh)) { -+ -+ dwc_otg_qh_t *qh_tmp; -+ dwc_list_link_t *qh_list; -+ DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) -+ { -+ qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry); -+ if(qh_tmp == qh) -+ { -+ /* -+ * FIQ is being disabled because this one nevers gets a np_count increment -+ * This is still not absolutely correct, but it should fix itself with -+ * just an unnecessary extra interrupt -+ */ -+ g_np_sent = g_np_count; -+ } -+ } -+ -+ - dwc_otg_hcd_qh_remove(hcd, qh); - if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { - /* Add back to inactive non-periodic schedule. */ -@@ -768,6 +802,7 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - if (sched_next_periodic_split) { - - qh->sched_frame = frame_number; -+ - if (dwc_frame_num_le(frame_number, - dwc_frame_num_inc - (qh->start_split_frame, -@@ -816,6 +851,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); - } else { -+ if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame)) -+ { -+ g_next_sched_frame = qh->sched_frame; -+ } -+ - DWC_LIST_MOVE_HEAD - (&hcd->periodic_sched_inactive, - &qh->qh_list_entry); -@@ -880,6 +920,7 @@ void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb) - * QH to place the QTD into. If it does not find a QH, then it will create a - * new QH. If the QH to which the QTD is added is not currently scheduled, it - * is placed into the proper schedule based on its EP type. -+ * HCD lock must be held and interrupts must be disabled on entry - * - * @param[in] qtd The QTD to add - * @param[in] hcd The DWC HCD structure -@@ -892,8 +933,6 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh, int atomic_alloc) - { - int retval = 0; -- dwc_irqflags_t flags; -- - dwc_otg_hcd_urb_t *urb = qtd->urb; - - /* -@@ -903,18 +942,16 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - if (*qh == NULL) { - *qh = dwc_otg_hcd_qh_create(hcd, urb, atomic_alloc); - if (*qh == NULL) { -- retval = -1; -+ retval = -DWC_E_NO_MEMORY; - goto done; - } - } -- DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); - retval = dwc_otg_hcd_qh_add(hcd, *qh); - if (retval == 0) { - DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, - qtd_list_entry); -+ qtd->qh = *qh; - } -- DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -- - done: - - return retval; -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -new file mode 100755 -index 0000000..50b94a8 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -@@ -0,0 +1,113 @@ -+#include "dwc_otg_regs.h" -+#include "dwc_otg_dbg.h" -+ -+void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n" -+ "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n" -+ "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n" -+ "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n" -+ "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n" -+ "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n" -+ "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n" -+ "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n" -+ "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -+ function_name, -+ gintsts.b.curmode, -+ gintsts.b.modemismatch, -+ gintsts.b.otgintr, -+ gintsts.b.sofintr, -+ gintsts.b.rxstsqlvl, -+ gintsts.b.nptxfempty, -+ gintsts.b.ginnakeff, -+ gintsts.b.goutnakeff, -+ gintsts.b.ulpickint, -+ gintsts.b.i2cintr, -+ gintsts.b.erlysuspend, -+ gintsts.b.usbsuspend, -+ gintsts.b.usbreset, -+ gintsts.b.enumdone, -+ gintsts.b.isooutdrop, -+ gintsts.b.eopframe, -+ gintsts.b.restoredone, -+ gintsts.b.epmismatch, -+ gintsts.b.inepint, -+ gintsts.b.outepintr, -+ gintsts.b.incomplisoin, -+ gintsts.b.incomplisoout, -+ gintsts.b.fetsusp, -+ gintsts.b.resetdet, -+ gintsts.b.portintr, -+ gintsts.b.hcintr, -+ gintsts.b.ptxfempty, -+ gintsts.b.lpmtranrcvd, -+ gintsts.b.conidstschng, -+ gintsts.b.disconnect, -+ gintsts.b.sessreqintr, -+ gintsts.b.wkupintr); -+ return; -+} -+ -+void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n" -+ "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n" -+ "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n" -+ "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n" -+ "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n" -+ "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n" -+ "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n" -+ "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n" -+ "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -+ function_name, -+ gintmsk.b.modemismatch, -+ gintmsk.b.otgintr, -+ gintmsk.b.sofintr, -+ gintmsk.b.rxstsqlvl, -+ gintmsk.b.nptxfempty, -+ gintmsk.b.ginnakeff, -+ gintmsk.b.goutnakeff, -+ gintmsk.b.ulpickint, -+ gintmsk.b.i2cintr, -+ gintmsk.b.erlysuspend, -+ gintmsk.b.usbsuspend, -+ gintmsk.b.usbreset, -+ gintmsk.b.enumdone, -+ gintmsk.b.isooutdrop, -+ gintmsk.b.eopframe, -+ gintmsk.b.restoredone, -+ gintmsk.b.epmismatch, -+ gintmsk.b.inepintr, -+ gintmsk.b.outepintr, -+ gintmsk.b.incomplisoin, -+ gintmsk.b.incomplisoout, -+ gintmsk.b.fetsusp, -+ gintmsk.b.resetdet, -+ gintmsk.b.portintr, -+ gintmsk.b.hcintr, -+ gintmsk.b.ptxfempty, -+ gintmsk.b.lpmtranrcvd, -+ gintmsk.b.conidstschng, -+ gintmsk.b.disconnect, -+ gintmsk.b.sessreqintr, -+ gintmsk.b.wkupintr); -+ return; -+} -+ -+void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name) -+{ -+ DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n" -+ "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n" -+ "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n" -+ "mvic: %1i\n", -+ function_name, -+ gotgint.b.sesenddet, -+ gotgint.b.sesreqsucstschng, -+ gotgint.b.hstnegsucstschng, -+ gotgint.b.hstnegdet, -+ gotgint.b.adevtoutchng, -+ gotgint.b.debdone, -+ gotgint.b.mvic); -+ -+ return; -+} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -new file mode 100755 -index 0000000..ca17379 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -@@ -0,0 +1,48 @@ -+#ifndef __DWC_OTG_MPHI_FIX_H__ -+#define __DWC_OTG_MPHI_FIX_H__ -+#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_)) -+#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_)) -+ -+typedef struct { -+ volatile void* base; -+ volatile void* ctrl; -+ volatile void* outdda; -+ volatile void* outddb; -+ volatile void* intstat; -+} mphi_regs_t; -+ -+void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name); -+void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name); -+void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name); -+ -+extern gintsts_data_t gintsts_saved; -+ -+#ifdef DEBUG -+#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__) -+#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__) -+#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__) -+ -+#else -+#define DWC_DBG_PRINT_CORE_INT(_arg_) -+#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) -+#define DWC_DBG_PRINT_OTG_INT(_arg_) -+ -+#endif -+ -+typedef enum { -+ FIQDBG_SCHED = (1 << 0), -+ FIQDBG_INT = (1 << 1), -+ FIQDBG_ERR = (1 << 2), -+ FIQDBG_PORTHUB = (1 << 3), -+} FIQDBG_T; -+ -+void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...); -+#ifdef FIQ_DEBUG -+#define fiq_print _fiq_print -+#else -+#define fiq_print(x, y, ...) -+#endif -+ -+extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable; -+ -+#endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -index e46d9bb..6b2c7d0 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_os_dep.h -@@ -97,6 +97,9 @@ typedef struct os_dependent { - /** Register offset for Diagnostic API */ - uint32_t reg_offset; - -+ /** Base address for MPHI peripheral */ -+ void *mphi_base; -+ - #ifdef LM_INTERFACE - struct lm_device *lmdev; - #elif defined(PCI_INTERFACE) -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -index 1b1f83c..c8590b5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c -@@ -4276,7 +4276,7 @@ do { \ - && (pcd->ep0state == EP0_OUT_DATA_PHASE)) - status.d32 = core_if->dev_if->out_desc_addr->status.d32; - if (pcd->ep0state == EP0_OUT_STATUS_PHASE) -- status.d32 = status.d32 = core_if->dev_if-> -+ status.d32 = core_if->dev_if-> - out_desc_addr->status.d32; - - if (status.b.sr) { - -From 5cd92267e90f236fd69402f1c98eef1613d2688a Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 19 Mar 2014 12:58:23 +0000 -Subject: [PATCH 027/172] dwc_otg: fiq_fsm: Base commit for driver rewrite - -This commit removes the previous FIQ fixes entirely and adds fiq_fsm. - -This rewrite features much more complete support for split transactions -and takes into account several OTG hardware bugs. High-speed -isochronous transactions are also capable of being performed by fiq_fsm. - -All driver options have been removed and replaced with: - - dwc_otg.fiq_enable (bool) - - dwc_otg.fiq_fsm_enable (bool) - - dwc_otg.fiq_fsm_mask (bitmask) - - dwc_otg.nak_holdoff (unsigned int) - -Defaults are specified such that fiq_fsm behaves similarly to the -previously implemented FIQ fixes. - -fiq_fsm: Push error recovery into the FIQ when fiq_fsm is used - -If the transfer associated with a QTD failed due to a bus error, the HCD -would retry the transfer up to 3 times (implementing the USB2.0 -three-strikes retry in software). - -Due to the masking mechanism used by fiq_fsm, it is only possible to pass -a single interrupt through to the HCD per-transfer. - -In this instance host channels would fall off the radar because the error -reset would function, but the subsequent channel halt would be lost. - -Push the error count reset into the FIQ handler. - -fiq_fsm: Implement timeout mechanism - -For full-speed endpoints with a large packet size, interrupt latency -runs the risk of the FIQ starting a transaction too late in a full-speed -frame. If the device is still transmitting data when EOF2 for the -downstream frame occurs, the hub will disable the port. This change is -not reflected in the hub status endpoint and the device becomes -unresponsive. - -Prevent high-bandwidth transactions from being started too late in a -frame. The mechanism is not guaranteed: a combination of bit stuffing -and hub latency may still result in a device overrunning. - -fiq_fsm: fix bounce buffer utilisation for Isochronous OUT - -Multi-packet isochronous OUT transactions were subject to a few bounday -bugs. Fix them. - -Audio playback is now much more robust: however, an issue stands with -devices that have adaptive sinks - ALSA plays samples too fast. - -dwc_otg: Return full-speed frame numbers in HS mode - -The frame counter increments on every *microframe* in high-speed mode. -Most device drivers expect this number to be in full-speed frames - this -caused considerable confusion to e.g. snd_usb_audio which uses the -frame counter to estimate the number of samples played. - -fiq_fsm: save PID on completion of interrupt OUT transfers - -Also add edge case handling for interrupt transports. - -Note that for periodic split IN, data toggles are unimplemented in the -OTG host hardware - it unconditionally accepts any PID. - -fiq_fsm: add missing case for fiq_fsm_tt_in_use() - -Certain combinations of bitrate and endpoint activity could -result in a periodic transaction erroneously getting started -while the previous Isochronous OUT was still active. - -fiq_fsm: clear hcintmsk for aborted transactions - -Prevents the FIQ from erroneously handling interrupts -on a timed out channel. - -fiq_fsm: enable by default - -fiq_fsm: fix dequeues for non-periodic split transactions - -If a dequeue happened between the SSPLIT and CSPLIT phases of the -transaction, the HCD would never receive an interrupt. - -fiq_fsm: Disable by default - -fiq_fsm: Handle HC babble errors - -The HCTSIZ transfer size field raises a babble interrupt if -the counter wraps. Handle the resulting interrupt in this case. - -dwc_otg: fix interrupt registration for fiq_enable=0 - -Additionally make the module parameter conditional for wherever -hcd->fiq_state is touched. - -fiq_fsm: Enable by default ---- - arch/arm/mach-bcm2708/bcm2708.c | 24 +- - drivers/usb/host/dwc_otg/Makefile | 3 +- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 47 +- - drivers/usb/host/dwc_otg/dwc_otg_driver.c | 51 +- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 1290 ++++++++++++++++++++++++++ - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 353 +++++++ - drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S | 80 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 775 +++++++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 11 + - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 999 ++++++++++---------- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 113 ++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 41 +- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c | 113 --- - drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h | 48 - - drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 8 +- - 15 files changed, 2991 insertions(+), 965 deletions(-) - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h - create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S - delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c - delete mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index dc59a6b..a740344 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -330,22 +330,13 @@ static struct resource bcm2708_usb_resources[] = { - .end = IRQ_HOSTPORT, - .flags = IORESOURCE_IRQ, - }, -+ [3] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, - }; - --bool fiq_fix_enable = true; -- --static struct resource bcm2708_usb_resources_no_fiq_fix[] = { -- [0] = { -- .start = USB_BASE, -- .end = USB_BASE + SZ_128K - 1, -- .flags = IORESOURCE_MEM, -- }, -- [1] = { -- .start = IRQ_USB, -- .end = IRQ_USB, -- .flags = IORESOURCE_IRQ, -- }, --}; - - static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - -@@ -701,11 +692,6 @@ void __init bcm2708_init(void) - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -- if (!fiq_fix_enable) -- { -- bcm2708_usb_device.resource = bcm2708_usb_resources_no_fiq_fix; -- bcm2708_usb_device.num_resources = ARRAY_SIZE(bcm2708_usb_resources_no_fiq_fix); -- } - bcm_register_device(&bcm2708_usb_device); - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); -diff --git a/drivers/usb/host/dwc_otg/Makefile b/drivers/usb/host/dwc_otg/Makefile -index a56f193..e7bdd12 100644 ---- a/drivers/usb/host/dwc_otg/Makefile -+++ b/drivers/usb/host/dwc_otg/Makefile -@@ -36,7 +36,8 @@ dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o - dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o - dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o - dwc_otg-objs += dwc_otg_adp.o --dwc_otg-objs += dwc_otg_mphi_fix.o -+dwc_otg-objs += dwc_otg_fiq_fsm.o -+dwc_otg-objs += dwc_otg_fiq_stub.o - ifneq ($(CFI),) - dwc_otg-objs += dwc_otg_cfi.o - endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 2f8b3bd..065807f 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -45,7 +45,6 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_pcd.h" - #include "dwc_otg_hcd.h" --#include "dwc_otg_mphi_fix.h" - - #ifdef DEBUG - inline const char *op_state_str(dwc_otg_core_if_t * core_if) -@@ -1319,7 +1318,7 @@ static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) - /** - * This function returns the Core Interrupt register. - */ --static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk) -+static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gintmsk_data_t *reenable_gintmsk, dwc_otg_hcd_t *hcd) - { - gahbcfg_data_t gahbcfg = {.d32 = 0 }; - gintsts_data_t gintsts; -@@ -1345,16 +1344,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - } - gintsts.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintsts); - gintmsk.d32 = DWC_READ_REG32(&core_if->core_global_regs->gintmsk); -- { -- unsigned long flags; -- -- // Re-enable the saved interrupts -- local_irq_save(flags); -+ if(fiq_enable) { - local_fiq_disable(); -- gintmsk.d32 |= gintmsk_common.d32; -- gintsts_saved.d32 &= ~gintmsk_common.d32; -- reenable_gintmsk->d32 = gintmsk.d32; -- local_irq_restore(flags); -+ /* Pull in the interrupts that the FIQ has masked */ -+ gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ -+ reenable_gintmsk->d32 |= gintmsk.d32; -+ reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ reenable_gintmsk->d32 &= gintmsk_common.d32; -+ local_fiq_enable(); - } - - gahbcfg.d32 = DWC_READ_REG32(&core_if->core_global_regs->gahbcfg); -@@ -1366,13 +1364,15 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - gintsts.d32, gintmsk.d32); - } - #endif -- if (!fiq_fix_enable){ -+ if (!fiq_enable){ - if (gahbcfg.b.glblintrmsk) - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); - else - return 0; -- } -- else { -+ } else { -+ /* Our IRQ kicker is no longer the USB hardware, it's the MPHI interface. -+ * Can't trust the global interrupt mask bit in this case. -+ */ - return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); - } - -@@ -1406,7 +1406,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - { - int retval = 0; - gintsts_data_t gintsts; -- gintmsk_data_t reenable_gintmsk; -+ gintmsk_data_t gintmsk_reenable = { .d32 = 0 }; - gpwrdn_data_t gpwrdn = {.d32 = 0 }; - dwc_otg_device_t *otg_dev = dev; - dwc_otg_core_if_t *core_if = otg_dev->core_if; -@@ -1428,7 +1428,10 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - - if (core_if->hibernation_suspend <= 0) { -- gintsts.d32 = dwc_otg_read_common_intr(core_if, &reenable_gintmsk); -+ /* read_common will have to poke the FIQ's saved mask. We must then clear this mask at the end -+ * of this handler - god only knows why it's done like this -+ */ -+ gintsts.d32 = dwc_otg_read_common_intr(core_if, &gintmsk_reenable, otg_dev->hcd); - - if (gintsts.b.modemismatch) { - retval |= dwc_otg_handle_mode_mismatch_intr(core_if); -@@ -1525,11 +1528,16 @@ int32_t dwc_otg_handle_common_intr(void *dev) - gintsts.b.portintr = 1; - DWC_WRITE_REG32(&core_if->core_global_regs->gintsts,gintsts.d32); - retval |= 1; -- reenable_gintmsk.b.portintr = 1; -+ gintmsk_reenable.b.portintr = 1; - - } -- -- DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, reenable_gintmsk.d32); -+ /* Did we actually handle anything? if so, unmask the interrupt */ -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); -+// fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); -+ if (retval) { -+ DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); -+ } - - } else { - DWC_DEBUGPL(DBG_ANY, "gpwrdn=%08x\n", gpwrdn.d32); -@@ -1583,6 +1591,5 @@ int32_t dwc_otg_handle_common_intr(void *dev) - } - if (core_if->lock) - DWC_SPINUNLOCK(core_if->lock); -- - return retval; - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -index f06c3d22..dc7cd32 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -@@ -56,6 +56,7 @@ - #include "dwc_otg_core_if.h" - #include "dwc_otg_pcd_if.h" - #include "dwc_otg_hcd_if.h" -+#include "dwc_otg_fiq_fsm.h" - - #define DWC_DRIVER_VERSION "3.00a 10-AUG-2012" - #define DWC_DRIVER_DESC "HS OTG USB Controller driver" -@@ -64,7 +65,6 @@ bool microframe_schedule=true; - - static const char dwc_driver_name[] = "dwc_otg"; - --extern void* dummy_send; - - extern int pcd_init( - #ifdef LM_INTERFACE -@@ -240,13 +240,14 @@ static struct dwc_otg_driver_module_params dwc_otg_module_params = { - .adp_enable = -1, - }; - --//Global variable to switch the fiq fix on or off (declared in bcm2708.c) --extern bool fiq_fix_enable; -+//Global variable to switch the fiq fix on or off -+bool fiq_enable = 1; - // Global variable to enable the split transaction fix --bool fiq_split_enable = true; --//Global variable to switch the nak holdoff on or off --bool nak_holdoff_enable = true; -+bool fiq_fsm_enable = true; -+//Bulk split-transaction NAK holdoff in microframes -+uint16_t nak_holdoff = 8; - -+unsigned short fiq_fsm_mask = 0x07; - - /** - * This function shows the Driver Version. -@@ -800,7 +801,7 @@ static int dwc_otg_driver_probe( - dwc_otg_device->os_dep.base = ioremap_nocache(_dev->resource[0].start, - _dev->resource[0].end - - _dev->resource[0].start+1); -- if (fiq_fix_enable) -+ if (fiq_enable) - { - if (!request_mem_region(_dev->resource[1].start, - _dev->resource[1].end - _dev->resource[1].start + 1, -@@ -813,7 +814,6 @@ static int dwc_otg_driver_probe( - dwc_otg_device->os_dep.mphi_base = ioremap_nocache(_dev->resource[1].start, - _dev->resource[1].end - - _dev->resource[1].start + 1); -- dummy_send = (void *) kmalloc(16, GFP_ATOMIC); - } - - #else -@@ -902,9 +902,9 @@ static int dwc_otg_driver_probe( - */ - - #if defined(PLATFORM_INTERFACE) -- devirq = platform_get_irq(_dev, 0); -+ devirq = platform_get_irq(_dev, fiq_enable ? 0 : 1); - #else -- devirq = _dev->irq; -+ devirq = _dev->irq; - #endif - DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", - devirq); -@@ -1071,9 +1071,9 @@ static int __init dwc_otg_driver_init(void) - int error; - struct device_driver *drv; - -- if(fiq_split_enable && !fiq_fix_enable) { -- printk(KERN_WARNING "dwc_otg: fiq_split_enable was set without fiq_fix_enable! Correcting.\n"); -- fiq_fix_enable = 1; -+ if(fiq_fsm_enable && !fiq_enable) { -+ printk(KERN_WARNING "dwc_otg: fiq_fsm_enable was set without fiq_enable! Correcting.\n"); -+ fiq_enable = 1; - } - - printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, -@@ -1095,9 +1095,9 @@ static int __init dwc_otg_driver_init(void) - printk(KERN_ERR "%s retval=%d\n", __func__, retval); - return retval; - } -- printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_fix_enable ? "enabled":"disabled"); -- printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff_enable ? "enabled":"disabled"); -- printk(KERN_DEBUG "dwc_otg: FIQ split fix %s\n", fiq_split_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ %s\n", fiq_enable ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: NAK holdoff %s\n", nak_holdoff ? "enabled":"disabled"); -+ printk(KERN_DEBUG "dwc_otg: FIQ split-transaction FSM %s\n", fiq_fsm_enable ? "enabled":"disabled"); - - error = driver_create_file(drv, &driver_attr_version); - #ifdef DEBUG -@@ -1378,12 +1378,19 @@ MODULE_PARM_DESC(otg_ver, "OTG revision supported 0=OTG 1.3 1=OTG 2.0"); - module_param(microframe_schedule, bool, 0444); - MODULE_PARM_DESC(microframe_schedule, "Enable the microframe scheduler"); - --module_param(fiq_fix_enable, bool, 0444); --MODULE_PARM_DESC(fiq_fix_enable, "Enable the fiq fix"); --module_param(nak_holdoff_enable, bool, 0444); --MODULE_PARM_DESC(nak_holdoff_enable, "Enable the NAK holdoff"); --module_param(fiq_split_enable, bool, 0444); --MODULE_PARM_DESC(fiq_split_enable, "Enable the FIQ fix on split transactions"); -+module_param(fiq_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_enable, "Enable the FIQ"); -+module_param(nak_holdoff, ushort, 0644); -+MODULE_PARM_DESC(nak_holdoff, "Throttle duration for bulk split-transaction endpoints on a NAK. Default 8"); -+module_param(fiq_fsm_enable, bool, 0444); -+MODULE_PARM_DESC(fiq_fsm_enable, "Enable the FIQ to perform split transactions as defined by fiq_fsm_mask"); -+module_param(fiq_fsm_mask, ushort, 0444); -+MODULE_PARM_DESC(fiq_fsm_mask, "Bitmask of transactions to perform in the FIQ.\n" -+ "Bit 0 : Non-periodic split transactions\n" -+ "Bit 1 : Periodic split transactions\n" -+ "Bit 2 : High-speed multi-transfer isochronous\n" -+ "All other bits should be set 0."); -+ - - /** @page "Module Parameters" - * -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -new file mode 100644 -index 0000000..1be6e71 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -0,0 +1,1290 @@ -+/* -+ * dwc_otg_fiq_fsm.c - The finite state machine FIQ -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This FIQ implements functionality that performs split transactions on -+ * the dwc_otg hardware without any outside intervention. A split transaction -+ * is "queued" by nominating a specific host channel to perform the entirety -+ * of a split transaction. This FIQ will then perform the microframe-precise -+ * scheduling required in each phase of the transaction until completion. -+ * -+ * The FIQ functionality is glued into the Synopsys driver via the entry point -+ * in the FSM enqueue function, and at the exit point in handling a HC interrupt -+ * for a FSM-enabled channel. -+ * -+ * NB: Large parts of this implementation have architecture-specific code. -+ * For porting this functionality to other ARM machines, the minimum is required: -+ * - An interrupt controller allowing the top-level dwc USB interrupt to be routed -+ * to the FIQ -+ * - A method of forcing a software generated interrupt from FIQ mode that then -+ * triggers an IRQ entry (with the dwc USB handler called by this IRQ number) -+ * - Guaranteed interrupt routing such that both the FIQ and SGI occur on the same -+ * processor core - there is no locking between the FIQ and IRQ (aside from -+ * local_fiq_disable) -+ * -+ */ -+ -+#include "dwc_otg_fiq_fsm.h" -+ -+ -+char buffer[1000*16]; -+int wptr; -+void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...) -+{ -+ enum fiq_debug_level dbg_lvl_req = FIQDBG_ERR; -+ va_list args; -+ char text[17]; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + 0x408) }; -+ -+ if((dbg_lvl & dbg_lvl_req) || dbg_lvl == FIQDBG_ERR) -+ { -+ snprintf(text, 9, " %4d:%1u ", hfnum.b.frnum/8, hfnum.b.frnum & 7); -+ va_start(args, fmt); -+ vsnprintf(text+8, 9, fmt, args); -+ va_end(args); -+ -+ memcpy(buffer + wptr, text, 16); -+ wptr = (wptr + 16) % sizeof(buffer); -+ } -+} -+ -+/** -+ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction -+ * @channel: channel to re-enable -+ */ -+static void fiq_fsm_restart_channel(struct fiq_state *st, int n, int force) -+{ -+ hcchar_data_t hcchar = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR) }; -+ -+ hcchar.b.chen = 0; -+ if (st->channel[n].hcchar_copy.b.eptype & 0x1) { -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ /* Hardware bug workaround: update the ssplit index */ -+ if (st->channel[n].hcsplt_copy.b.spltena) -+ st->channel[n].expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; -+ -+ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; -+ } -+ -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); -+ hcchar.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ hcchar.b.chen = 1; -+ -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, hcchar.d32); -+ fiq_print(FIQDBG_INT, st, "HCGO %01d %01d", n, force); -+} -+ -+/** -+ * fiq_fsm_setup_csplit() - Prepare a host channel for a CSplit transaction stage -+ * @st: Pointer to the channel's state -+ * @n : channel number -+ * -+ * Change host channel registers to perform a complete-split transaction. Being mindful of the -+ * endpoint direction, set control regs up correctly. -+ */ -+static void notrace fiq_fsm_setup_csplit(struct fiq_state *st, int n) -+{ -+ hcsplt_data_t hcsplt = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT) }; -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ -+ hcsplt.b.compsplt = 1; -+ if (st->channel[n].hcchar_copy.b.epdir == 1) { -+ // If IN, the CSPLIT result contains the data or a hub handshake. hctsiz = maxpacket. -+ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; -+ } else { -+ // If OUT, the CSPLIT result contains handshake only. -+ hctsiz.b.xfersize = 0; -+ } -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+ mb(); -+} -+ -+static inline int notrace fiq_get_xfer_len(struct fiq_state *st, int n) -+{ -+ /* The xfersize register is a bit wonky. For IN transfers, it decrements by the packet size. */ -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ -+ if (st->channel[n].hcchar_copy.b.epdir == 0) { -+ return st->channel[n].hctsiz_copy.b.xfersize; -+ } else { -+ return st->channel[n].hctsiz_copy.b.xfersize - hctsiz.b.xfersize; -+ } -+ -+} -+ -+ -+/** -+ * fiq_increment_dma_buf() - update DMA address for bounce buffers after a CSPLIT -+ * -+ * Of use only for IN periodic transfers. -+ */ -+static int notrace fiq_increment_dma_buf(struct fiq_state *st, int num_channels, int n) -+{ -+ hcdma_data_t hcdma; -+ int i = st->channel[n].dma_info.index; -+ int len; -+ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; -+ -+ len = fiq_get_xfer_len(st, n); -+ fiq_print(FIQDBG_INT, st, "LEN: %03d", len); -+ st->channel[n].dma_info.slot_len[i] = len; -+ i++; -+ if (i > 6) -+ BUG(); -+ -+ hcdma.d32 = (dma_addr_t) &blob->channel[n].index[i].buf[0]; -+ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ st->channel[n].dma_info.index = i; -+ return 0; -+} -+ -+/** -+ * fiq_reload_hctsiz() - for IN transactions, reset HCTSIZ -+ */ -+static void notrace fiq_fsm_reload_hctsiz(struct fiq_state *st, int n) -+{ -+ hctsiz_data_t hctsiz = { .d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ) }; -+ hctsiz.b.xfersize = st->channel[n].hctsiz_copy.b.xfersize; -+ hctsiz.b.pktcnt = 1; -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+} -+ -+/** -+ * fiq_iso_out_advance() - update DMA address and split position bits -+ * for isochronous OUT transactions. -+ * -+ * Returns 1 if this is the last packet queued, 0 otherwise. Split-ALL and -+ * Split-BEGIN states are not handled - this is done when the transaction was queued. -+ * -+ * This function must only be called from the FIQ_ISO_OUT_ACTIVE state. -+ */ -+static int notrace fiq_iso_out_advance(struct fiq_state *st, int num_channels, int n) -+{ -+ hcsplt_data_t hcsplt; -+ hctsiz_data_t hctsiz; -+ hcdma_data_t hcdma; -+ struct fiq_dma_blob *blob = (struct fiq_dma_blob *) st->dma_base; -+ int last = 0; -+ int i = st->channel[n].dma_info.index; -+ -+ fiq_print(FIQDBG_INT, st, "ADV %01d %01d ", n, i); -+ i++; -+ if (i == 4) -+ last = 1; -+ if (st->channel[n].dma_info.slot_len[i+1] == 255) -+ last = 1; -+ -+ /* New DMA address - address of bounce buffer referred to in index */ -+ hcdma.d32 = (uint32_t) &blob->channel[n].index[i].buf[0]; -+ //hcdma.d32 = FIQ_READ(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n)); -+ //hcdma.d32 += st->channel[n].dma_info.slot_len[i]; -+ fiq_print(FIQDBG_INT, st, "LAST: %01d ", last); -+ fiq_print(FIQDBG_INT, st, "LEN: %03d", st->channel[n].dma_info.slot_len[i]); -+ hcsplt.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT); -+ hctsiz.d32 = FIQ_READ(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ); -+ hcsplt.b.xactpos = (last) ? ISOC_XACTPOS_END : ISOC_XACTPOS_MID; -+ /* Set up new packet length */ -+ hctsiz.b.pktcnt = 1; -+ hctsiz.b.xfersize = st->channel[n].dma_info.slot_len[i]; -+ fiq_print(FIQDBG_INT, st, "%08x", hctsiz.d32); -+ -+ st->channel[n].dma_info.index++; -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCSPLT, hcsplt.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, hctsiz.d32); -+ FIQ_WRITE(st->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ return last; -+} -+ -+/** -+ * fiq_fsm_tt_next_isoc() - queue next pending isochronous out start-split on a TT -+ * -+ * Despite the limitations of the DWC core, we can force a microframe pipeline of -+ * isochronous OUT start-split transactions while waiting for a corresponding other-type -+ * of endpoint to finish its CSPLITs. TTs have big periodic buffers therefore it -+ * is very unlikely that filling the start-split FIFO will cause data loss. -+ * This allows much better interleaving of transactions in an order-independent way- -+ * there is no requirement to prioritise isochronous, just a state-space search has -+ * to be performed on each periodic start-split complete interrupt. -+ */ -+static int notrace fiq_fsm_tt_next_isoc(struct fiq_state *st, int num_channels, int n) -+{ -+ int hub_addr = st->channel[n].hub_addr; -+ int port_addr = st->channel[n].port_addr; -+ int i, poked = 0; -+ for (i = 0; i < num_channels; i++) { -+ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ if (st->channel[i].hub_addr == hub_addr && -+ st->channel[i].port_addr == port_addr) { -+ switch (st->channel[i].fsm) { -+ case FIQ_PER_ISO_OUT_PENDING: -+ if (st->channel[i].nrpackets == 1) { -+ st->channel[i].fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ st->channel[i].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ fiq_fsm_restart_channel(st, i, 0); -+ poked = 1; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ if (poked) -+ break; -+ } -+ return poked; -+} -+ -+/** -+ * fiq_fsm_tt_in_use() - search for host channels using this TT -+ * @n: Channel to use as reference -+ * -+ */ -+int notrace noinline fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n) -+{ -+ int hub_addr = st->channel[n].hub_addr; -+ int port_addr = st->channel[n].port_addr; -+ int i, in_use = 0; -+ for (i = 0; i < num_channels; i++) { -+ if (i == n || st->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ switch (st->channel[i].fsm) { -+ /* TT is reserved for channels that are in the middle of a periodic -+ * split transaction. -+ */ -+ case FIQ_PER_SSPLIT_STARTED: -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ //case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ case FIQ_PER_ISO_OUT_LAST: -+ if (st->channel[i].hub_addr == hub_addr && -+ st->channel[i].port_addr == port_addr) { -+ in_use = 1; -+ } -+ break; -+ default: -+ break; -+ } -+ if (in_use) -+ break; -+ } -+ return in_use; -+} -+ -+/** -+ * fiq_fsm_more_csplits() - determine whether additional CSPLITs need -+ * to be issued for this IN transaction. -+ * -+ * We cannot tell the inbound PID of a data packet due to hardware limitations. -+ * we need to make an educated guess as to whether we need to queue another CSPLIT -+ * or not. A no-brainer is when we have received enough data to fill the endpoint -+ * size, but for endpoints that give variable-length data then we have to resort -+ * to heuristics. -+ * -+ * We also return whether this is the last CSPLIT to be queued, again based on -+ * heuristics. This is to allow a 1-uframe overlap of periodic split transactions. -+ * Note: requires at least 1 CSPLIT to have been performed prior to being called. -+ */ -+ -+/* -+ * We need some way of guaranteeing if a returned periodic packet of size X -+ * has a DATA0 PID. -+ * The heuristic value of 144 bytes assumes that the received data has maximal -+ * bit-stuffing and the clock frequency of the transmitting device is at the lowest -+ * permissible limit. If the transfer length results in a final packet size -+ * 144 < p <= 188, then an erroneous CSPLIT will be issued. -+ * Also used to ensure that an endpoint will nominally only return a single -+ * complete-split worth of data. -+ */ -+#define DATA0_PID_HEURISTIC 144 -+ -+static int notrace noinline fiq_fsm_more_csplits(struct fiq_state *state, int n, int *probably_last) -+{ -+ -+ int i; -+ int total_len = 0; -+ int more_needed = 1; -+ struct fiq_channel_state *st = &state->channel[n]; -+ -+ for (i = 0; i < st->dma_info.index; i++) { -+ total_len += st->dma_info.slot_len[i]; -+ } -+ -+ *probably_last = 0; -+ -+ if (st->hcchar_copy.b.eptype == 0x3) { -+ /* -+ * An interrupt endpoint will take max 2 CSPLITs. if we are receiving data -+ * then this is definitely the last CSPLIT. -+ */ -+ *probably_last = 1; -+ } else { -+ /* Isoc IN. This is a bit risky if we are the first transaction: -+ * we may have been held off slightly. */ -+ if (i > 1 && st->dma_info.slot_len[st->dma_info.index-1] <= DATA0_PID_HEURISTIC) { -+ more_needed = 0; -+ } -+ /* If in the next uframe we will receive enough data to fill the endpoint, -+ * then only issue 1 more csplit. -+ */ -+ if (st->hctsiz_copy.b.xfersize - total_len <= DATA0_PID_HEURISTIC) -+ *probably_last = 1; -+ } -+ -+ if (total_len >= st->hctsiz_copy.b.xfersize || -+ i == 6 || total_len == 0) -+ /* Note: due to bit stuffing it is possible to have > 6 CSPLITs for -+ * a single endpoint. Accepting more would completely break our scheduling mechanism though -+ * - in these extreme cases we will pass through a truncated packet. -+ */ -+ more_needed = 0; -+ -+ return more_needed; -+} -+ -+/** -+ * fiq_fsm_too_late() - Test transaction for lateness -+ * -+ * If a SSPLIT for a large IN transaction is issued too late in a frame, -+ * the hub will disable the port to the device and respond with ERR handshakes. -+ * The hub status endpoint will not reflect this change. -+ * Returns 1 if we will issue a SSPLIT that will result in a device babble. -+ */ -+int notrace fiq_fsm_too_late(struct fiq_state *st, int n) -+{ -+ int uframe; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ uframe = hfnum.b.frnum & 0x7; -+ if ((uframe < 6) && (st->channel[n].nrpackets + 1 + uframe > 7)) { -+ return 1; -+ } else { -+ return 0; -+ } -+} -+ -+ -+/** -+ * fiq_fsm_start_next_periodic() - A half-arsed attempt at a microframe pipeline -+ * -+ * Search pending transactions in the start-split pending state and queue them. -+ * Don't queue packets in uframe .5 (comes out in .6) (USB2.0 11.18.4). -+ * Note: we specifically don't do isochronous OUT transactions first because better -+ * use of the TT's start-split fifo can be achieved by pipelining an IN before an OUT. -+ */ -+static void notrace noinline fiq_fsm_start_next_periodic(struct fiq_state *st, int num_channels) -+{ -+ int n; -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(st->dwc_regs_base + HFNUM) }; -+ if ((hfnum.b.frnum & 0x7) == 5) -+ return; -+ for (n = 0; n < num_channels; n++) { -+ if (st->channel[n].fsm == FIQ_PER_SSPLIT_QUEUED) { -+ /* Check to see if any other transactions are using this TT */ -+ if(!fiq_fsm_tt_in_use(st, num_channels, n)) { -+ if (!fiq_fsm_too_late(st, n)) { -+ st->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; -+ fiq_print(FIQDBG_INT, st, "NEXTPER "); -+ fiq_fsm_restart_channel(st, n, 0); -+ } else { -+ st->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ } -+ break; -+ } -+ } -+ } -+ for (n = 0; n < num_channels; n++) { -+ if (st->channel[n].fsm == FIQ_PER_ISO_OUT_PENDING) { -+ if (!fiq_fsm_tt_in_use(st, num_channels, n)) { -+ fiq_print(FIQDBG_INT, st, "NEXTISO "); -+ st->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ fiq_fsm_restart_channel(st, n, 0); -+ break; -+ } -+ } -+ } -+} -+ -+/** -+ * fiq_fsm_update_hs_isoc() - update isochronous frame and transfer data -+ * @state: Pointer to fiq_state -+ * @n: Channel transaction is active on -+ * @hcint: Copy of host channel interrupt register -+ * -+ * Returns 0 if there are no more transactions for this HC to do, 1 -+ * otherwise. -+ */ -+static int notrace noinline fiq_fsm_update_hs_isoc(struct fiq_state *state, int n, hcint_data_t hcint) -+{ -+ struct fiq_channel_state *st = &state->channel[n]; -+ int xfer_len = 0, nrpackets = 0; -+ hcdma_data_t hcdma; -+ fiq_print(FIQDBG_INT, state, "HSISO %02d", n); -+ -+ xfer_len = fiq_get_xfer_len(state, n); -+ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].actual_length = xfer_len; -+ -+ st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].status = hcint.d32; -+ -+ st->hs_isoc_info.index++; -+ if (st->hs_isoc_info.index == st->hs_isoc_info.nrframes) { -+ return 0; -+ } -+ -+ /* grab the next DMA address offset from the array */ -+ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].offset; -+ FIQ_WRITE(state->dwc_regs_base + HC_DMA + (HC_OFFSET * n), hcdma.d32); -+ -+ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as -+ * the core needs to be told to send the correct number. Caution: for IN transfers, -+ * this is always set to the maximum size of the endpoint. */ -+ xfer_len = st->hs_isoc_info.iso_desc[st->hs_isoc_info.index].length; -+ /* Integer divide in a FIQ: fun. FIXME: make this not suck */ -+ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; -+ if (nrpackets == 0) -+ nrpackets = 1; -+ st->hcchar_copy.b.multicnt = nrpackets; -+ st->hctsiz_copy.b.pktcnt = nrpackets; -+ -+ /* Initial PID also needs to be set */ -+ if (st->hcchar_copy.b.epdir == 0) { -+ st->hctsiz_copy.b.xfersize = xfer_len; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_MDATA; -+ break; -+ } -+ -+ } else { -+ switch (st->hcchar_copy.b.multicnt) { -+ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA1; -+ break; -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA2; -+ break; -+ } -+ } -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCTSIZ, st->hctsiz_copy.d32); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR, st->hcchar_copy.d32); -+ /* Channel is enabled on hcint handler exit */ -+ fiq_print(FIQDBG_INT, state, "HSISOOUT"); -+ return 1; -+} -+ -+ -+/** -+ * fiq_fsm_do_sof() - FSM start-of-frame interrupt handler -+ * @state: Pointer to the state struct passed from banked FIQ mode registers. -+ * @num_channels: set according to the DWC hardware configuration -+ * -+ * The SOF handler in FSM mode has two functions -+ * 1. Hold off SOF from causing schedule advancement in IRQ context if there's -+ * nothing to do -+ * 2. Advance certain FSM states that require either a microframe delay, or a microframe -+ * of holdoff. -+ * -+ * The second part is architecture-specific to mach-bcm2835 - -+ * a sane interrupt controller would have a mask register for ARM interrupt sources -+ * to be promoted to the nFIQ line, but it doesn't. Instead a single interrupt -+ * number (USB) can be enabled. This means that certain parts of the USB specification -+ * that require "wait a little while, then issue another packet" cannot be fulfilled with -+ * the timing granularity required to achieve optimal throughout. The workaround is to use -+ * the SOF "timer" (125uS) to perform this task. -+ */ -+static int notrace noinline fiq_fsm_do_sof(struct fiq_state *state, int num_channels) -+{ -+ hfnum_data_t hfnum = { .d32 = FIQ_READ(state->dwc_regs_base + HFNUM) }; -+ int n; -+ int kick_irq = 0; -+ -+ if ((hfnum.b.frnum & 0x7) == 1) { -+ /* We cannot issue csplits for transactions in the last frame past (n+1).1 -+ * Check to see if there are any transactions that are stale. -+ * Boot them out. -+ */ -+ for (n = 0; n < num_channels; n++) { -+ switch (state->channel[n].fsm) { -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_CSPLIT_LAST: -+ /* Check if we are no longer in the same full-speed frame. */ -+ if (((state->channel[n].expected_uframe & 0x3FFF) & ~0x7) < -+ (hfnum.b.frnum & ~0x7)) -+ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ break; -+ default: -+ break; -+ } -+ } -+ } -+ -+ for (n = 0; n < num_channels; n++) { -+ switch (state->channel[n].fsm) { -+ -+ case FIQ_NP_SSPLIT_RETRY: -+ case FIQ_NP_IN_CSPLIT_RETRY: -+ case FIQ_NP_OUT_CSPLIT_RETRY: -+ fiq_fsm_restart_channel(state, n, 0); -+ break; -+ -+ case FIQ_HS_ISOC_SLEEPING: -+ state->channel[n].fsm = FIQ_HS_ISOC_TURBO; -+ fiq_fsm_restart_channel(state, n, 0); -+ break; -+ -+ case FIQ_PER_SSPLIT_QUEUED: -+ if ((hfnum.b.frnum & 0x7) == 5) -+ break; -+ if(!fiq_fsm_tt_in_use(state, num_channels, n)) { -+ if (!fiq_fsm_too_late(state, n)) { -+ fiq_print(FIQDBG_INT, st, "SOF GO %01d", n); -+ fiq_fsm_restart_channel(state, n, 0); -+ state->channel[n].fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ /* Transaction cannot be started without risking a device babble error */ -+ state->channel[n].fsm = FIQ_PER_SPLIT_TIMEOUT; -+ state->haintmsk_saved.b2.chint &= ~(1 << n); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); -+ kick_irq |= 1; -+ } -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_PENDING: -+ /* Ordinarily, this should be poked after the SSPLIT -+ * complete interrupt for a competing transfer on the same -+ * TT. Doesn't happen for aborted transactions though. -+ */ -+ if ((hfnum.b.frnum & 0x7) >= 5) -+ break; -+ if (!fiq_fsm_tt_in_use(state, num_channels, n)) { -+ /* Hardware bug. SOF can sometimes occur after the channel halt interrupt -+ * that caused this. -+ */ -+ fiq_fsm_restart_channel(state, n, 0); -+ fiq_print(FIQDBG_INT, state, "SOF ISOC"); -+ if (state->channel[n].nrpackets == 1) { -+ state->channel[n].fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ state->channel[n].fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_WAIT: -+ /* we are guaranteed to be in this state if and only if the SSPLIT interrupt -+ * occurred when the bus transaction occurred. The SOF interrupt reversal bug -+ * will utterly bugger this up though. -+ */ -+ if (hfnum.b.frnum != state->channel[n].expected_uframe) { -+ fiq_print(FIQDBG_INT, state, "SOFCS %d ", n); -+ state->channel[n].fsm = FIQ_PER_CSPLIT_POLL; -+ fiq_fsm_restart_channel(state, n, 0); -+ fiq_fsm_start_next_periodic(state, num_channels); -+ -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ case FIQ_DEQUEUE_ISSUED: -+ /* Ugly: we have to force a HCD interrupt. -+ * Poke the mask for the channel in question. -+ * We will take a fake SOF because of this, but -+ * that's OK. -+ */ -+ state->haintmsk_saved.b2.chint &= ~(1 << n); -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, 0); -+ kick_irq |= 1; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ if (state->kick_np_queues || -+ dwc_frame_num_le(state->next_sched_frame, hfnum.b.frnum)) -+ kick_irq |= 1; -+ -+ return !kick_irq; -+} -+ -+ -+/** -+ * fiq_fsm_do_hcintr() - FSM host channel interrupt handler -+ * @state: Pointer to the FIQ state struct -+ * @num_channels: Number of channels as per hardware config -+ * @n: channel for which HAINT(i) was raised -+ * -+ * An important property is that only the CHHLT interrupt is unmasked. Unfortunately, AHBerr is as well. -+ */ -+static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_channels, int n) -+{ -+ hcint_data_t hcint; -+ hcintmsk_data_t hcintmsk; -+ hcint_data_t hcint_probe; -+ hcchar_data_t hcchar; -+ int handled = 0; -+ int restart = 0; -+ int last_csplit = 0; -+ int start_next_periodic = 0; -+ struct fiq_channel_state *st = &state->channel[n]; -+ hfnum_data_t hfnum; -+ -+ hcint.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT); -+ hcintmsk.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK); -+ hcint_probe.d32 = hcint.d32 & hcintmsk.d32; -+ -+ if (st->fsm != FIQ_PASSTHROUGH) { -+ fiq_print(FIQDBG_INT, state, "HC%01d ST%02d", n, st->fsm); -+ fiq_print(FIQDBG_INT, state, "%08x", hcint.d32); -+ } -+ -+ switch (st->fsm) { -+ -+ case FIQ_PASSTHROUGH: -+ case FIQ_DEQUEUE_ISSUED: -+ /* doesn't belong to us, kick it upstairs */ -+ break; -+ -+ case FIQ_PASSTHROUGH_ERRORSTATE: -+ /* We are here to emulate the error recovery mechanism of the dwc HCD. -+ * Several interrupts are unmasked if a previous transaction failed - it's -+ * death for the FIQ to attempt to handle them as the channel isn't halted. -+ * Emulate what the HCD does in this situation: mask and continue. -+ * The FSM has no other state setup so this has to be handled out-of-band. -+ */ -+ fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); -+ if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { -+ fiq_print(FIQDBG_ERR, state, "RESET %02d", n); -+ st->nr_errors = 0; -+ hcintmsk.b.nak = 0; -+ hcintmsk.b.ack = 0; -+ hcintmsk.b.datatglerr = 0; -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINTMSK, hcintmsk.d32); -+ return 1; -+ } -+ if (hcint_probe.b.chhltd) { -+ fiq_print(FIQDBG_ERR, state, "CHHLT %02d", n); -+ fiq_print(FIQDBG_ERR, state, "%08x", hcint.d32); -+ return 0; -+ } -+ break; -+ -+ /* Non-periodic state groups */ -+ case FIQ_NP_SSPLIT_STARTED: -+ case FIQ_NP_SSPLIT_RETRY: -+ /* Got a HCINT for a NP SSPLIT. Expected ACK / NAK / fail */ -+ if (hcint.b.ack) { -+ /* SSPLIT complete. For OUT, the data has been sent. For IN, the LS transaction -+ * will start shortly. SOF needs to kick the transaction to prevent a NYET flood. -+ */ -+ if(st->hcchar_copy.b.epdir == 1) -+ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; -+ else -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ st->nr_errors = 0; -+ handled = 1; -+ fiq_fsm_setup_csplit(state, n); -+ } else if (hcint.b.nak) { -+ // No buffer space in TT. Retry on a uframe boundary. -+ st->fsm = FIQ_NP_SSPLIT_RETRY; -+ handled = 1; -+ } else if (hcint.b.xacterr) { -+ // The only other one we care about is xacterr. This implies HS bus error - retry. -+ st->nr_errors++; -+ st->fsm = FIQ_NP_SSPLIT_RETRY; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else { -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ handled = 0; -+ restart = 0; -+ } -+ break; -+ -+ case FIQ_NP_IN_CSPLIT_RETRY: -+ /* Received a CSPLIT done interrupt. -+ * Expected Data/NAK/STALL/NYET for IN. -+ */ -+ if (hcint.b.xfercomp) { -+ /* For IN, data is present. */ -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nak) { -+ /* no endpoint data. Punt it upstairs */ -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nyet) { -+ /* CSPLIT NYET - retry on a uframe boundary. */ -+ handled = 1; -+ st->nr_errors = 0; -+ } else if (hcint.b.datatglerr) { -+ /* data toggle errors do not set the xfercomp bit. */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else if (hcint.b.xacterr) { -+ /* HS error. Retry immediate */ -+ st->fsm = FIQ_NP_IN_CSPLIT_RETRY; -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else if (hcint.b.stall || hcint.b.bblerr) { -+ /* A STALL implies either a LS bus error or a genuine STALL. */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else { -+ /* Hardware bug. It's possible in some cases to -+ * get a channel halt with nothing else set when -+ * the response was a NYET. Treat as local 3-strikes retry. -+ */ -+ hcint_data_t hcint_test = hcint; -+ hcint_test.b.chhltd = 0; -+ if (!hcint_test.d32) { -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ } -+ } else { -+ /* Bail out if something unexpected happened */ -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } -+ } -+ break; -+ -+ case FIQ_NP_OUT_CSPLIT_RETRY: -+ /* Received a CSPLIT done interrupt. -+ * Expected ACK/NAK/STALL/NYET/XFERCOMP for OUT.*/ -+ if (hcint.b.xfercomp) { -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nak) { -+ // The HCD will implement the holdoff on frame boundaries. -+ st->fsm = FIQ_NP_SPLIT_DONE; -+ } else if (hcint.b.nyet) { -+ // Hub still processing. -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ handled = 1; -+ st->nr_errors = 0; -+ //restart = 1; -+ } else if (hcint.b.xacterr) { -+ /* HS error. retry immediate */ -+ st->fsm = FIQ_NP_OUT_CSPLIT_RETRY; -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ restart = 1; -+ } -+ } else if (hcint.b.stall) { -+ /* LS bus error or genuine stall */ -+ st->fsm = FIQ_NP_SPLIT_LS_ABORTED; -+ } else { -+ /* -+ * Hardware bug. It's possible in some cases to get a -+ * channel halt with nothing else set when the response was a NYET. -+ * Treat as local 3-strikes retry. -+ */ -+ hcint_data_t hcint_test = hcint; -+ hcint_test.b.chhltd = 0; -+ if (!hcint_test.d32) { -+ st->nr_errors++; -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } else { -+ handled = 1; -+ } -+ } else { -+ // Something unexpected happened. AHBerror or babble perhaps. Let the IRQ deal with it. -+ st->fsm = FIQ_NP_SPLIT_HS_ABORTED; -+ } -+ } -+ break; -+ -+ /* Periodic split states (except isoc out) */ -+ case FIQ_PER_SSPLIT_STARTED: -+ /* Expect an ACK or failure for SSPLIT */ -+ if (hcint.b.ack) { -+ /* -+ * SSPLIT transfer complete interrupt - the generation of this interrupt is fraught with bugs. -+ * For a packet queued in microframe n-3 to appear in n-2, if the channel is enabled near the EOF1 -+ * point for microframe n-3, the packet will not appear on the bus until microframe n. -+ * Additionally, the generation of the actual interrupt is dodgy. For a packet appearing on the bus -+ * in microframe n, sometimes the interrupt is generated immediately. Sometimes, it appears in n+1 -+ * coincident with SOF for n+1. -+ * SOF is also buggy. It can sometimes be raised AFTER the first bus transaction has taken place. -+ * These appear to be caused by timing/clock crossing bugs within the core itself. -+ * State machine workaround. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ fiq_fsm_setup_csplit(state, n); -+ /* Poke the oddfrm bit. If we are equivalent, we received the interrupt at the correct -+ * time. If not, then we're in the next SOF. -+ */ -+ if ((hfnum.b.frnum & 0x1) == hcchar.b.oddfrm) { -+ fiq_print(FIQDBG_INT, state, "CSWAIT %01d", n); -+ st->expected_uframe = hfnum.b.frnum; -+ st->fsm = FIQ_PER_CSPLIT_WAIT; -+ } else { -+ fiq_print(FIQDBG_INT, state, "CSPOL %01d", n); -+ /* For isochronous IN endpoints, -+ * we need to hold off if we are expecting a lot of data */ -+ if (st->hcchar_copy.b.mps < DATA0_PID_HEURISTIC) { -+ start_next_periodic = 1; -+ } -+ /* Danger will robinson: we are in a broken state. If our first interrupt after -+ * this is a NYET, it will be delayed by 1 uframe and result in an unrecoverable -+ * lag. Unmask the NYET interrupt. -+ */ -+ st->expected_uframe = (hfnum.b.frnum + 1) & 0x3FFF; -+ st->fsm = FIQ_PER_CSPLIT_BROKEN_NYET1; -+ restart = 1; -+ } -+ handled = 1; -+ } else if (hcint.b.xacterr) { -+ /* 3-strikes retry is enabled, we have hit our max nr_errors */ -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ start_next_periodic = 1; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ start_next_periodic = 1; -+ } -+ /* We can now queue the next isochronous OUT transaction, if one is pending. */ -+ if(fiq_fsm_tt_next_isoc(state, num_channels, n)) { -+ fiq_print(FIQDBG_INT, state, "NEXTISO "); -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_NYET1: -+ /* First CSPLIT attempt was a NYET. If we get a subsequent NYET, -+ * we are too late and the TT has dropped its CSPLIT fifo. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ start_next_periodic = 1; -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ st->fsm = FIQ_PER_CSPLIT_POLL; -+ st->nr_errors = 0; -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ handled = 1; -+ restart = 1; -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ /* Doh. Data lost. */ -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_BROKEN_NYET1: -+ /* -+ * we got here because our host channel is in the delayed-interrupt -+ * state and we cannot take a NYET interrupt any later than when it -+ * occurred. Disable then re-enable the channel if this happens to force -+ * CSPLITs to occur at the right time. -+ */ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ fiq_print(FIQDBG_INT, state, "BROK: %01d ", n); -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ start_next_periodic = 1; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ st->fsm = FIQ_PER_CSPLIT_POLL; -+ handled = 1; -+ restart = 1; -+ start_next_periodic = 1; -+ /* Reload HCTSIZ for the next transfer */ -+ fiq_fsm_reload_hctsiz(state, n); -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ start_next_periodic = 1; -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ /* Local 3-strikes retry is handled by the core. This is a ERR response.*/ -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_PER_CSPLIT_POLL: -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ hcchar.d32 = FIQ_READ(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCCHAR); -+ start_next_periodic = 1; -+ if (hcint.b.nak) { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } else if (hcint.b.xfercomp) { -+ fiq_increment_dma_buf(state, num_channels, n); -+ if (fiq_fsm_more_csplits(state, n, &last_csplit)) { -+ handled = 1; -+ restart = 1; -+ /* Reload HCTSIZ for the next transfer */ -+ fiq_fsm_reload_hctsiz(state, n); -+ if (!last_csplit) -+ start_next_periodic = 0; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } else if (hcint.b.nyet) { -+ /* Are we a NYET after the first data packet? */ -+ if (st->nrpackets == 0) { -+ st->fsm = FIQ_PER_CSPLIT_NYET1; -+ handled = 1; -+ restart = 1; -+ } else { -+ /* We got a NYET when polling CSPLITs. Can happen -+ * if our heuristic fails, or if someone disables us -+ * for any significant length of time. -+ */ -+ if (st->nr_errors >= 3) { -+ st->fsm = FIQ_PER_SPLIT_NYET_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_DONE; -+ } -+ } -+ } else if (hcint.b.xacterr || hcint.b.stall || hcint.b.bblerr) { -+ /* For xacterr, Local 3-strikes retry is handled by the core. This is a ERR response.*/ -+ st->fsm = FIQ_PER_SPLIT_LS_ABORTED; -+ } else { -+ st->fsm = FIQ_PER_SPLIT_HS_ABORTED; -+ } -+ break; -+ -+ case FIQ_HS_ISOC_TURBO: -+ if (fiq_fsm_update_hs_isoc(state, n, hcint)) { -+ /* more transactions to come */ -+ handled = 1; -+ restart = 1; -+ fiq_print(FIQDBG_INT, state, "HSISO M "); -+ } else { -+ st->fsm = FIQ_HS_ISOC_DONE; -+ fiq_print(FIQDBG_INT, state, "HSISO F "); -+ } -+ break; -+ -+ case FIQ_HS_ISOC_ABORTED: -+ /* This abort is called by the driver rewriting the state mid-transaction -+ * which allows the dequeue mechanism to work more effectively. -+ */ -+ break; -+ -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ if (hcint.b.ack) { -+ if(fiq_iso_out_advance(state, num_channels, n)) { -+ /* last OUT transfer */ -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ /* -+ * Assuming the periodic FIFO in the dwc core -+ * actually does its job properly, we can queue -+ * the next ssplit now and in theory, the wire -+ * transactions will be in-order. -+ */ -+ // No it doesn't. It appears to process requests in host channel order. -+ //start_next_periodic = 1; -+ } -+ handled = 1; -+ restart = 1; -+ } else { -+ /* -+ * Isochronous transactions carry on regardless. Log the error -+ * and continue. -+ */ -+ //explode += 1; -+ st->nr_errors++; -+ if(fiq_iso_out_advance(state, num_channels, n)) { -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ //start_next_periodic = 1; -+ } -+ handled = 1; -+ restart = 1; -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_LAST: -+ if (hcint.b.ack) { -+ /* All done here */ -+ st->fsm = FIQ_PER_ISO_OUT_DONE; -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_DONE; -+ st->nr_errors++; -+ } -+ start_next_periodic = 1; -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ /* SOF kicked us because we overran. */ -+ start_next_periodic = 1; -+ break; -+ -+ default: -+ break; -+ } -+ -+ if (handled) { -+ FIQ_WRITE(state->dwc_regs_base + HC_START + (HC_OFFSET * n) + HCINT, hcint.d32); -+ } else { -+ /* Copy the regs into the state so the IRQ knows what to do */ -+ st->hcint_copy.d32 = hcint.d32; -+ } -+ -+ if (restart) { -+ /* Restart always implies handled. */ -+ if (restart == 2) { -+ /* For complete-split INs, the show must go on. -+ * Force a channel restart */ -+ fiq_fsm_restart_channel(state, n, 1); -+ } else { -+ fiq_fsm_restart_channel(state, n, 0); -+ } -+ } -+ if (start_next_periodic) { -+ fiq_fsm_start_next_periodic(state, num_channels); -+ } -+ if (st->fsm != FIQ_PASSTHROUGH) -+ fiq_print(FIQDBG_INT, state, "FSMOUT%02d", st->fsm); -+ -+ return handled; -+} -+ -+ -+/** -+ * dwc_otg_fiq_fsm() - Flying State Machine (monster) FIQ -+ * @state: pointer to state struct passed from the banked FIQ mode registers. -+ * @num_channels: set according to the DWC hardware configuration -+ * @dma: pointer to DMA bounce buffers for split transaction slots -+ * -+ * The FSM FIQ performs the low-level tasks that normally would be performed by the microcode -+ * inside an EHCI or similar host controller regarding split transactions. The DWC core -+ * interrupts each and every time a split transaction packet is received or sent successfully. -+ * This results in either an interrupt storm when everything is working "properly", or -+ * the interrupt latency of the system in general breaks time-sensitive periodic split -+ * transactions. Pushing the low-level, but relatively easy state machine work into the FIQ -+ * solves these problems. -+ * -+ * Return: void -+ */ -+void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) -+{ -+ gintsts_data_t gintsts, gintsts_handled; -+ gintmsk_data_t gintmsk; -+ //hfnum_data_t hfnum; -+ haint_data_t haint, haint_handled; -+ haintmsk_data_t haintmsk; -+ int kick_irq = 0; -+ -+ gintsts_handled.d32 = 0; -+ haint_handled.d32 = 0; -+ -+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); -+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -+ gintsts.d32 &= gintmsk.d32; -+ -+ if (gintsts.b.sofintr) { -+ /* For FSM mode, SOF is required to keep the state machine advance for -+ * certain stages of the periodic pipeline. It's death to mask this -+ * interrupt in that case. -+ */ -+ -+ if (!fiq_fsm_do_sof(state, num_channels)) { -+ /* Kick IRQ once. Queue advancement means that all pending transactions -+ * will get serviced when the IRQ finally executes. -+ */ -+ if (state->gintmsk_saved.b.sofintr == 1) -+ kick_irq |= 1; -+ state->gintmsk_saved.b.sofintr = 0; -+ } -+ gintsts_handled.b.sofintr = 1; -+ } -+ -+ if (gintsts.b.hcintr) { -+ int i; -+ haint.d32 = FIQ_READ(state->dwc_regs_base + HAINT); -+ haintmsk.d32 = FIQ_READ(state->dwc_regs_base + HAINTMSK); -+ haint.d32 &= haintmsk.d32; -+ haint_handled.d32 = 0; -+ for (i=0; ihaintmsk_saved.b2.chint &= ~(1 << i); -+ } else { -+ /* do_hcintr cleaned up after itself, but clear haint */ -+ haint_handled.b2.chint |= (1 << i); -+ } -+ } -+ } -+ -+ if (haint_handled.b2.chint) { -+ FIQ_WRITE(state->dwc_regs_base + HAINT, haint_handled.d32); -+ } -+ -+ if (haintmsk.d32 != (haintmsk.d32 & state->haintmsk_saved.d32)) { -+ /* -+ * This is necessary to avoid multiple retriggers of the MPHI in the case -+ * where interrupts are held off and HCINTs start to pile up. -+ * Only wake up the IRQ if a new interrupt came in, was not handled and was -+ * masked. -+ */ -+ haintmsk.d32 &= state->haintmsk_saved.d32; -+ FIQ_WRITE(state->dwc_regs_base + HAINTMSK, haintmsk.d32); -+ kick_irq |= 1; -+ } -+ /* Top-Level interrupt - always handled because it's level-sensitive */ -+ gintsts_handled.b.hcintr = 1; -+ } -+ -+ -+ /* Clear the bits in the saved register that were not handled but were triggered. */ -+ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); -+ -+ /* FIQ didn't handle something - mask has changed - write new mask */ -+ if (gintmsk.d32 != (gintmsk.d32 & state->gintmsk_saved.d32)) { -+ gintmsk.d32 &= state->gintmsk_saved.d32; -+ gintmsk.b.sofintr = 1; -+ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); -+// fiq_print(FIQDBG_INT, state, "KICKGINT"); -+// fiq_print(FIQDBG_INT, state, "%08x", gintmsk.d32); -+// fiq_print(FIQDBG_INT, state, "%08x", state->gintmsk_saved.d32); -+ kick_irq |= 1; -+ } -+ -+ if (gintsts_handled.d32) { -+ /* Only applies to edge-sensitive bits in GINTSTS */ -+ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); -+ } -+ -+ /* We got an interrupt, didn't handle it. */ -+ if (kick_irq) { -+ state->mphi_int_count++; -+ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); -+ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); -+ -+ } -+ state->fiq_done++; -+ mb(); -+} -+ -+ -+/** -+ * dwc_otg_fiq_nop() - FIQ "lite" -+ * @state: pointer to state struct passed from the banked FIQ mode registers. -+ * -+ * The "nop" handler does not intervene on any interrupts other than SOF. -+ * It is limited in scope to deciding at each SOF if the IRQ SOF handler (which deals -+ * with non-periodic/periodic queues) needs to be kicked. -+ * -+ * This is done to hold off the SOF interrupt, which occurs at a rate of 8000 per second. -+ * -+ * Return: void -+ */ -+void notrace dwc_otg_fiq_nop(struct fiq_state *state) -+{ -+ gintsts_data_t gintsts, gintsts_handled; -+ gintmsk_data_t gintmsk; -+ hfnum_data_t hfnum; -+ -+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); -+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); -+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -+ gintsts.d32 &= gintmsk.d32; -+ gintsts_handled.d32 = 0; -+ -+ if (gintsts.b.sofintr) { -+ if (!state->kick_np_queues && -+ dwc_frame_num_gt(state->next_sched_frame, hfnum.b.frnum)) { -+ /* SOF handled, no work to do, just ACK interrupt */ -+ gintsts_handled.b.sofintr = 1; -+ } else { -+ /* Kick IRQ */ -+ state->gintmsk_saved.b.sofintr = 0; -+ } -+ } -+ -+ /* Reset handled interrupts */ -+ if(gintsts_handled.d32) { -+ FIQ_WRITE(state->dwc_regs_base + GINTSTS, gintsts_handled.d32); -+ } -+ -+ /* Clear the bits in the saved register that were not handled but were triggered. */ -+ state->gintmsk_saved.d32 &= ~(gintsts.d32 & ~gintsts_handled.d32); -+ -+ /* We got an interrupt, didn't handle it and want to mask it */ -+ if (~(state->gintmsk_saved.d32)) { -+ state->mphi_int_count++; -+ gintmsk.d32 &= state->gintmsk_saved.d32; -+ FIQ_WRITE(state->dwc_regs_base + GINTMSK, gintmsk.d32); -+ /* Force a clear before another dummy send */ -+ FIQ_WRITE(state->mphi_regs.intstat, (1<<29)); -+ FIQ_WRITE(state->mphi_regs.outdda, (int) state->dummy_send); -+ FIQ_WRITE(state->mphi_regs.outddb, (1<<29)); -+ -+ } -+ state->fiq_done++; -+ mb(); -+} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -new file mode 100644 -index 0000000..5c7707f ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -@@ -0,0 +1,353 @@ -+/* -+ * dwc_otg_fiq_fsm.h - Finite state machine FIQ header definitions -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ * -+ * This FIQ implements functionality that performs split transactions on -+ * the dwc_otg hardware without any outside intervention. A split transaction -+ * is "queued" by nominating a specific host channel to perform the entirety -+ * of a split transaction. This FIQ will then perform the microframe-precise -+ * scheduling required in each phase of the transaction until completion. -+ * -+ * The FIQ functionality has been surgically implanted into the Synopsys -+ * vendor-provided driver. -+ * -+ */ -+ -+#ifndef DWC_OTG_FIQ_FSM_H_ -+#define DWC_OTG_FIQ_FSM_H_ -+ -+#include "dwc_otg_regs.h" -+#include "dwc_otg_cil.h" -+#include "dwc_otg_hcd.h" -+#include -+#include -+#include -+#include -+ -+#if 0 -+#define FLAME_ON(x) \ -+do { \ -+ int gpioreg; \ -+ \ -+ gpioreg = readl(__io_address(0x20200000+0x8)); \ -+ gpioreg &= ~(7 << (x-20)*3); \ -+ gpioreg |= 0x1 << (x-20)*3; \ -+ writel(gpioreg, __io_address(0x20200000+0x8)); \ -+ \ -+ writel(1< 1, SOF wakes up the isochronous FSM */ -+ FIQ_HS_ISOC_SLEEPING = 24, -+ FIQ_HS_ISOC_DONE = 25, -+ FIQ_HS_ISOC_ABORTED = 26, -+ FIQ_DEQUEUE_ISSUED = 30, -+ FIQ_TEST = 32, -+}; -+ -+struct fiq_stack { -+ int magic1; -+ uint8_t stack[2048]; -+ int magic2; -+}; -+ -+ -+/** -+ * struct fiq_dma_info - DMA bounce buffer utilisation information (per-channel) -+ * @index: Number of slots reported used for IN transactions / number of slots -+ * transmitted for an OUT transaction -+ * @slot_len[6]: Number of actual transfer bytes in each slot (255 if unused) -+ * -+ * Split transaction transfers can have variable length depending on other bus -+ * traffic. The OTG core DMA engine requires 4-byte aligned addresses therefore -+ * each transaction needs a guaranteed aligned address. A maximum of 6 split transfers -+ * can happen per-frame. -+ */ -+struct fiq_dma_info { -+ u8 index; -+ u8 slot_len[6]; -+}; -+ -+struct __attribute__((packed)) fiq_split_dma_slot { -+ u8 buf[188]; -+}; -+ -+struct fiq_dma_channel { -+ struct __attribute__((packed)) fiq_split_dma_slot index[6]; -+}; -+ -+struct fiq_dma_blob { -+ struct __attribute__((packed)) fiq_dma_channel channel[0]; -+}; -+ -+/** -+ * struct fiq_hs_isoc_info - USB2.0 isochronous data -+ * @iso_frame: Pointer to the array of OTG URB iso_frame_descs. -+ * @nrframes: Total length of iso_frame_desc array -+ * @index: Current index (FIQ-maintained) -+ * -+ */ -+struct fiq_hs_isoc_info { -+ struct dwc_otg_hcd_iso_packet_desc *iso_desc; -+ unsigned int nrframes; -+ unsigned int index; -+}; -+ -+/** -+ * struct fiq_channel_state - FIQ state machine storage -+ * @fsm: Current state of the channel as understood by the FIQ -+ * @nr_errors: Number of transaction errors on this split-transaction -+ * @hub_addr: SSPLIT/CSPLIT destination hub -+ * @port_addr: SSPLIT/CSPLIT destination port - always 1 if single TT hub -+ * @nrpackets: For isoc OUT, the number of split-OUT packets to transmit. For -+ * split-IN, number of CSPLIT data packets that were received. -+ * @hcchar_copy: -+ * @hcsplt_copy: -+ * @hcintmsk_copy: -+ * @hctsiz_copy: Copies of the host channel registers. -+ * For use as scratch, or for returning state. -+ * -+ * The fiq_channel_state is state storage between interrupts for a host channel. The -+ * FSM state is stored here. Members of this structure must only be set up by the -+ * driver prior to enabling the FIQ for this host channel, and not touched until the FIQ -+ * has updated the state to either a COMPLETE state group or ABORT state group. -+ */ -+ -+struct fiq_channel_state { -+ enum fiq_fsm_state fsm; -+ unsigned int nr_errors; -+ unsigned int hub_addr; -+ unsigned int port_addr; -+ /* Hardware bug workaround: sometimes channel halt interrupts are -+ * delayed until the next SOF. Keep track of when we expected to get interrupted. */ -+ unsigned int expected_uframe; -+ /* in/out for communicating number of dma buffers used, or number of ISOC to do */ -+ unsigned int nrpackets; -+ struct fiq_dma_info dma_info; -+ struct fiq_hs_isoc_info hs_isoc_info; -+ /* Copies of HC registers - in/out communication from/to IRQ handler -+ * and for ease of channel setup. A bit of mungeing is performed - for -+ * example the hctsiz.b.maxp is _always_ the max packet size of the endpoint. -+ */ -+ hcchar_data_t hcchar_copy; -+ hcsplt_data_t hcsplt_copy; -+ hcint_data_t hcint_copy; -+ hcintmsk_data_t hcintmsk_copy; -+ hctsiz_data_t hctsiz_copy; -+ hcdma_data_t hcdma_copy; -+}; -+ -+/** -+ * struct fiq_state - top-level FIQ state machine storage -+ * @mphi_regs: virtual address of the MPHI peripheral register file -+ * @dwc_regs_base: virtual address of the base of the DWC core register file -+ * @dma_base: physical address for the base of the DMA bounce buffers -+ * @dummy_send: Scratch area for sending a fake message to the MPHI peripheral -+ * @gintmsk_saved: Top-level mask of interrupts that the FIQ has not handled. -+ * Used for determining which interrupts fired to set off the IRQ handler. -+ * @haintmsk_saved: Mask of interrupts from host channels that the FIQ did not handle internally. -+ * @np_count: Non-periodic transactions in the active queue -+ * @np_sent: Count of non-periodic transactions that have completed -+ * @next_sched_frame: For periodic transactions handled by the driver's SOF-driven queuing mechanism, -+ * this is the next frame on which a SOF interrupt is required. Used to hold off -+ * passing SOF through to the driver until necessary. -+ * @channel[n]: Per-channel FIQ state. Allocated during init depending on the number of host -+ * channels configured into the core logic. -+ * -+ * This is passed as the first argument to the dwc_otg_fiq_fsm top-level FIQ handler from the asm stub. -+ * It contains top-level state information. -+ */ -+struct fiq_state { -+ mphi_regs_t mphi_regs; -+ void *dwc_regs_base; -+ dma_addr_t dma_base; -+ struct fiq_dma_blob *fiq_dmab; -+ void *dummy_send; -+ gintmsk_data_t gintmsk_saved; -+ haintmsk_data_t haintmsk_saved; -+ int mphi_int_count; -+ unsigned int fiq_done; -+ unsigned int kick_np_queues; -+ unsigned int next_sched_frame; -+#ifdef FIQ_DEBUG -+ char * buffer; -+ unsigned int bufsiz; -+#endif -+ struct fiq_channel_state channel[0]; -+}; -+ -+extern int fiq_fsm_too_late(struct fiq_state *st, int n); -+ -+extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); -+ -+extern void dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels); -+ -+extern void dwc_otg_fiq_nop(struct fiq_state *state); -+ -+#endif /* DWC_OTG_FIQ_FSM_H_ */ -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S -new file mode 100644 -index 0000000..ffa8d21 ---- /dev/null -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_stub.S -@@ -0,0 +1,80 @@ -+/* -+ * dwc_otg_fiq_fsm.S - assembly stub for the FSM FIQ -+ * -+ * Copyright (c) 2013 Raspberry Pi Foundation -+ * -+ * Author: Jonathan Bell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * * Neither the name of Raspberry Pi nor the -+ * names of its contributors may be used to endorse or promote products -+ * derived from this software without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+ * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY -+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+#include -+#include -+ -+ -+.text -+ -+.global _dwc_otg_fiq_stub_end; -+ -+/** -+ * _dwc_otg_fiq_stub() - entry copied to the FIQ vector page to allow -+ * a C-style function call with arguments from the FIQ banked registers. -+ * r0 = &hcd->fiq_state -+ * r1 = &hcd->num_channels -+ * r2 = &hcd->dma_buffers -+ * Tramples: r0, r1, r2, r4, fp, ip -+ */ -+ -+ENTRY(_dwc_otg_fiq_stub) -+ /* Stash unbanked regs - SP will have been set up for us */ -+ mov ip, sp; -+ stmdb sp!, {r0-r12, lr}; -+#ifdef FIQ_DEBUG -+ // Cycle profiling - read cycle counter at start -+ mrc p15, 0, r5, c15, c12, 1; -+#endif -+ /* r11 = fp, don't trample it */ -+ mov r4, fp; -+ /* set EABI frame size */ -+ sub fp, ip, #512; -+ -+ /* for fiq NOP mode - just need state */ -+ mov r0, r8; -+ /* r9 = num_channels */ -+ mov r1, r9; -+ /* r10 = struct *dma_bufs */ -+// mov r2, r10; -+ -+ /* r4 = &fiq_c_function */ -+ blx r4; -+#ifdef FIQ_DEBUG -+ mrc p15, 0, r4, c15, c12, 1; -+ subs r5, r5, r4; -+ // r5 is now the cycle count time for executing the FIQ. Store it somewhere? -+#endif -+ ldmia sp!, {r0-r12, lr}; -+ subs pc, lr, #4; -+_dwc_otg_fiq_stub_end: -+END(_dwc_otg_fiq_stub) -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 986d361..130096b 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -45,9 +45,10 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" -+#include "dwc_otg_fiq_fsm.h" - --extern bool microframe_schedule, nak_holdoff_enable; -+extern bool microframe_schedule; -+extern uint16_t fiq_fsm_mask, nak_holdoff; - - //#define DEBUG_HOST_CHANNELS - #ifdef DEBUG_HOST_CHANNELS -@@ -57,12 +58,6 @@ static int last_sel_trans_num_avail_hc_at_start = 0; - static int last_sel_trans_num_avail_hc_at_end = 0; - #endif /* DEBUG_HOST_CHANNELS */ - --extern int g_next_sched_frame, g_np_count, g_np_sent; -- --extern haint_data_t haint_saved; --extern hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; --extern hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; --extern gintsts_data_t ginsts_saved; - - dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) - { -@@ -295,7 +290,7 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - */ - dwc_otg_hcd->flags.b.port_connect_status_change = 1; - dwc_otg_hcd->flags.b.port_connect_status = 0; -- if(fiq_fix_enable) -+ if(fiq_enable) - local_fiq_disable(); - /* - * Shutdown any transfers in process by clearing the Tx FIFO Empty -@@ -392,20 +387,15 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *p) - channel->qh = NULL; - } - } -- if(fiq_split_enable) { -+ if(fiq_fsm_enable) { - for(i=0; i < 128; i++) { - dwc_otg_hcd->hub_port[i] = 0; - } -- haint_saved.d32 = 0; -- for(i=0; i < MAX_EPS_CHANNELS; i++) { -- hcint_saved[i].d32 = 0; -- hcintmsk_saved[i].d32 = 0; -- } - } - - } - -- if(fiq_fix_enable) -+ if(fiq_enable) - local_fiq_enable(); - - if (dwc_otg_hcd->fops->disconnect) { -@@ -542,7 +532,7 @@ int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, - } - #endif - intr_mask.d32 = DWC_READ_REG32(&hcd->core_if->core_global_regs->gintmsk); -- if(!intr_mask.b.sofintr) needs_scheduling = 1; -+ if(!intr_mask.b.sofintr || fiq_enable) needs_scheduling = 1; - if((((dwc_otg_qh_t *)ep_handle)->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) - /* Do not schedule SG transactions until qtd has URB_GIVEBACK_ASAP set */ - needs_scheduling = 0; -@@ -613,6 +603,7 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - if (urb_qtd->in_process && qh->channel) { - /* The QTD is in process (it has been assigned to a channel). */ - if (hcd->flags.b.port_connect_status) { -+ int n = qh->channel->hc_num; - /* - * If still connected (i.e. in host mode), halt the - * channel so it can be used for other transfers. If -@@ -620,10 +611,16 @@ int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, - * written to halt the channel since the core is in - * device mode. - */ -- dwc_otg_hc_halt(hcd->core_if, qh->channel, -- DWC_OTG_HC_XFER_URB_DEQUEUE); -- -- dwc_otg_hcd_release_port(hcd, qh); -+ /* In FIQ FSM mode, we need to shut down carefully. -+ * The FIQ may attempt to restart a disabled channel */ -+ if (fiq_fsm_enable && (hcd->fiq_state->channel[n].fsm != FIQ_PASSTHROUGH)) { -+ qh->channel->halt_status = DWC_OTG_HC_XFER_URB_DEQUEUE; -+ qh->channel->halt_pending = 1; -+ hcd->fiq_state->channel[n].fsm = FIQ_DEQUEUE_ISSUED; -+ } else { -+ dwc_otg_hc_halt(hcd->core_if, qh->channel, -+ DWC_OTG_HC_XFER_URB_DEQUEUE); -+ } - } - } - -@@ -759,7 +756,6 @@ static void completion_tasklet_func(void *ptr) - - usb_hcd_giveback_urb(hcd->priv, urb, urb->status); - -- fiq_print(FIQDBG_PORTHUB, "COMPLETE"); - - DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); - } -@@ -854,6 +850,34 @@ void dwc_otg_hcd_power_up(void *ptr) - cil_hcd_start(core_if); - } - -+void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num) -+{ -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ struct fiq_dma_blob *blob = hcd->fiq_dmab; -+ int i; -+ -+ st->fsm = FIQ_PASSTHROUGH; -+ st->hcchar_copy.d32 = 0; -+ st->hcsplt_copy.d32 = 0; -+ st->hcint_copy.d32 = 0; -+ st->hcintmsk_copy.d32 = 0; -+ st->hctsiz_copy.d32 = 0; -+ st->hcdma_copy.d32 = 0; -+ st->nr_errors = 0; -+ st->hub_addr = 0; -+ st->port_addr = 0; -+ st->expected_uframe = 0; -+ st->nrpackets = 0; -+ st->dma_info.index = 0; -+ for (i = 0; i < 6; i++) -+ st->dma_info.slot_len[i] = 255; -+ st->hs_isoc_info.index = 0; -+ st->hs_isoc_info.iso_desc = NULL; -+ st->hs_isoc_info.nrframes = 0; -+ -+ DWC_MEMSET(&blob->channel[num].index[0], 0x6b, 1128); -+} -+ - /** - * Frees secondary storage associated with the dwc_otg_hcd structure contained - * in the struct usb_hcd field. -@@ -907,6 +931,7 @@ static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) - DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); - DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); - DWC_TASK_FREE(dwc_otg_hcd->completion_tasklet); -+ DWC_FREE(dwc_otg_hcd->fiq_state); - - #ifdef DWC_DEV_SRPCAP - if (dwc_otg_hcd->core_if->power_down == 2 && -@@ -984,6 +1009,59 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - channel); - } - -+ if (fiq_enable) { -+ hcd->fiq_state = DWC_ALLOC(sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels)); -+ if (!hcd->fiq_state) { -+ retval = -DWC_E_NO_MEMORY; -+ DWC_ERROR("%s: cannot allocate fiq_state structure\n", __func__); -+ dwc_otg_hcd_free(hcd); -+ goto out; -+ } -+ DWC_MEMSET(hcd->fiq_state, 0, (sizeof(struct fiq_state) + (sizeof(struct fiq_channel_state) * num_channels))); -+ -+ for (i = 0; i < num_channels; i++) { -+ hcd->fiq_state->channel[i].fsm = FIQ_PASSTHROUGH; -+ } -+ hcd->fiq_state->dummy_send = DWC_ALLOC_ATOMIC(16); -+ -+ hcd->fiq_stack = DWC_ALLOC(sizeof(struct fiq_stack)); -+ if (!hcd->fiq_stack) { -+ retval = -DWC_E_NO_MEMORY; -+ DWC_ERROR("%s: cannot allocate fiq_stack structure\n", __func__); -+ dwc_otg_hcd_free(hcd); -+ goto out; -+ } -+ hcd->fiq_stack->magic1 = 0xDEADBEEF; -+ hcd->fiq_stack->magic2 = 0xD00DFEED; -+ hcd->fiq_state->gintmsk_saved.d32 = ~0; -+ hcd->fiq_state->haintmsk_saved.b2.chint = ~0; -+ -+ /* This bit is terrible and uses no API, but necessary. The FIQ has no concept of DMA pools -+ * (and if it did, would be a lot slower). This allocates a chunk of memory (~9kiB for 8 host channels) -+ * for use as transaction bounce buffers in a 2-D array. Our access into this chunk is done by some -+ * moderately readable array casts. -+ */ -+ hcd->fiq_dmab = DWC_DMA_ALLOC((sizeof(struct fiq_dma_channel) * num_channels), &hcd->fiq_state->dma_base); -+ DWC_WARN("FIQ DMA bounce buffers: virt = 0x%08x dma = 0x%08x len=%d", -+ (unsigned int)hcd->fiq_dmab, (unsigned int)hcd->fiq_state->dma_base, -+ sizeof(struct fiq_dma_channel) * num_channels); -+ -+ DWC_MEMSET(hcd->fiq_dmab, 0x6b, 9024); -+ -+ /* pointer for debug in fiq_print */ -+ hcd->fiq_state->fiq_dmab = hcd->fiq_dmab; -+ if (fiq_fsm_enable) { -+ int i; -+ for (i=0; i < hcd->core_if->core_params->host_channels; i++) { -+ dwc_otg_cleanup_fiq_channel(hcd, i); -+ } -+ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s", -+ (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", -+ (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", -+ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : ""); -+ } -+ } -+ - /* Initialize the Connection timeout timer. */ - hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", - dwc_otg_hcd_connect_timeout, 0); -@@ -1181,7 +1259,8 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->do_split = 1; - hc->xact_pos = qtd->isoc_split_pos; - /* We don't need to do complete splits anymore */ -- if(fiq_split_enable) -+// if(fiq_fsm_enable) -+ if (0) - hc->complete_split = qtd->complete_split = 0; - else - hc->complete_split = qtd->complete_split; -@@ -1332,62 +1411,487 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->qh = qh; - } - --/* --** Check the transaction to see if the port / hub has already been assigned for --** a split transaction --** --** Return 0 - Port is already in use --*/ --int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+ -+/** -+ * fiq_fsm_transaction_suitable() - Test a QH for compatibility with the FIQ -+ * @qh: pointer to the endpoint's queue head -+ * -+ * Transaction start/end control flow is grafted onto the existing dwc_otg -+ * mechanisms, to avoid spaghettifying the functions more than they already are. -+ * This function's eligibility check is altered by debug parameter. -+ * -+ * Returns: 0 for unsuitable, 1 implies the FIQ can be enabled for this transaction. -+ */ -+ -+int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh) - { -- uint32_t hub_addr, port_addr; -+ if (qh->do_split) { -+ switch (qh->ep_type) { -+ case UE_CONTROL: -+ case UE_BULK: -+ if (fiq_fsm_mask & (1 << 0)) -+ return 1; -+ break; -+ case UE_INTERRUPT: -+ case UE_ISOCHRONOUS: -+ if (fiq_fsm_mask & (1 << 1)) -+ return 1; -+ break; -+ default: -+ break; -+ } -+ } else if (qh->ep_type == UE_ISOCHRONOUS) { -+ if (fiq_fsm_mask & (1 << 2)) { -+ /* HS ISOCH support. We test for compatibility: -+ * - DWORD aligned buffers -+ * - Must be at least 2 transfers (otherwise pointless to use the FIQ) -+ * If yes, then the fsm enqueue function will handle the state machine setup. -+ */ -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ dwc_otg_hcd_urb_t *urb = qtd->urb; -+ struct dwc_otg_hcd_iso_packet_desc (*iso_descs)[0] = &urb->iso_descs; -+ int nr_iso_frames = urb->packet_count; -+ int i; -+ uint32_t ptr; -+ -+ if (nr_iso_frames < 2) -+ return 0; -+ for (i = 0; i < nr_iso_frames; i++) { -+ ptr = urb->dma + iso_descs[i]->offset; -+ if (ptr & 0x3) { -+ printk_ratelimited("%s: Non-Dword aligned isochronous frame offset." -+ " Cannot queue FIQ-accelerated transfer to device %d endpoint %d\n", -+ __FUNCTION__, qh->channel->dev_addr, qh->channel->ep_num); -+ return 0; -+ } -+ } -+ return 1; -+ } -+ } -+ return 0; -+} - -- if(!fiq_split_enable) -- return 0; -+/** -+ * fiq_fsm_setup_periodic_dma() - Set up DMA bounce buffers -+ * @hcd: Pointer to the dwc_otg_hcd struct -+ * @qh: Pointer to the endpoint's queue head -+ * -+ * Periodic split transactions are transmitted modulo 188 bytes. -+ * This necessitates slicing data up into buckets for isochronous out -+ * and fixing up the DMA address for all IN transfers. -+ * -+ * Returns 1 if the DMA bounce buffers have been used, 0 if the default -+ * HC buffer has been used. -+ */ -+int fiq_fsm_setup_periodic_dma(dwc_otg_hcd_t *hcd, struct fiq_channel_state *st, dwc_otg_qh_t *qh) -+ { -+ int frame_length, i = 0; -+ uint8_t *ptr = NULL; -+ dwc_hc_t *hc = qh->channel; -+ struct fiq_dma_blob *blob; -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc; -+ -+ for (i = 0; i < 6; i++) { -+ st->dma_info.slot_len[i] = 255; -+ } -+ st->dma_info.index = 0; -+ i = 0; -+ if (hc->ep_is_in) { -+ /* -+ * Set dma_regs to bounce buffer. FIQ will update the -+ * state depending on transaction progress. -+ */ -+ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; -+ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; -+ /* Calculate the max number of CSPLITS such that the FIQ can time out -+ * a transaction if it fails. -+ */ -+ frame_length = st->hcchar_copy.b.mps; -+ do { -+ i++; -+ frame_length -= 188; -+ } while (frame_length >= 0); -+ st->nrpackets = i; -+ return 1; -+ } else { -+ if (qh->ep_type == UE_ISOCHRONOUS) { - -- hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - -- if(hcd->hub_port[hub_addr] & (1 << port_addr)) -- { -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:S%02d", hub_addr, port_addr, qh->skip_count); -+ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ frame_length = frame_desc->length; - -- qh->skip_count++; -+ /* Virtual address for bounce buffers */ -+ blob = hcd->fiq_dmab; - -- if(qh->skip_count > 40000) -- { -- printk_once(KERN_ERR "Error: Having to skip port allocation"); -- local_fiq_disable(); -- BUG(); -+ ptr = qtd->urb->buf + frame_desc->offset; -+ if (frame_length == 0) { -+ /* -+ * for isochronous transactions, we must still transmit a packet -+ * even if the length is zero. -+ */ -+ st->dma_info.slot_len[0] = 0; -+ st->nrpackets = 1; -+ } else { -+ do { -+ if (frame_length <= 188) { -+ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, frame_length); -+ st->dma_info.slot_len[i] = frame_length; -+ ptr += frame_length; -+ } else { -+ dwc_memcpy(&blob->channel[hc->hc_num].index[i].buf[0], ptr, 188); -+ st->dma_info.slot_len[i] = 188; -+ ptr += 188; -+ } -+ i++; -+ frame_length -= 188; -+ } while (frame_length > 0); -+ st->nrpackets = i; -+ } -+ ptr = qtd->urb->buf + frame_desc->offset; -+ /* Point the HC at the DMA address of the bounce buffers */ -+ blob = (struct fiq_dma_blob *) hcd->fiq_state->dma_base; -+ st->hcdma_copy.d32 = (uint32_t) &blob->channel[hc->hc_num].index[0].buf[0]; -+ -+ /* fixup xfersize to the actual packet size */ -+ st->hctsiz_copy.b.pid = 0; -+ st->hctsiz_copy.b.xfersize = st->dma_info.slot_len[0]; -+ return 1; -+ } else { -+ /* For interrupt, single OUT packet required, goes in the SSPLIT from hc_buff. */ - return 0; - } -- return 1; - } -- else -- { -- qh->skip_count = 0; -- hcd->hub_port[hub_addr] |= 1 << port_addr; -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:A %d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hub_addr * 16 + port_addr] = dwc_otg_hcd_get_frame_number(hcd); --#endif -+} -+ -+/* -+ * Pushing a periodic request into the queue near the EOF1 point -+ * in a microframe causes erroneous behaviour (frmovrun) interrupt. -+ * Usually, the request goes out on the bus causing a transfer but -+ * the core does not transfer the data to memory. -+ * This guard interval (in number of 60MHz clocks) is required which -+ * must cater for CPU latency between reading the value and enabling -+ * the channel. -+ */ -+#define PERIODIC_FRREM_BACKOFF 1000 -+ -+int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) -+{ -+ dwc_hc_t *hc = qh->channel; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -+ int frame; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; -+ int xfer_len, nrpackets; -+ hcdma_data_t hcdma; -+ hfnum_data_t hfnum; -+ -+ if (st->fsm != FIQ_PASSTHROUGH) - return 0; -+ -+ st->nr_errors = 0; -+ -+ st->hcchar_copy.d32 = 0; -+ st->hcchar_copy.b.mps = hc->max_packet; -+ st->hcchar_copy.b.epdir = hc->ep_is_in; -+ st->hcchar_copy.b.devaddr = hc->dev_addr; -+ st->hcchar_copy.b.epnum = hc->ep_num; -+ st->hcchar_copy.b.eptype = hc->ep_type; -+ -+ st->hcintmsk_copy.b.chhltd = 1; -+ -+ frame = dwc_otg_hcd_get_frame_number(hcd); -+ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; -+ -+ st->hcchar_copy.b.lspddev = 0; -+ /* Enable the channel later as a final register write. */ -+ -+ st->hcsplt_copy.d32 = 0; -+ -+ st->hs_isoc_info.iso_desc = (struct dwc_otg_hcd_iso_packet_desc *) &qtd->urb->iso_descs; -+ st->hs_isoc_info.nrframes = qtd->urb->packet_count; -+ /* grab the next DMA address offset from the array */ -+ st->hcdma_copy.d32 = qtd->urb->dma; -+ hcdma.d32 = st->hcdma_copy.d32 + st->hs_isoc_info.iso_desc[0].offset; -+ -+ /* We need to set multi_count. This is a bit tricky - has to be set per-transaction as -+ * the core needs to be told to send the correct number. Caution: for IN transfers, -+ * this is always set to the maximum size of the endpoint. */ -+ xfer_len = st->hs_isoc_info.iso_desc[0].length; -+ nrpackets = (xfer_len + st->hcchar_copy.b.mps - 1) / st->hcchar_copy.b.mps; -+ if (nrpackets == 0) -+ nrpackets = 1; -+ st->hcchar_copy.b.multicnt = nrpackets; -+ st->hctsiz_copy.b.pktcnt = nrpackets; -+ -+ /* Initial PID also needs to be set */ -+ if (st->hcchar_copy.b.epdir == 0) { -+ st->hctsiz_copy.b.xfersize = xfer_len; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_MDATA; -+ break; -+ } -+ -+ } else { -+ st->hctsiz_copy.b.xfersize = nrpackets * st->hcchar_copy.b.mps; -+ switch (st->hcchar_copy.b.multicnt) { -+ case 1: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA0; -+ break; -+ case 2: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA1; -+ break; -+ case 3: -+ st->hctsiz_copy.b.pid = DWC_PID_DATA2; -+ break; -+ } - } -+ -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d ", hc->hc_num); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcchar_copy.d32); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); -+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -+ local_fiq_disable(); -+ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); -+ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { -+ /* Prevent queueing near EOF1. Bad things happen if a periodic -+ * split transaction is queued very close to EOF. -+ */ -+ st->fsm = FIQ_HS_ISOC_SLEEPING; -+ } else { -+ st->fsm = FIQ_HS_ISOC_TURBO; -+ st->hcchar_copy.b.chen = 1; -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ } -+ mb(); -+ st->hcchar_copy.b.chen = 0; -+ local_fiq_enable(); -+ return 0; - } --void dwc_otg_hcd_release_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh) -+ -+ -+/** -+ * fiq_fsm_queue_split_transaction() - Set up a host channel and FIQ state -+ * @hcd: Pointer to the dwc_otg_hcd struct -+ * @qh: Pointer to the endpoint's queue head -+ * -+ * This overrides the dwc_otg driver's normal method of queueing a transaction. -+ * Called from dwc_otg_hcd_queue_transactions(), this performs specific setup -+ * for the nominated host channel. -+ * -+ * For periodic transfers, it also peeks at the FIQ state to see if an immediate -+ * start is possible. If not, then the FIQ is left to start the transfer. -+ */ -+int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - { -- uint32_t hub_addr, port_addr; -+ int start_immediate = 1, i; -+ hfnum_data_t hfnum; -+ dwc_hc_t *hc = qh->channel; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; -+ /* Program HC registers, setup FIQ_state, examine FIQ if periodic, start transfer (not if uframe 5) */ -+ int hub_addr, port_addr, frame, uframe; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[hc->hc_num]; - -- if(!fiq_split_enable) -- return; -+ if (st->fsm != FIQ_PASSTHROUGH) -+ return 0; -+ st->nr_errors = 0; -+ -+ st->hcchar_copy.d32 = 0; -+ st->hcchar_copy.b.mps = hc->max_packet; -+ st->hcchar_copy.b.epdir = hc->ep_is_in; -+ st->hcchar_copy.b.devaddr = hc->dev_addr; -+ st->hcchar_copy.b.epnum = hc->ep_num; -+ st->hcchar_copy.b.eptype = hc->ep_type; -+ if (hc->ep_type & 0x1) { -+ if (hc->ep_is_in) -+ st->hcchar_copy.b.multicnt = 3; -+ else -+ /* Docs say set this to 1, but driver sets to 0! */ -+ st->hcchar_copy.b.multicnt = 0; -+ } else { -+ st->hcchar_copy.b.multicnt = 1; -+ st->hcchar_copy.b.oddfrm = 0; -+ } -+ st->hcchar_copy.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW) ? 1 : 0; -+ /* Enable the channel later as a final register write. */ -+ -+ st->hcsplt_copy.d32 = 0; -+ if(qh->do_split) { -+ hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ st->hcsplt_copy.b.compsplt = 0; -+ st->hcsplt_copy.b.spltena = 1; -+ // XACTPOS is for isoc-out only but needs initialising anyway. -+ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_ALL; -+ if((qh->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!qh->ep_is_in)) { -+ /* For packetsize 0 < L < 188, ISOC_XACTPOS_ALL. -+ * for longer than this, ISOC_XACTPOS_BEGIN and the FIQ -+ * will update as necessary. -+ */ -+ if (hc->xfer_len > 188) { -+ st->hcsplt_copy.b.xactpos = ISOC_XACTPOS_BEGIN; -+ } -+ } -+ st->hcsplt_copy.b.hubaddr = (uint8_t) hub_addr; -+ st->hcsplt_copy.b.prtaddr = (uint8_t) port_addr; -+ st->hub_addr = hub_addr; -+ st->port_addr = port_addr; -+ } - -- hcd->fops->hub_info(hcd, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->priv, &hub_addr, &port_addr); -+ st->hctsiz_copy.d32 = 0; -+ st->hctsiz_copy.b.dopng = 0; -+ st->hctsiz_copy.b.pid = hc->data_pid_start; - -- hcd->hub_port[hub_addr] &= ~(1 << port_addr); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hub_addr * 16 + port_addr] = -1; --#endif -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:RO%d", hub_addr, port_addr, DWC_CIRCLEQ_FIRST(&qh->qtd_list)->urb->pipe_info.ep_num); -+ if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { -+ hc->xfer_len = hc->max_packet; -+ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { -+ hc->xfer_len = 188; -+ } -+ st->hctsiz_copy.b.xfersize = hc->xfer_len; -+ -+ st->hctsiz_copy.b.pktcnt = 1; - -+ if (hc->ep_type & 0x1) { -+ /* -+ * For potentially multi-packet transfers, must use the DMA bounce buffers. For IN transfers, -+ * the DMA address is the address of the first 188byte slot buffer in the bounce buffer array. -+ * For multi-packet OUT transfers, we need to copy the data into the bounce buffer array so the FIQ can punt -+ * the right address out as necessary. hc->xfer_buff and hc->xfer_len have already been set -+ * in assign_and_init_hc(), but this is for the eventual transaction completion only. The FIQ -+ * must not touch internal driver state. -+ */ -+ if(!fiq_fsm_setup_periodic_dma(hcd, st, qh)) { -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); -+ } -+ } -+ } else { -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); -+ } -+ } -+ /* The FIQ depends upon no other interrupts being enabled except channel halt. -+ * Fixup channel interrupt mask. */ -+ st->hcintmsk_copy.d32 = 0; -+ st->hcintmsk_copy.b.chhltd = 1; -+ st->hcintmsk_copy.b.ahberr = 1; -+ -+ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); -+ -+ local_fiq_disable(); -+ mb(); -+ -+ if (hc->ep_type & 0x1) { -+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -+ frame = (hfnum.b.frnum & ~0x7) >> 3; -+ uframe = hfnum.b.frnum & 0x7; -+ if (hfnum.b.frrem < PERIODIC_FRREM_BACKOFF) { -+ /* Prevent queueing near EOF1. Bad things happen if a periodic -+ * split transaction is queued very close to EOF. -+ */ -+ start_immediate = 0; -+ } else if (uframe == 5) { -+ start_immediate = 0; -+ } else if (hc->ep_type == UE_ISOCHRONOUS && !hc->ep_is_in) { -+ start_immediate = 0; -+ } else if (hc->ep_is_in && fiq_fsm_too_late(hcd->fiq_state, hc->hc_num)) { -+ start_immediate = 0; -+ } else { -+ /* Search through all host channels to determine if a transaction -+ * is currently in progress */ -+ for (i = 0; i < hcd->core_if->core_params->host_channels; i++) { -+ if (i == hc->hc_num || hcd->fiq_state->channel[i].fsm == FIQ_PASSTHROUGH) -+ continue; -+ switch (hcd->fiq_state->channel[i].fsm) { -+ /* TT is reserved for channels that are in the middle of a periodic -+ * split transaction. -+ */ -+ case FIQ_PER_SSPLIT_STARTED: -+ case FIQ_PER_CSPLIT_WAIT: -+ case FIQ_PER_CSPLIT_NYET1: -+ case FIQ_PER_CSPLIT_POLL: -+ case FIQ_PER_ISO_OUT_ACTIVE: -+ case FIQ_PER_ISO_OUT_LAST: -+ if (hcd->fiq_state->channel[i].hub_addr == hub_addr && -+ hcd->fiq_state->channel[i].port_addr == port_addr) { -+ start_immediate = 0; -+ } -+ break; -+ default: -+ break; -+ } -+ if (!start_immediate) -+ break; -+ } -+ } -+ } -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hctsiz_copy.d32); -+ //fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); -+ switch (hc->ep_type) { -+ case UE_CONTROL: -+ case UE_BULK: -+ st->fsm = FIQ_NP_SSPLIT_STARTED; -+ break; -+ case UE_ISOCHRONOUS: -+ if (hc->ep_is_in) { -+ if (start_immediate) { -+ st->fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; -+ } -+ } else { -+ if (start_immediate) { -+ /* Single-isoc OUT packets don't require FIQ involvement */ -+ if (st->nrpackets == 1) { -+ st->fsm = FIQ_PER_ISO_OUT_LAST; -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_ACTIVE; -+ } -+ } else { -+ st->fsm = FIQ_PER_ISO_OUT_PENDING; -+ } -+ } -+ break; -+ case UE_INTERRUPT: -+ if (start_immediate) { -+ st->fsm = FIQ_PER_SSPLIT_STARTED; -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; -+ } -+ default: -+ break; -+ } -+ if (start_immediate) { -+ /* Set the oddfrm bit as close as possible to actual queueing */ -+ frame = dwc_otg_hcd_get_frame_number(hcd); -+ st->expected_uframe = (frame + 1) & 0x3FFF; -+ st->hcchar_copy.b.oddfrm = (frame & 0x1) ? 0 : 1; -+ st->hcchar_copy.b.chen = 1; -+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); -+ } -+ mb(); -+ local_fiq_enable(); -+ return 0; - } - - -@@ -1404,16 +1908,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - { - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -- dwc_otg_qtd_t *qtd; - int num_channels; - dwc_irqflags_t flags; - dwc_spinlock_t *channel_lock = hcd->channel_lock; - dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; - --#ifdef DEBUG_SOF -- DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); --#endif -- - #ifdef DEBUG_HOST_CHANNELS - last_sel_trans_num_per_scheduled = 0; - last_sel_trans_num_nonper_scheduled = 0; -@@ -1428,26 +1927,11 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); - -- if(qh->do_split) { -- qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); -- if(!(qh->ep_type == UE_ISOCHRONOUS && -- (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || -- qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END))) { -- if(dwc_otg_hcd_allocate_port(hcd, qh)) -- { -- qh_ptr = DWC_LIST_NEXT(qh_ptr); -- g_next_sched_frame = dwc_frame_num_inc(dwc_otg_hcd_get_frame_number(hcd), 1); -- continue; -- } -- } -- } -- - if (microframe_schedule) { - // Make sure we leave one channel for non periodic transactions. - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - if (hcd->available_host_channels <= 1) { - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); -- if(qh->do_split) dwc_otg_hcd_release_port(hcd, qh); - break; - } - hcd->available_host_channels--; -@@ -1483,27 +1967,24 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { - - qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -- - /* - * Check to see if this is a NAK'd retransmit, in which case ignore for retransmission - * we hold off on bulk retransmissions to reduce NAK interrupt overhead for full-speed - * cheeky devices that just hold off using NAKs - */ -- if (nak_holdoff_enable && qh->do_split) { -- if (qh->nak_frame != 0xffff && -- dwc_full_frame_num(qh->nak_frame) == -- dwc_full_frame_num(dwc_otg_hcd_get_frame_number(hcd))) { -- /* -- * Revisit: Need to avoid trampling on periodic scheduling. -- * Currently we are safe because g_np_count != g_np_sent whenever we hit this, -- * but if this behaviour is changed then periodic endpoints will get a slower -- * polling rate. -- */ -- g_next_sched_frame = ((qh->nak_frame + 8) & ~7) & DWC_HFNUM_MAX_FRNUM; -- qh_ptr = DWC_LIST_NEXT(qh_ptr); -- continue; -- } else { -- qh->nak_frame = 0xffff; -+ if (nak_holdoff && qh->do_split) { -+ if (qh->nak_frame != 0xffff) { -+ uint16_t next_frame = dwc_frame_num_inc(qh->nak_frame, (qh->ep_type == UE_BULK) ? nak_holdoff : 8); -+ uint16_t frame = dwc_otg_hcd_get_frame_number(hcd); -+ if (dwc_frame_num_le(frame, next_frame)) { -+ if(dwc_frame_num_le(next_frame, hcd->fiq_state->next_sched_frame)) { -+ hcd->fiq_state->next_sched_frame = next_frame; -+ } -+ qh_ptr = DWC_LIST_NEXT(qh_ptr); -+ continue; -+ } else { -+ qh->nak_frame = 0xFFFF; -+ } - } - } - -@@ -1532,12 +2013,31 @@ dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) - &qh->qh_list_entry); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - -- g_np_sent++; - - if (!microframe_schedule) - hcd->non_periodic_channels++; - } -- -+ /* we moved a non-periodic QH to the active schedule. If the inactive queue is empty, -+ * stop the FIQ from kicking us. We could potentially still have elements here if we -+ * ran out of host channels. -+ */ -+ if (fiq_enable) { -+ if (DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) { -+ hcd->fiq_state->kick_np_queues = 0; -+ } else { -+ /* For each entry remaining in the NP inactive queue, -+ * if this a NAK'd retransmit then don't set the kick flag. -+ */ -+ if(nak_holdoff) { -+ DWC_LIST_FOREACH(qh_ptr, &hcd->non_periodic_sched_inactive) { -+ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); -+ if (qh->nak_frame == 0xFFFF) { -+ hcd->fiq_state->kick_np_queues = 1; -+ } -+ } -+ } -+ } -+ } - if(!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) - ret_val |= DWC_OTG_TRANSACTION_PERIODIC; - -@@ -1582,6 +2082,12 @@ static int queue_transaction(dwc_otg_hcd_t * hcd, - hc->qh->ping_state = 0; - } - } else if (!hc->xfer_started) { -+ if (fiq_fsm_enable && hc->error_state) { -+ hcd->fiq_state->channel[hc->hc_num].nr_errors = -+ DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list)->error_count; -+ hcd->fiq_state->channel[hc->hc_num].fsm = -+ FIQ_PASSTHROUGH_ERRORSTATE; -+ } - dwc_otg_hc_start_transfer(hcd->core_if, hc); - hc->qh->ping_state = 0; - } -@@ -1634,7 +2140,7 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - hptxsts_data_t tx_status; - dwc_list_link_t *qh_ptr; - dwc_otg_qh_t *qh; -- int status; -+ int status = 0; - int no_queue_space = 0; - int no_fifo_space = 0; - -@@ -1663,27 +2169,34 @@ static void process_periodic_channels(dwc_otg_hcd_t * hcd) - - // Do not send a split start transaction any later than frame .6 - // Note, we have to schedule a periodic in .5 to make it go in .6 -- if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -+ if(fiq_fsm_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) - { - qh_ptr = qh_ptr->next; -- g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -+ hcd->fiq_state->next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; - continue; - } - -- /* -- * Set a flag if we're queuing high-bandwidth in slave mode. -- * The flag prevents any halts to get into the request queue in -- * the middle of multiple high-bandwidth packets getting queued. -- */ -- if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -- hcd->core_if->queuing_high_bandwidth = 1; -- } -- status = -- queue_transaction(hcd, qh->channel, -- tx_status.b.ptxfspcavail); -- if (status < 0) { -- no_fifo_space = 1; -- break; -+ if (fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { -+ if (qh->do_split) -+ fiq_fsm_queue_split_transaction(hcd, qh); -+ else -+ fiq_fsm_queue_isoc_transaction(hcd, qh); -+ } else { -+ -+ /* -+ * Set a flag if we're queueing high-bandwidth in slave mode. -+ * The flag prevents any halts to get into the request queue in -+ * the middle of multiple high-bandwidth packets getting queued. -+ */ -+ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { -+ hcd->core_if->queuing_high_bandwidth = 1; -+ } -+ status = queue_transaction(hcd, qh->channel, -+ tx_status.b.ptxfspcavail); -+ if (status < 0) { -+ no_fifo_space = 1; -+ break; -+ } - } - - /* -@@ -1800,25 +2313,19 @@ static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) - qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, - qh_list_entry); - -- // Do not send a split start transaction any later than frame .5 -- // non periodic transactions will start immediately in this uframe -- if(fiq_split_enable && qh->do_split && ((dwc_otg_hcd_get_frame_number(hcd) + 1) & 7) > 6) -- { -- g_next_sched_frame = dwc_otg_hcd_get_frame_number(hcd) | 7; -- break; -- } -- -- status = -- queue_transaction(hcd, qh->channel, -- tx_status.b.nptxfspcavail); -+ if(fiq_fsm_enable && fiq_fsm_transaction_suitable(qh)) { -+ fiq_fsm_queue_split_transaction(hcd, qh); -+ } else { -+ status = queue_transaction(hcd, qh->channel, -+ tx_status.b.nptxfspcavail); - -- if (status > 0) { -- more_to_do = 1; -- } else if (status < 0) { -- no_fifo_space = 1; -- break; -+ if (status > 0) { -+ more_to_do = 1; -+ } else if (status < 0) { -+ no_fifo_space = 1; -+ break; -+ } - } -- - /* Advance to next QH, skipping start-of-list entry. */ - hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; - if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -index 0007fa1..43dbed9 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h -@@ -40,6 +40,8 @@ - #include "dwc_otg_core_if.h" - #include "dwc_list.h" - #include "dwc_otg_cil.h" -+#include "dwc_otg_fiq_fsm.h" -+ - - /** - * @file -@@ -585,6 +587,12 @@ struct dwc_otg_hcd { - /** Frame List DMA address */ - dma_addr_t frame_list_dma; - -+ struct fiq_stack *fiq_stack; -+ struct fiq_state *fiq_state; -+ -+ /** Virtual address for split transaction DMA bounce buffers */ -+ struct fiq_dma_blob *fiq_dmab; -+ - #ifdef DEBUG - uint32_t frrem_samples; - uint64_t frrem_accum; -@@ -615,6 +623,9 @@ extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - int dwc_otg_hcd_allocate_port(dwc_otg_hcd_t * hcd, dwc_otg_qh_t *qh); - void dwc_otg_hcd_release_port(dwc_otg_hcd_t * dwc_otg_hcd, dwc_otg_qh_t *qh); - -+extern int fiq_fsm_queue_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); -+extern int fiq_fsm_transaction_suitable(dwc_otg_qh_t *qh); -+extern void dwc_otg_cleanup_fiq_channel(dwc_otg_hcd_t *hcd, uint32_t num); - - /** @} */ - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 64d33a5..4195ff2 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -34,7 +34,6 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" - - #include - #include -@@ -47,33 +46,8 @@ extern bool microframe_schedule; - * This file contains the implementation of the HCD Interrupt handlers. - */ - --/* -- * Some globals to communicate between the FIQ and INTERRUPT -- */ -- --void * dummy_send; --mphi_regs_t c_mphi_regs; --volatile void *dwc_regs_base; - int fiq_done, int_done; - --gintsts_data_t gintsts_saved = {.d32 = 0}; --hcint_data_t hcint_saved[MAX_EPS_CHANNELS]; --hcintmsk_data_t hcintmsk_saved[MAX_EPS_CHANNELS]; --int split_out_xfersize[MAX_EPS_CHANNELS]; --haint_data_t haint_saved; -- --int g_next_sched_frame, g_np_count, g_np_sent; --static int mphi_int_count = 0 ; -- --hcchar_data_t nak_hcchar; --hctsiz_data_t nak_hctsiz; --hcsplt_data_t nak_hcsplt; --int nak_count; -- --int complete_sched[MAX_EPS_CHANNELS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; --int split_start_frame[MAX_EPS_CHANNELS]; --int queued_port[MAX_EPS_CHANNELS]; -- - #ifdef FIQ_DEBUG - char buffer[1000*16]; - int wptr; -@@ -83,12 +57,10 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) - va_list args; - char text[17]; - hfnum_data_t hfnum = { .d32 = FIQ_READ(dwc_regs_base + 0x408) }; -- unsigned long flags; - -- local_irq_save(flags); -- local_fiq_disable(); - if(dbg_lvl & dbg_lvl_req || dbg_lvl == FIQDBG_ERR) - { -+ local_fiq_disable(); - snprintf(text, 9, "%4d%d:%d ", hfnum.b.frnum/8, hfnum.b.frnum%8, 8 - hfnum.b.frrem/937); - va_start(args, fmt); - vsnprintf(text+8, 9, fmt, args); -@@ -96,410 +68,21 @@ void notrace _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...) - - memcpy(buffer + wptr, text, 16); - wptr = (wptr + 16) % sizeof(buffer); -+ local_fiq_enable(); - } -- local_irq_restore(flags); - } - #endif - --void notrace fiq_queue_request(int channel, int odd_frame) --{ -- hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -- hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -- hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10) }; -- -- if(hcsplt.b.spltena == 0) -- { -- fiq_print(FIQDBG_ERR, "SPLTENA "); -- BUG(); -- } -- -- if(hcchar.b.epdir == 1) -- { -- fiq_print(FIQDBG_SCHED, "IN Ch %d", channel); -- } -- else -- { -- hctsiz.b.xfersize = 0; -- fiq_print(FIQDBG_SCHED, "OUT Ch %d", channel); -- } -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x10), hctsiz.d32); -- -- hcsplt.b.compsplt = 1; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x4), hcsplt.d32); -- -- // Send the Split complete -- hcchar.b.chen = 1; -- hcchar.b.oddfrm = odd_frame ? 1 : 0; -- -- // Post this for transmit on the next frame for periodic or this frame for non-periodic -- fiq_print(FIQDBG_SCHED, "SND_%s", odd_frame ? "ODD " : "EVEN"); -- -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x0), hcchar.d32); --} -- --static int last_sof = -1; -- --/* --** Function to handle the start of frame interrupt, choose whether we need to do anything and --** therefore trigger the main interrupt --** --** returns int != 0 - interrupt has been handled --*/ --int diff; -- --int notrace fiq_sof_handle(hfnum_data_t hfnum) --{ -- int handled = 0; -- int i; -- -- // Just check that once we're running we don't miss a SOF -- /*if(last_sof != -1 && (hfnum.b.frnum != ((last_sof + 1) & 0x3fff))) -- { -- fiq_print(FIQDBG_ERR, "LASTSOF "); -- fiq_print(FIQDBG_ERR, "%4d%d ", last_sof / 8, last_sof & 7); -- fiq_print(FIQDBG_ERR, "%4d%d ", hfnum.b.frnum / 8, hfnum.b.frnum & 7); -- BUG(); -- }*/ -- -- // Only start remembering the last sof when the interrupt has been -- // enabled (we don't check the mask to come in here...) -- if(last_sof != -1 || FIQ_READ(dwc_regs_base + 0x18) & (1<<3)) -- last_sof = hfnum.b.frnum; -- -- for(i = 0; i < MAX_EPS_CHANNELS; i++) -- { -- if(complete_sched[i] != -1) -- { -- if(complete_sched[i] <= hfnum.b.frnum || (complete_sched[i] > 0x3f00 && hfnum.b.frnum < 0xf0)) -- { -- fiq_queue_request(i, hfnum.b.frnum & 1); -- complete_sched[i] = -1; -- } -- } -- -- if(complete_sched[i] != -1) -- { -- // This is because we've seen a split complete occur with no start... -- // most likely because missed the complete 0x3fff frames ago! -- -- diff = (hfnum.b.frnum + 0x3fff - complete_sched[i]) & 0x3fff ; -- if(diff > 32 && diff < 0x3f00) -- { -- fiq_print(FIQDBG_ERR, "SPLTMISS"); -- BUG(); -- } -- } -- } -- -- if(g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -- { -- /* -- * If np_count != np_sent that means we need to queue non-periodic (bulk) packets this packet -- * g_next_sched_frame is the next frame we have periodic packets for -- * -- * if neither of these are required for this frame then just clear the interrupt -- */ -- handled = 1; -- -- } -- -- return handled; --} -- --int notrace port_id(hcsplt_data_t hcsplt) --{ -- return hcsplt.b.prtaddr + (hcsplt.b.hubaddr << 8); --} -- --int notrace fiq_hcintr_handle(int channel, hfnum_data_t hfnum) --{ -- hcchar_data_t hcchar = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x0) }; -- hcsplt_data_t hcsplt = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x4) }; -- hcint_data_t hcint = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x8) }; -- hcintmsk_data_t hcintmsk = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0xc) }; -- hctsiz_data_t hctsiz = { .d32 = FIQ_READ(dwc_regs_base + 0x500 + (channel * 0x20) + 0x10)}; -- -- hcint_saved[channel].d32 |= hcint.d32; -- hcintmsk_saved[channel].d32 = hcintmsk.d32; -- -- if(hcsplt.b.spltena) -- { -- fiq_print(FIQDBG_PORTHUB, "ph: %4x", port_id(hcsplt)); -- if(hcint.b.chhltd) -- { -- fiq_print(FIQDBG_SCHED, "CH HLT %d", channel); -- fiq_print(FIQDBG_SCHED, "%08x", hcint_saved[channel]); -- } -- if(hcint.b.stall || hcint.b.xacterr || hcint.b.bblerr || hcint.b.frmovrun || hcint.b.datatglerr) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_ERR, "CHAN ERR"); -- } -- if(hcint.b.xfercomp) -- { -- // Clear the port allocation and transmit anything also on this port -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "XFERCOMP"); -- } -- if(hcint.b.nak) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "NAK"); -- } -- if(hcint.b.ack && !hcsplt.b.compsplt) -- { -- int i; -- -- // Do not complete isochronous out transactions -- if(hcchar.b.eptype == 1 && hcchar.b.epdir == 0) -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_SCHED, "ISOC_OUT"); -- } -- else -- { -- // Make sure we check the port / hub combination that we sent this split on. -- // Do not queue a second request to the same port -- for(i = 0; i < MAX_EPS_CHANNELS; i++) -- { -- if(port_id(hcsplt) == queued_port[i]) -- { -- fiq_print(FIQDBG_ERR, "PORTERR "); -- //BUG(); -- } -- } -- -- split_start_frame[channel] = (hfnum.b.frnum + 1) & ~7; -- -- // Note, the size of an OUT is in the start split phase, not -- // the complete split -- split_out_xfersize[channel] = hctsiz.b.xfersize; -- -- hcint_saved[channel].b.chhltd = 0; -- hcint_saved[channel].b.ack = 0; -- -- queued_port[channel] = port_id(hcsplt); -- -- if(hcchar.b.eptype & 1) -- { -- // Send the periodic complete in the same oddness frame as the ACK went... -- fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -- // complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -- } -- else -- { -- // Schedule the split complete to occur later -- complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 2); -- fiq_print(FIQDBG_SCHED, "ACK%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -- } -- } -- } -- if(hcint.b.nyet) -- { -- fiq_print(FIQDBG_ERR, "NYETERR1"); -- //BUG(); -- // Can transmit a split complete up to uframe .0 of the next frame -- if(hfnum.b.frnum <= dwc_frame_num_inc(split_start_frame[channel], 8)) -- { -- // Send it next frame -- if(hcchar.b.eptype & 1) // type 1 & 3 are interrupt & isoc -- { -- fiq_print(FIQDBG_SCHED, "NYT:SEND"); -- fiq_queue_request(channel, !(hfnum.b.frnum & 1)); -- } -- else -- { -- // Schedule non-periodic access for next frame (the odd-even bit doesn't effect NP) -- complete_sched[channel] = dwc_frame_num_inc(hfnum.b.frnum, 1); -- fiq_print(FIQDBG_SCHED, "NYT%04d%d", complete_sched[channel]/8, complete_sched[channel]%8); -- } -- hcint_saved[channel].b.chhltd = 0; -- hcint_saved[channel].b.nyet = 0; -- } -- else -- { -- queued_port[channel] = 0; -- fiq_print(FIQDBG_ERR, "NYETERR2"); -- //BUG(); -- } -- } -- } -- else -- { -- /* -- * If we have any of NAK, ACK, Datatlgerr active on a -- * non-split channel, the sole reason is to reset error -- * counts for a previously broken transaction. The FIQ -- * will thrash on NAK IN and ACK OUT in particular so -- * handle it "once" and allow the IRQ to do the rest. -- */ -- hcint.d32 &= hcintmsk.d32; -- if(hcint.b.nak) -- { -- hcintmsk.b.nak = 0; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -- } -- if (hcint.b.ack) -- { -- hcintmsk.b.ack = 0; -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0xc), hcintmsk.d32); -- } -- } -- -- // Clear the interrupt, this will also clear the HAINT bit -- FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32); -- return hcint_saved[channel].d32 == 0; --} -- --gintsts_data_t gintsts; --gintmsk_data_t gintmsk; --// triggered: The set of interrupts that were triggered --// handled: The set of interrupts that have been handled (no IRQ is --// required) --// keep: The set of interrupts we want to keep unmasked even though we --// want to trigger an IRQ to handle it (SOF and HCINTR) --gintsts_data_t triggered, handled, keep; --hfnum_data_t hfnum; -- --void __attribute__ ((naked)) notrace dwc_otg_hcd_handle_fiq(void) --{ -- -- /* entry takes care to store registers we will be treading on here */ -- asm __volatile__ ( -- "mov ip, sp ;" -- /* stash FIQ and normal regs */ -- "stmdb sp!, {r0-r12, lr};" -- /* !! THIS SETS THE FRAME, adjust to > sizeof locals */ -- "sub fp, ip, #512 ;" -- ); -- -- // Cannot put local variables at the beginning of the function -- // because otherwise 'C' will play with the stack pointer. any locals -- // need to be inside the following block -- do -- { -- fiq_done++; -- gintsts.d32 = FIQ_READ(dwc_regs_base + 0x14); -- gintmsk.d32 = FIQ_READ(dwc_regs_base + 0x18); -- hfnum.d32 = FIQ_READ(dwc_regs_base + 0x408); -- triggered.d32 = gintsts.d32 & gintmsk.d32; -- handled.d32 = 0; -- keep.d32 = 0; -- fiq_print(FIQDBG_INT, "FIQ "); -- fiq_print(FIQDBG_INT, "%08x", gintsts.d32); -- fiq_print(FIQDBG_INT, "%08x", gintmsk.d32); -- if(gintsts.d32) -- { -- // If port enabled -- if((FIQ_READ(dwc_regs_base + 0x440) & 0xf) == 0x5) -- { -- if(gintsts.b.sofintr) -- { -- if(fiq_sof_handle(hfnum)) -- { -- handled.b.sofintr = 1; /* Handled in FIQ */ -- } -- else -- { -- /* Keer interrupt unmasked */ -- keep.b.sofintr = 1; -- } -- { -- // Need to make sure the read and clearing of the SOF interrupt is as close as possible to avoid the possibility of missing -- // a start of frame interrupt -- gintsts_data_t gintsts = { .b.sofintr = 1 }; -- FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -- } -- } -- -- if(fiq_split_enable && gintsts.b.hcintr) -- { -- int i; -- haint_data_t haint; -- haintmsk_data_t haintmsk; -- -- haint.d32 = FIQ_READ(dwc_regs_base + 0x414); -- haintmsk.d32 = FIQ_READ(dwc_regs_base + 0x418); -- haint.d32 &= haintmsk.d32; -- haint_saved.d32 |= haint.d32; -- -- fiq_print(FIQDBG_INT, "hcintr"); -- fiq_print(FIQDBG_INT, "%08x", FIQ_READ(dwc_regs_base + 0x414)); -- -- // Go through each channel that has an enabled interrupt -- for(i = 0; i < 16; i++) -- if((haint.d32 >> i) & 1) -- if(fiq_hcintr_handle(i, hfnum)) -- haint_saved.d32 &= ~(1 << i); /* this was handled */ -- -- /* If we've handled all host channel interrupts then don't trigger the interrupt */ -- if(haint_saved.d32 == 0) -- { -- handled.b.hcintr = 1; -- } -- else -- { -- /* Make sure we keep the channel interrupt unmasked when triggering the IRQ */ -- keep.b.hcintr = 1; -- } -- -- { -- gintsts_data_t gintsts = { .b.hcintr = 1 }; -- -- // Always clear the channel interrupt -- FIQ_WRITE((dwc_regs_base + 0x14), gintsts.d32); -- } -- } -- } -- else -- { -- last_sof = -1; -- } -- } -- -- // Mask out the interrupts triggered - those handled - don't mask out the ones we want to keep -- gintmsk.d32 = keep.d32 | (gintmsk.d32 & ~(triggered.d32 & ~handled.d32)); -- // Save those that were triggered but not handled -- gintsts_saved.d32 |= triggered.d32 & ~handled.d32; -- FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -- -- // Clear and save any unhandled interrupts and trigger the interrupt -- if(gintsts_saved.d32) -- { -- /* To enable the MPHI interrupt (INT 32) -- */ -- FIQ_WRITE( c_mphi_regs.outdda, (int) dummy_send); -- FIQ_WRITE( c_mphi_regs.outddb, (1 << 29)); -- -- mphi_int_count++; -- } -- } -- while(0); -- -- mb(); -- -- /* exit back to normal mode restoring everything */ -- asm __volatile__ ( -- /* return FIQ regs back to pristine state -- * and get normal regs back -- */ -- "ldmia sp!, {r0-r12, lr};" -- -- /* return */ -- "subs pc, lr, #4;" -- ); --} -- - /** This function handles interrupts for the HCD. */ - int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int retval = 0; - static int last_time; -- - dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; - gintsts_data_t gintsts; - gintmsk_data_t gintmsk; - hfnum_data_t hfnum; -+ haintmsk_data_t haintmsk; - - #ifdef DEBUG - dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; -@@ -516,15 +99,29 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - DWC_SPINLOCK(dwc_otg_hcd->lock); - /* Check if HOST Mode */ - if (dwc_otg_is_host_mode(core_if)) { -- local_fiq_disable(); -- gintmsk.d32 |= gintsts_saved.d32; -- gintsts.d32 |= gintsts_saved.d32; -- gintsts_saved.d32 = 0; -- local_fiq_enable(); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ /* Pull in from the FIQ's disabled mask */ -+ gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); -+ dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; -+ } -+ -+ if (fiq_fsm_enable && ( 0x0000FFFF & ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint))) { -+ gintsts.b.hcintr = 1; -+ } -+ -+ /* Danger will robinson: fake a SOF if necessary */ -+ if (fiq_fsm_enable && (dwc_otg_hcd->fiq_state->gintmsk_saved.b.sofintr == 1)) { -+ gintsts.b.sofintr = 1; -+ } -+ gintsts.d32 &= gintmsk.d32; -+ -+ if (fiq_enable) -+ local_fiq_enable(); -+ - if (!gintsts.d32) { - goto exit_handler_routine; - } -- gintsts.d32 &= gintmsk.d32; - - #ifdef DEBUG - // We should be OK doing this because the common interrupts should already have been serviced -@@ -544,12 +141,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - gintsts.d32, core_if); - #endif - hfnum.d32 = DWC_READ_REG32(&dwc_otg_hcd->core_if->host_if->host_global_regs->hfnum); -- if (gintsts.b.sofintr && g_np_count == g_np_sent && dwc_frame_num_gt(g_next_sched_frame, hfnum.b.frnum)) -- { -- /* Note, we should never get here if the FIQ is doing it's job properly*/ -- retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); -- } -- else if (gintsts.b.sofintr) { -+ if (gintsts.b.sofintr) { - retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); - } - -@@ -604,37 +196,43 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - } - - exit_handler_routine: -- -- if (fiq_fix_enable) -- { -+ if (fiq_enable) { -+ gintmsk_data_t gintmsk_new; -+ haintmsk_data_t haintmsk_new; - local_fiq_disable(); -- // Make sure that we don't clear the interrupt if we've still got pending work to do -- if(gintsts_saved.d32 == 0) -- { -- /* Clear the MPHI interrupt */ -- DWC_WRITE_REG32(c_mphi_regs.intstat, (1<<16)); -- if (mphi_int_count >= 60) -- { -- DWC_WRITE_REG32(c_mphi_regs.ctrl, ((1<<31) + (1<<16))); -- while(!(DWC_READ_REG32(c_mphi_regs.ctrl) & (1 << 17))) -- ; -- DWC_WRITE_REG32(c_mphi_regs.ctrl, (1<<31)); -- mphi_int_count = 0; -- } -- int_done++; -+ gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; -+ if(fiq_fsm_enable) -+ haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; -+ else -+ haintmsk_new.d32 = 0x0000FFFF; -+ -+ /* The FIQ could have sneaked another interrupt in. If so, don't clear MPHI */ -+ if ((gintmsk_new.d32 == ~0) && (haintmsk_new.d32 == 0x0000FFFF)) { -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.intstat, (1<<16)); -+ if (dwc_otg_hcd->fiq_state->mphi_int_count >= 50) { -+ fiq_print(FIQDBG_INT, dwc_otg_hcd->fiq_state, "MPHI CLR"); -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, ((1<<31) + (1<<16))); -+ while (!(DWC_READ_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & (1 << 17))) -+ ; -+ DWC_WRITE_REG32(dwc_otg_hcd->fiq_state->mphi_regs.ctrl, (1<<31)); -+ dwc_otg_hcd->fiq_state->mphi_int_count = 0; -+ } -+ int_done++; - } -- -- // Unmask handled interrupts -- FIQ_WRITE(dwc_regs_base + 0x18, gintmsk.d32); -- //DWC_MODIFY_REG32((uint32_t *)IO_ADDRESS(USB_BASE + 0x8), 0 , 1); -- -+ haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); -+ /* Re-enable interrupts that the FIQ masked (first time round) */ -+ FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); - local_fiq_enable(); - -- if((jiffies / HZ) > last_time) -- { -+ if ((jiffies / HZ) > last_time) { -+ //dwc_otg_qh_t *qh; -+ //dwc_list_link_t *cur; - /* Once a second output the fiq and irq numbers, useful for debug */ - last_time = jiffies / HZ; -- DWC_DEBUGPL(DBG_USER, "int_done = %d fiq_done = %d\n", int_done, fiq_done); -+ // DWC_WARN("np_kick=%d AHC=%d sched_frame=%d cur_frame=%d int_done=%d fiq_done=%d", -+ // dwc_otg_hcd->fiq_state->kick_np_queues, dwc_otg_hcd->available_host_channels, -+ // dwc_otg_hcd->fiq_state->next_sched_frame, hfnum.b.frnum, int_done, dwc_otg_hcd->fiq_state->fiq_done); -+ //printk(KERN_WARNING "Periodic queues:\n"); - } - } - -@@ -686,6 +284,7 @@ static inline void track_missed_sofs(uint16_t curr_frame_number) - int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - { - hfnum_data_t hfnum; -+ gintsts_data_t gintsts = { .d32 = 0 }; - dwc_list_link_t *qh_entry; - dwc_otg_qh_t *qh; - dwc_otg_transaction_type_e tr_type; -@@ -732,8 +331,8 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - } - } - } -- -- g_next_sched_frame = next_sched_frame; -+ if (fiq_enable) -+ hcd->fiq_state->next_sched_frame = next_sched_frame; - - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { -@@ -741,10 +340,11 @@ int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) - did_something = 1; - } - -- /* Clear interrupt */ -- gintsts.b.sofintr = 1; -- DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -- -+ /* Clear interrupt - but do not trample on the FIQ sof */ -+ if (!fiq_fsm_enable) { -+ gintsts.b.sofintr = 1; -+ DWC_WRITE_REG32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); -+ } - return 1; - } - -@@ -1020,19 +620,21 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - int i; - int retval = 0; -- haint_data_t haint; -+ haint_data_t haint = { .d32 = 0 } ; - - /* Clear appropriate bits in HCINTn to clear the interrupt bit in - * GINTSTS */ - -- haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); -+ if (!fiq_fsm_enable) -+ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); - - // Overwrite with saved interrupts from fiq handler -- if(fiq_split_enable) -+ if(fiq_fsm_enable) - { -+ /* check the mask? */ - local_fiq_disable(); -- haint.d32 = haint_saved.d32; -- haint_saved.d32 = 0; -+ haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); -+ dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; - local_fiq_enable(); - } - -@@ -1076,9 +678,7 @@ static uint32_t get_actual_xfer_length(dwc_hc_t * hc, - *short_read = (hctsiz.b.xfersize != 0); - } - } else if (hc->qh->do_split) { -- if(fiq_split_enable) -- length = split_out_xfersize[hc->hc_num]; -- else -+ //length = split_out_xfersize[hc->hc_num]; - length = qtd->ssplit_out_xfer_count; - } else { - length = hc->xfer_len; -@@ -1325,19 +925,17 @@ static void release_channel(dwc_otg_hcd_t * hcd, - int free_qtd; - dwc_irqflags_t flags; - dwc_spinlock_t *channel_lock = hcd->channel_lock; --#ifdef FIQ_DEBUG -- int endp = qtd->urb ? qtd->urb->pipe_info.ep_num : 0; --#endif -+ - int hog_port = 0; - - DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", - __func__, hc->hc_num, halt_status, hc->xfer_len); - -- if(fiq_split_enable && hc->do_split) { -+ if(fiq_fsm_enable && hc->do_split) { - if(!hc->ep_is_in && hc->ep_type == UE_ISOCHRONOUS) { - if(hc->xact_pos == DWC_HCSPLIT_XACTPOS_MID || - hc->xact_pos == DWC_HCSPLIT_XACTPOS_BEGIN) { -- hog_port = 1; -+ hog_port = 0; - } - } - } -@@ -1394,6 +992,8 @@ static void release_channel(dwc_otg_hcd_t * hcd, - * function clears the channel interrupt enables and conditions, so - * there's no need to clear the Channel Halted interrupt separately. - */ -+ if (fiq_fsm_enable && hcd->fiq_state->channel[hc->hc_num].fsm != FIQ_PASSTHROUGH) -+ dwc_otg_cleanup_fiq_channel(hcd, hc->hc_num); - dwc_otg_hc_cleanup(hcd->core_if, hc); - DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); - -@@ -1416,27 +1016,10 @@ static void release_channel(dwc_otg_hcd_t * hcd, - - DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); - hcd->available_host_channels++; -- fiq_print(FIQDBG_PORTHUB, "AHC = %d ", hcd->available_host_channels); -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "AHC = %d ", hcd->available_host_channels); - DWC_SPINUNLOCK_IRQRESTORE(channel_lock, flags); - } - -- if(fiq_split_enable && hc->do_split) -- { -- if(!(hcd->hub_port[hc->hub_addr] & (1 << hc->port_addr))) -- { -- fiq_print(FIQDBG_ERR, "PRTNOTAL"); -- //BUG(); -- } -- if(!hog_port && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC || -- hc->ep_type == DWC_OTG_EP_TYPE_INTR)) { -- hcd->hub_port[hc->hub_addr] &= ~(1 << hc->port_addr); --#ifdef FIQ_DEBUG -- hcd->hub_port_alloc[hc->hub_addr * 16 + hc->port_addr] = -1; --#endif -- fiq_print(FIQDBG_PORTHUB, "H%dP%d:RR%d", hc->hub_addr, hc->port_addr, endp); -- } -- } -- - /* Try to queue more transfers now that there's a free channel. */ - tr_type = dwc_otg_hcd_select_transactions(hcd); - if (tr_type != DWC_OTG_TRANSACTION_NONE) { -@@ -1858,7 +1441,7 @@ static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, - switch(dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { - case UE_BULK: - case UE_CONTROL: -- if (nak_holdoff_enable) -+ if (nak_holdoff && qtd->qh->do_split) - hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); - } - -@@ -2074,7 +1657,7 @@ static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, - // With the FIQ running we only ever see the failed NYET - if (dwc_full_frame_num(frnum) != - dwc_full_frame_num(hc->qh->sched_frame) || -- fiq_split_enable) { -+ fiq_fsm_enable) { - /* - * No longer in the same full speed frame. - * Treat this as a transaction error. -@@ -2460,12 +2043,11 @@ static inline int halt_status_ok(dwc_otg_hcd_t * hcd, - static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd, -- hcint_data_t hcint, -- hcintmsk_data_t hcintmsk) -+ dwc_otg_qtd_t * qtd) - { - int out_nak_enh = 0; -- -+ hcint_data_t hcint; -+ hcintmsk_data_t hcintmsk; - /* For core with OUT NAK enhancement, the flow for high- - * speed CONTROL/BULK OUT is handled a little differently. - */ -@@ -2495,11 +2077,9 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - } - - /* Read the HCINTn register to determine the cause for the halt. */ -- if(!fiq_split_enable) -- { -- hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -- } -+ -+ hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -+ hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); - - if (hcint.b.xfercomp) { - /** @todo This is here because of a possible hardware bug. Spec -@@ -2624,15 +2204,13 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, - static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - dwc_hc_t * hc, - dwc_otg_hc_regs_t * hc_regs, -- dwc_otg_qtd_t * qtd, -- hcint_data_t hcint, -- hcintmsk_data_t hcintmsk) -+ dwc_otg_qtd_t * qtd) - { - DWC_DEBUGPL(DBG_HCDI, "--Host Channel %d Interrupt: " - "Channel Halted--\n", hc->hc_num); - - if (hcd->core_if->dma_enable) { -- handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd, hcint, hcintmsk); -+ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); - } else { - #ifdef DEBUG - if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { -@@ -2645,11 +2223,372 @@ static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, - return 1; - } - -+ -+/** -+ * dwc_otg_fiq_unmangle_isoc() - Update the iso_frame_desc structure on -+ * FIQ transfer completion -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * 1. Un-mangle the status as recorded in each iso_frame_desc status -+ * 2. Copy it from the dwc_otg_urb into the real URB -+ */ -+void dwc_otg_fiq_unmangle_isoc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) -+{ -+ struct dwc_otg_hcd_urb *dwc_urb = qtd->urb; -+ int nr_frames = dwc_urb->packet_count; -+ int i; -+ hcint_data_t frame_hcint; -+ -+ for (i = 0; i < nr_frames; i++) { -+ frame_hcint.d32 = dwc_urb->iso_descs[i].status; -+ if (frame_hcint.b.xfercomp) { -+ dwc_urb->iso_descs[i].status = 0; -+ dwc_urb->actual_length += dwc_urb->iso_descs[i].actual_length; -+ } else if (frame_hcint.b.frmovrun) { -+ if (qh->ep_is_in) -+ dwc_urb->iso_descs[i].status = -DWC_E_NO_STREAM_RES; -+ else -+ dwc_urb->iso_descs[i].status = -DWC_E_COMMUNICATION; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else if (frame_hcint.b.xacterr) { -+ dwc_urb->iso_descs[i].status = -DWC_E_PROTOCOL; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else if (frame_hcint.b.bblerr) { -+ dwc_urb->iso_descs[i].status = -DWC_E_OVERFLOW; -+ dwc_urb->error_count++; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ } else { -+ /* Something went wrong */ -+ dwc_urb->iso_descs[i].status = -1; -+ dwc_urb->iso_descs[i].actual_length = 0; -+ dwc_urb->error_count++; -+ } -+ } -+ //printk_ratelimited(KERN_INFO "%s: HS isochronous of %d/%d frames with %d errors complete\n", -+ // __FUNCTION__, i, dwc_urb->packet_count, dwc_urb->error_count); -+ hcd->fops->complete(hcd, dwc_urb->priv, dwc_urb, 0); -+ release_channel(hcd, qh->channel, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+} -+ -+/** -+ * dwc_otg_fiq_unsetup_per_dma() - Remove data from bounce buffers for split transactions -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * Copies data from the FIQ bounce buffers into the URB's transfer buffer. Does not modify URB state. -+ * Returns total length of data or -1 if the buffers were not used. -+ * -+ */ -+int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qtd_t *qtd, uint32_t num) -+{ -+ dwc_hc_t *hc = qh->channel; -+ struct fiq_dma_blob *blob = hcd->fiq_dmab; -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ uint8_t *ptr = NULL; -+ int index = 0, len = 0; -+ int i = 0; -+ if (hc->ep_is_in) { -+ /* Copy data out of the DMA bounce buffers to the URB's buffer. -+ * The align_buf is ignored as this is ignored on FSM enqueue. */ -+ ptr = qtd->urb->buf; -+ if (qh->ep_type == UE_ISOCHRONOUS) { -+ /* Isoc IN transactions - grab the offset of the iso_frame_desc into the URB transfer buffer */ -+ index = qtd->isoc_frame_index; -+ ptr += qtd->urb->iso_descs[index].offset; -+ } else { -+ /* Need to increment by actual_length for interrupt IN */ -+ ptr += qtd->urb->actual_length; -+ } -+ -+ for (i = 0; i < st->dma_info.index; i++) { -+ len += st->dma_info.slot_len[i]; -+ dwc_memcpy(ptr, &blob->channel[num].index[i].buf[0], st->dma_info.slot_len[i]); -+ ptr += st->dma_info.slot_len[i]; -+ } -+ return len; -+ } else { -+ /* OUT endpoints - nothing to do. */ -+ return -1; -+ } -+ -+} -+/** -+ * dwc_otg_hcd_handle_hc_fsm() - handle an unmasked channel interrupt -+ * from a channel handled in the FIQ -+ * @hcd: Pointer to dwc_otg_hcd struct -+ * @num: Host channel number -+ * -+ * If a host channel interrupt was received by the IRQ and this was a channel -+ * used by the FIQ, the execution flow for transfer completion is substantially -+ * different from the normal (messy) path. This function and its friends handles -+ * channel cleanup and transaction completion from a FIQ transaction. -+ */ -+int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) -+{ -+ struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; -+ dwc_hc_t *hc = hcd->hc_ptr_array[num]; -+ dwc_otg_qtd_t *qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); -+ dwc_otg_qh_t *qh = hc->qh; -+ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; -+ hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; -+ int hostchannels = 0; -+ int ret = 0; -+ fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); -+ -+ hostchannels = hcd->available_host_channels; -+ switch (st->fsm) { -+ case FIQ_TEST: -+ break; -+ -+ case FIQ_DEQUEUE_ISSUED: -+ /* hc_halt was called. QTD no longer exists. */ -+ /* TODO: for a nonperiodic split transaction, need to issue a -+ * CLEAR_TT_BUFFER hub command if we were in the start-split phase. -+ */ -+ release_channel(hcd, hc, NULL, hc->halt_status); -+ ret = 1; -+ break; -+ -+ case FIQ_NP_SPLIT_DONE: -+ /* Nonperiodic transaction complete. */ -+ if (!hc->ep_is_in) { -+ qtd->ssplit_out_xfer_count = hc->xfer_len; -+ } -+ if (hcint.b.xfercomp) { -+ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.nak) { -+ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); -+ } -+ ret = 1; -+ break; -+ -+ case FIQ_NP_SPLIT_HS_ABORTED: -+ /* A HS abort is a 3-strikes on the HS bus at any point in the transaction. -+ * Normally a CLEAR_TT_BUFFER hub command would be required: we can't do that -+ * because there's no guarantee which order a non-periodic split happened in. -+ * We could end up clearing a perfectly good transaction out of the buffer. -+ */ -+ if (hcint.b.xacterr) { -+ qtd->error_count += st->nr_errors; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.ahberr) { -+ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ local_fiq_disable(); -+ BUG(); -+ } -+ break; -+ -+ case FIQ_NP_SPLIT_LS_ABORTED: -+ /* A few cases can cause this - either an unknown state on a SSPLIT or -+ * STALL/data toggle error response on a CSPLIT */ -+ if (hcint.b.stall) { -+ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.datatglerr) { -+ handle_hc_datatglerr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.bblerr) { -+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.ahberr) { -+ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ local_fiq_disable(); -+ BUG(); -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_DONE: -+ /* Isoc IN or Interrupt IN/OUT */ -+ -+ /* Flow control here is different from the normal execution by the driver. -+ * We need to completely ignore most of the driver's method of handling -+ * split transactions and do it ourselves. -+ */ -+ if (hc->ep_type == UE_INTERRUPT) { -+ if (hcint.b.nak) { -+ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); -+ } else if (hc->ep_is_in) { -+ int len; -+ len = dwc_otg_fiq_unsetup_per_dma(hcd, hc->qh, qtd, num); -+ //printk(KERN_NOTICE "FIQ Transaction: hc=%d len=%d urb_len = %d\n", num, len, qtd->urb->length); -+ qtd->urb->actual_length += len; -+ if (qtd->urb->actual_length >= qtd->urb->length) { -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ /* Interrupt transfer not complete yet - is it a short read? */ -+ if (len < hc->max_packet) { -+ /* Interrupt transaction complete */ -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ /* Further transactions required */ -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ } else { -+ /* Interrupt OUT complete. */ -+ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); -+ qtd->urb->actual_length += hc->xfer_len; -+ if (qtd->urb->actual_length >= qtd->urb->length) { -+ qtd->urb->status = 0; -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, qtd->urb->status); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ } else { -+ /* ISOC IN complete. */ -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ int len = 0; -+ /* Record errors, update qtd. */ -+ if (st->nr_errors) { -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ } else { -+ frame_desc->status = 0; -+ /* Unswizzle dma */ -+ len = dwc_otg_fiq_unsetup_per_dma(hcd, qh, qtd, num); -+ frame_desc->actual_length = len; -+ } -+ qtd->isoc_frame_index++; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ break; -+ -+ case FIQ_PER_ISO_OUT_DONE: { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ if (st->nr_errors) { -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ } else { -+ frame_desc->status = 0; -+ frame_desc->actual_length = frame_desc->length; -+ } -+ qtd->isoc_frame_index++; -+ qtd->isoc_split_offset = 0; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_NYET_ABORTED: -+ /* Doh. lost the data. */ -+ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " -+ "- FIQ reported NYET. Data may have been lost.\n", -+ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); -+ if (hc->ep_type == UE_ISOCHRONOUS) { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ frame_desc->actual_length = 0; -+ frame_desc->status = -DWC_E_PROTOCOL; -+ qtd->isoc_frame_index++; -+ qtd->isoc_split_offset = 0; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ case FIQ_HS_ISOC_DONE: -+ /* The FIQ has performed a whole pile of isochronous transactions. -+ * The status is recorded as the interrupt state should the transaction -+ * fail. -+ */ -+ dwc_otg_fiq_unmangle_isoc(hcd, qh, qtd, num); -+ break; -+ -+ case FIQ_PER_SPLIT_LS_ABORTED: -+ if (hcint.b.xacterr) { -+ /* Hub has responded with an ERR packet. Device -+ * has been unplugged or the port has been disabled. -+ * TODO: need to issue a reset to the hub port. */ -+ qtd->error_count += 3; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.stall) { -+ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); -+ } else if (hcint.b.bblerr) { -+ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); -+ } else { -+ printk_ratelimited(KERN_INFO "Transfer to device %d endpoint 0x%x failed " -+ "- FIQ reported FSM=%d. Data may have been lost.\n", -+ st->fsm, hc->dev_addr, hc->ep_num); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ case FIQ_PER_SPLIT_HS_ABORTED: -+ /* Either the SSPLIT phase suffered transaction errors or something -+ * unexpected happened. -+ */ -+ qtd->error_count += 3; -+ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ break; -+ -+ case FIQ_PER_SPLIT_TIMEOUT: -+ /* Couldn't complete in the nominated frame */ -+ printk(KERN_INFO "Transfer to device %d endpoint 0x%x frame %d failed " -+ "- FIQ timed out. Data may have been lost.\n", -+ hc->dev_addr, hc->ep_num, dwc_otg_hcd_get_frame_number(hcd) >> 3); -+ if (hc->ep_type == UE_ISOCHRONOUS) { -+ struct dwc_otg_hcd_iso_packet_desc *frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; -+ /* Record errors, update qtd. */ -+ frame_desc->actual_length = 0; -+ if (hc->ep_is_in) { -+ frame_desc->status = -DWC_E_NO_STREAM_RES; -+ } else { -+ frame_desc->status = -DWC_E_COMMUNICATION; -+ } -+ qtd->isoc_frame_index++; -+ if (qtd->isoc_frame_index == qtd->urb->packet_count) { -+ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_COMPLETE); -+ } -+ } else { -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ break; -+ -+ default: -+ local_fiq_disable(); -+ DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm); -+ BUG(); -+ } -+ //if (hostchannels != hcd->available_host_channels) { -+ /* should have incremented by now! */ -+ // BUG(); -+// } -+ return ret; -+} -+ - /** Handles interrupt for a specific Host Channel */ - int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - { - int retval = 0; -- hcint_data_t hcint, hcint_orig; -+ hcint_data_t hcint; - hcintmsk_data_t hcintmsk; - dwc_hc_t *hc; - dwc_otg_hc_regs_t *hc_regs; -@@ -2668,24 +2607,32 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - } - qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); - -+ /* -+ * FSM mode: Check to see if this is a HC interrupt from a channel handled by the FIQ. -+ * Execution path is fundamentally different for the channels after a FIQ has completed -+ * a split transaction. -+ */ -+ if (fiq_fsm_enable) { -+ switch (dwc_otg_hcd->fiq_state->channel[num].fsm) { -+ case FIQ_PASSTHROUGH: -+ break; -+ case FIQ_PASSTHROUGH_ERRORSTATE: -+ /* Hook into the error count */ -+ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); -+ if (dwc_otg_hcd->fiq_state->channel[num].nr_errors) { -+ qtd->error_count = 0; -+ fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); -+ } -+ break; -+ default: -+ dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd, num); -+ return 1; -+ } -+ } -+ - hcint.d32 = DWC_READ_REG32(&hc_regs->hcint); -- hcint_orig = hcint; - hcintmsk.d32 = DWC_READ_REG32(&hc_regs->hcintmsk); -- DWC_DEBUGPL(DBG_HCDV, -- " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", -- hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); - hcint.d32 = hcint.d32 & hcintmsk.d32; -- -- if(fiq_split_enable) -- { -- // replace with the saved interrupts from the fiq handler -- local_fiq_disable(); -- hcint_orig.d32 = hcint_saved[num].d32; -- hcint.d32 = hcint_orig.d32 & hcintmsk_saved[num].d32; -- hcint_saved[num].d32 = 0; -- local_fiq_enable(); -- } -- - if (!dwc_otg_hcd->core_if->dma_enable) { - if (hcint.b.chhltd && hcint.d32 != 0x2) { - hcint.b.chhltd = 0; -@@ -2703,7 +2650,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - hcint.b.nyet = 0; - } - if (hcint.b.chhltd) { -- retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd, hcint_orig, hcintmsk_saved[num]); -+ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); - } - if (hcint.b.ahberr) { - retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index ee8eec9..98e1dc5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -58,6 +58,7 @@ - #else - #include - #endif -+#include - - #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) - #define USB_URB_EP_LINKING 1 -@@ -69,7 +70,8 @@ - #include "dwc_otg_dbg.h" - #include "dwc_otg_driver.h" - #include "dwc_otg_hcd.h" --#include "dwc_otg_mphi_fix.h" -+ -+extern unsigned char _dwc_otg_fiq_stub, _dwc_otg_fiq_stub_end; - - /** - * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is -@@ -80,7 +82,7 @@ - - static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; - --extern bool fiq_fix_enable; -+extern bool fiq_enable; - - /** @name Linux HC Driver API Functions */ - /** @{ */ -@@ -351,7 +353,6 @@ static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, - urb); - } - } -- - DWC_FREE(dwc_otg_urb); - if (!new_entry) { - DWC_ERROR("dwc_otg_hcd: complete: cannot allocate URB TQ entry\n"); -@@ -395,13 +396,9 @@ static struct dwc_otg_hcd_function_ops hcd_fops = { - static struct fiq_handler fh = { - .name = "usb_fiq", - }; --struct fiq_stack_s { -- int magic1; -- uint8_t stack[2048]; -- int magic2; --} fiq_stack; - --extern mphi_regs_t c_mphi_regs; -+ -+ - /** - * Initializes the HCD. This function allocates memory for and initializes the - * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the -@@ -433,20 +430,6 @@ int hcd_init(dwc_bus_dev_t *_dev) - pci_set_consistent_dma_mask(_dev, dmamask); - #endif - -- if (fiq_fix_enable) -- { -- // Set up fiq -- claim_fiq(&fh); -- set_fiq_handler(__FIQ_Branch, 4); -- memset(®s,0,sizeof(regs)); -- regs.ARM_r8 = (long)dwc_otg_hcd_handle_fiq; -- regs.ARM_r9 = (long)0; -- regs.ARM_sp = (long)fiq_stack.stack + sizeof(fiq_stack.stack) - 4; -- set_fiq_regs(®s); -- fiq_stack.magic1 = 0xdeadbeef; -- fiq_stack.magic2 = 0xaa995566; -- } -- - /* - * Allocate memory for the base HCD plus the DWC OTG HCD. - * Initialize the base HCD. -@@ -466,30 +449,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - - hcd->regs = otg_dev->os_dep.base; - -- if (fiq_fix_enable) -- { -- volatile extern void *dwc_regs_base; -- -- //Set the mphi periph to the required registers -- c_mphi_regs.base = otg_dev->os_dep.mphi_base; -- c_mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -- c_mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -- c_mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -- c_mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -- -- dwc_regs_base = otg_dev->os_dep.base; - -- //Enable mphi peripheral -- writel((1<<31),c_mphi_regs.ctrl); --#ifdef DEBUG -- if (readl(c_mphi_regs.ctrl) & 0x80000000) -- DWC_DEBUGPL(DBG_USER, "MPHI periph has been enabled\n"); -- else -- DWC_DEBUGPL(DBG_USER, "MPHI periph has NOT been enabled\n"); --#endif -- // Enable FIQ interrupt from USB peripheral -- enable_fiq(INTERRUPT_VC_USB); -- } - /* Initialize the DWC OTG HCD. */ - dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); - if (!dwc_otg_hcd) { -@@ -503,6 +463,55 @@ int hcd_init(dwc_bus_dev_t *_dev) - goto error2; - } - -+ if (fiq_enable) -+ { -+ if (claim_fiq(&fh)) { -+ DWC_ERROR("Can't claim FIQ"); -+ goto error2; -+ } -+ -+ DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -+ -+ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -+ memset(®s,0,sizeof(regs)); -+ -+ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -+ if (fiq_fsm_enable) { -+ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -+ //regs.ARM_r10 = dwc_otg_hcd->dma; -+ regs.ARM_fp = (long) dwc_otg_fiq_fsm; -+ } else { -+ regs.ARM_fp = (long) dwc_otg_fiq_nop; -+ } -+ -+ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -+ -+// __show_regs(®s); -+ set_fiq_regs(®s); -+ -+ //Set the mphi periph to the required registers -+ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -+ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -+ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -+ //Enable mphi peripheral -+ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -+ DWC_WARN("MPHI periph has been enabled"); -+ else -+ DWC_WARN("MPHI periph has NOT been enabled"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ local_fiq_enable(); -+ } -+ -+ - otg_dev->hcd->otg_dev = otg_dev; - hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) //don't support for LM(with 2.6.20.1 kernel) -@@ -518,9 +527,9 @@ int hcd_init(dwc_bus_dev_t *_dev) - * IRQ line, and calls hcd_start method. - */ - #ifdef PLATFORM_INTERFACE -- retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED | IRQF_DISABLED); -+ retval = usb_add_hcd(hcd, platform_get_irq(_dev, fiq_enable ? 0 : 1), IRQF_SHARED | IRQF_DISABLED); - #else -- retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); -+ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED | IRQF_DISABLED); - #endif - if (retval < 0) { - goto error2; -@@ -617,9 +626,13 @@ void hcd_stop(struct usb_hcd *hcd) - /** Returns the current frame number. */ - static int get_frame_number(struct usb_hcd *hcd) - { -+ hprt0_data_t hprt0; - dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); -- -- return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); -+ hprt0.d32 = DWC_READ_REG32(dwc_otg_hcd->core_if->host_if->hprt0); -+ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd) >> 3; -+ else -+ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); - } - - #ifdef DEBUG -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 5c22b6c..17d3030 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -41,7 +41,6 @@ - - #include "dwc_otg_hcd.h" - #include "dwc_otg_regs.h" --#include "dwc_otg_mphi_fix.h" - - extern bool microframe_schedule; - -@@ -577,7 +576,6 @@ static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - } - - --extern int g_next_sched_frame, g_np_count, g_np_sent; - - /** - * Schedules an interrupt or isochronous transfer in the periodic schedule. -@@ -637,9 +635,9 @@ static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); - } - else { -- if(DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, g_next_sched_frame)) -+ if(fiq_enable && (DWC_LIST_EMPTY(&hcd->periodic_sched_inactive) || dwc_frame_num_le(qh->sched_frame, hcd->fiq_state->next_sched_frame))) - { -- g_next_sched_frame = qh->sched_frame; -+ hcd->fiq_state->next_sched_frame = qh->sched_frame; - - } - /* Always start in the inactive schedule. */ -@@ -680,7 +678,7 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - /* Always start in the inactive schedule. */ - DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, - &qh->qh_list_entry); -- g_np_count++; -+ //hcd->fiq_state->kick_np_queues = 1; - } else { - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { -@@ -740,13 +738,12 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->non_periodic_qh_ptr->next; - } - DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); -- -- // If we've removed the last non-periodic entry then there are none left! -- g_np_count = g_np_sent; -+ //if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_inactive)) -+ // hcd->fiq_state->kick_np_queues = 1; - } else { - deschedule_periodic(hcd, qh); - hcd->periodic_qh_count--; -- if( !hcd->periodic_qh_count ) { -+ if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { - intr_mask.b.sofintr = 1; - DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, - intr_mask.d32, 0); -@@ -771,28 +768,11 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - int sched_next_periodic_split) - { - if (dwc_qh_is_non_per(qh)) { -- -- dwc_otg_qh_t *qh_tmp; -- dwc_list_link_t *qh_list; -- DWC_LIST_FOREACH(qh_list, &hcd->non_periodic_sched_inactive) -- { -- qh_tmp = DWC_LIST_ENTRY(qh_list, struct dwc_otg_qh, qh_list_entry); -- if(qh_tmp == qh) -- { -- /* -- * FIQ is being disabled because this one nevers gets a np_count increment -- * This is still not absolutely correct, but it should fix itself with -- * just an unnecessary extra interrupt -- */ -- g_np_sent = g_np_count; -- } -- } -- -- - dwc_otg_hcd_qh_remove(hcd, qh); - if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { - /* Add back to inactive non-periodic schedule. */ - dwc_otg_hcd_qh_add(hcd, qh); -+ //hcd->fiq_state->kick_np_queues = 1; - } - } else { - uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); -@@ -851,9 +831,9 @@ void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, - DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, - &qh->qh_list_entry); - } else { -- if(!dwc_frame_num_le(g_next_sched_frame, qh->sched_frame)) -+ if(fiq_enable && !dwc_frame_num_le(hcd->fiq_state->next_sched_frame, qh->sched_frame)) - { -- g_next_sched_frame = qh->sched_frame; -+ hcd->fiq_state->next_sched_frame = qh->sched_frame; - } - - DWC_LIST_MOVE_HEAD -@@ -944,6 +924,9 @@ int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, - if (*qh == NULL) { - retval = -DWC_E_NO_MEMORY; - goto done; -+ } else { -+ if (fiq_enable) -+ hcd->fiq_state->kick_np_queues = 1; - } - } - retval = dwc_otg_hcd_qh_add(hcd, *qh); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -deleted file mode 100755 -index 50b94a8..0000000 ---- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.c -+++ /dev/null -@@ -1,113 +0,0 @@ --#include "dwc_otg_regs.h" --#include "dwc_otg_dbg.h" -- --void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "*** Debugging from within the %s function: ***\n" -- "curmode: %1i Modemismatch: %1i otgintr: %1i sofintr: %1i\n" -- "rxstsqlvl: %1i nptxfempty : %1i ginnakeff: %1i goutnakeff: %1i\n" -- "ulpickint: %1i i2cintr: %1i erlysuspend:%1i usbsuspend: %1i\n" -- "usbreset: %1i enumdone: %1i isooutdrop: %1i eopframe: %1i\n" -- "restoredone: %1i epmismatch: %1i inepint: %1i outepintr: %1i\n" -- "incomplisoin:%1i incomplisoout:%1i fetsusp: %1i resetdet: %1i\n" -- "portintr: %1i hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i\n" -- "conidstschng:%1i disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -- function_name, -- gintsts.b.curmode, -- gintsts.b.modemismatch, -- gintsts.b.otgintr, -- gintsts.b.sofintr, -- gintsts.b.rxstsqlvl, -- gintsts.b.nptxfempty, -- gintsts.b.ginnakeff, -- gintsts.b.goutnakeff, -- gintsts.b.ulpickint, -- gintsts.b.i2cintr, -- gintsts.b.erlysuspend, -- gintsts.b.usbsuspend, -- gintsts.b.usbreset, -- gintsts.b.enumdone, -- gintsts.b.isooutdrop, -- gintsts.b.eopframe, -- gintsts.b.restoredone, -- gintsts.b.epmismatch, -- gintsts.b.inepint, -- gintsts.b.outepintr, -- gintsts.b.incomplisoin, -- gintsts.b.incomplisoout, -- gintsts.b.fetsusp, -- gintsts.b.resetdet, -- gintsts.b.portintr, -- gintsts.b.hcintr, -- gintsts.b.ptxfempty, -- gintsts.b.lpmtranrcvd, -- gintsts.b.conidstschng, -- gintsts.b.disconnect, -- gintsts.b.sessreqintr, -- gintsts.b.wkupintr); -- return; --} -- --void dwc_debug_core_int_mask(gintmsk_data_t gintmsk, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "Interrupt Mask status (called from %s) :\n" -- "modemismatch: %1i otgintr: %1i sofintr: %1i rxstsqlvl: %1i\n" -- "nptxfempty: %1i ginnakeff: %1i goutnakeff: %1i ulpickint: %1i\n" -- "i2cintr: %1i erlysuspend:%1i usbsuspend: %1i usbreset: %1i\n" -- "enumdone: %1i isooutdrop: %1i eopframe: %1i restoredone: %1i\n" -- "epmismatch: %1i inepintr: %1i outepintr: %1i incomplisoin:%1i\n" -- "incomplisoout:%1i fetsusp: %1i resetdet: %1i portintr: %1i\n" -- "hcintr: %1i ptxfempty: %1i lpmtranrcvd:%1i conidstschng:%1i\n" -- "disconnect: %1i sessreqintr:%1i wkupintr: %1i\n", -- function_name, -- gintmsk.b.modemismatch, -- gintmsk.b.otgintr, -- gintmsk.b.sofintr, -- gintmsk.b.rxstsqlvl, -- gintmsk.b.nptxfempty, -- gintmsk.b.ginnakeff, -- gintmsk.b.goutnakeff, -- gintmsk.b.ulpickint, -- gintmsk.b.i2cintr, -- gintmsk.b.erlysuspend, -- gintmsk.b.usbsuspend, -- gintmsk.b.usbreset, -- gintmsk.b.enumdone, -- gintmsk.b.isooutdrop, -- gintmsk.b.eopframe, -- gintmsk.b.restoredone, -- gintmsk.b.epmismatch, -- gintmsk.b.inepintr, -- gintmsk.b.outepintr, -- gintmsk.b.incomplisoin, -- gintmsk.b.incomplisoout, -- gintmsk.b.fetsusp, -- gintmsk.b.resetdet, -- gintmsk.b.portintr, -- gintmsk.b.hcintr, -- gintmsk.b.ptxfempty, -- gintmsk.b.lpmtranrcvd, -- gintmsk.b.conidstschng, -- gintmsk.b.disconnect, -- gintmsk.b.sessreqintr, -- gintmsk.b.wkupintr); -- return; --} -- --void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name) --{ -- DWC_DEBUGPL(DBG_USER, "otg int register (from %s function):\n" -- "sesenddet:%1i sesreqsucstschung:%2i hstnegsucstschng:%1i\n" -- "hstnegdet:%1i adevtoutchng: %2i debdone: %1i\n" -- "mvic: %1i\n", -- function_name, -- gotgint.b.sesenddet, -- gotgint.b.sesreqsucstschng, -- gotgint.b.hstnegsucstschng, -- gotgint.b.hstnegdet, -- gotgint.b.adevtoutchng, -- gotgint.b.debdone, -- gotgint.b.mvic); -- -- return; --} -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h b/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -deleted file mode 100755 -index ca17379..0000000 ---- a/drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h -+++ /dev/null -@@ -1,48 +0,0 @@ --#ifndef __DWC_OTG_MPHI_FIX_H__ --#define __DWC_OTG_MPHI_FIX_H__ --#define FIQ_WRITE(_addr_,_data_) (*(volatile uint32_t *) (_addr_) = (_data_)) --#define FIQ_READ(_addr_) (*(volatile uint32_t *) (_addr_)) -- --typedef struct { -- volatile void* base; -- volatile void* ctrl; -- volatile void* outdda; -- volatile void* outddb; -- volatile void* intstat; --} mphi_regs_t; -- --void dwc_debug_print_core_int_reg(gintsts_data_t gintsts, const char* function_name); --void dwc_debug_core_int_mask(gintsts_data_t gintmsk, const char* function_name); --void dwc_debug_otg_int(gotgint_data_t gotgint, const char* function_name); -- --extern gintsts_data_t gintsts_saved; -- --#ifdef DEBUG --#define DWC_DBG_PRINT_CORE_INT(_arg_) dwc_debug_print_core_int_reg(_arg_,__func__) --#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) dwc_debug_core_int_mask(_arg_,__func__) --#define DWC_DBG_PRINT_OTG_INT(_arg_) dwc_debug_otg_int(_arg_,__func__) -- --#else --#define DWC_DBG_PRINT_CORE_INT(_arg_) --#define DWC_DBG_PRINT_CORE_INT_MASK(_arg_) --#define DWC_DBG_PRINT_OTG_INT(_arg_) -- --#endif -- --typedef enum { -- FIQDBG_SCHED = (1 << 0), -- FIQDBG_INT = (1 << 1), -- FIQDBG_ERR = (1 << 2), -- FIQDBG_PORTHUB = (1 << 3), --} FIQDBG_T; -- --void _fiq_print(FIQDBG_T dbg_lvl, char *fmt, ...); --#ifdef FIQ_DEBUG --#define fiq_print _fiq_print --#else --#define fiq_print(x, y, ...) --#endif -- --extern bool fiq_fix_enable, nak_holdoff_enable, fiq_split_enable; -- --#endif -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -index 5d310df..4b32941 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c -@@ -59,6 +59,8 @@ - #include "dwc_otg_driver.h" - #include "dwc_otg_dbg.h" - -+extern bool fiq_enable; -+ - static struct gadget_wrapper { - dwc_otg_pcd_t *pcd; - -@@ -1222,13 +1224,13 @@ int pcd_init(dwc_bus_dev_t *_dev) - */ - #ifdef PLATFORM_INTERFACE - DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", -- platform_get_irq(_dev, 0)); -- retval = request_irq(platform_get_irq(_dev, 0), dwc_otg_pcd_irq, -+ platform_get_irq(_dev, fiq_enable ? 0 : 1)); -+ retval = request_irq(platform_get_irq(_dev, fiq_enable ? 0 : 1), dwc_otg_pcd_irq, - IRQF_SHARED, gadget_wrapper->gadget.name, - otg_dev->pcd); - if (retval != 0) { - DWC_ERROR("request of irq%d failed\n", -- platform_get_irq(_dev, 0)); -+ platform_get_irq(_dev, fiq_enable ? 0 : 1)); - free_wrapper(gadget_wrapper); - return -EBUSY; - } - -From 264fb63a197f8df7a31f27109409699c43a5c82e Mon Sep 17 00:00:00 2001 -From: Vincent Sanders -Date: Wed, 30 Jan 2013 12:45:18 +0000 -Subject: [PATCH 028/172] bcm2835: add v4l2 camera device - -- Supports raw YUV capture, preview, JPEG and H264. -- Uses videobuf2 for data transfer, using dma_buf. -- Uses 3.6.10 timestamping -- Camera power based on use -- Uses immutable input mode on video encoder - -Signed-off-by: Daniel Stone -Signed-off-by: Luke Diamand ---- - Documentation/video4linux/bcm2835-v4l2.txt | 60 + - drivers/media/platform/Kconfig | 1 + - drivers/media/platform/Makefile | 2 + - drivers/media/platform/bcm2835/Kconfig | 25 + - drivers/media/platform/bcm2835/Makefile | 5 + - drivers/media/platform/bcm2835/bcm2835-camera.c | 1476 +++++++++++++++++ - drivers/media/platform/bcm2835/bcm2835-camera.h | 113 ++ - drivers/media/platform/bcm2835/controls.c | 725 ++++++++ - drivers/media/platform/bcm2835/mmal-common.h | 51 + - drivers/media/platform/bcm2835/mmal-encodings.h | 93 ++ - drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + - drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + - drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ - drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ - drivers/media/platform/bcm2835/mmal-parameters.h | 539 ++++++ - drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ - drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ - 17 files changed, 5826 insertions(+) - create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt - create mode 100644 drivers/media/platform/bcm2835/Kconfig - create mode 100644 drivers/media/platform/bcm2835/Makefile - create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c - create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h - create mode 100644 drivers/media/platform/bcm2835/controls.c - create mode 100644 drivers/media/platform/bcm2835/mmal-common.h - create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h - create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h - create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h - create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c - create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h - -diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt -new file mode 100644 -index 0000000..c585a8f ---- /dev/null -+++ b/Documentation/video4linux/bcm2835-v4l2.txt -@@ -0,0 +1,60 @@ -+ -+BCM2835 (aka Raspberry Pi) V4L2 driver -+====================================== -+ -+1. Copyright -+============ -+ -+Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ -+2. License -+========== -+ -+This program is free software; you can redistribute it and/or modify -+it under the terms of the GNU General Public License as published by -+the Free Software Foundation; either version 2 of the License, or -+(at your option) any later version. -+ -+This program is distributed in the hope that it will be useful, -+but WITHOUT 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. -+ -+3. Quick Start -+============== -+ -+You need a version 1.0 or later of v4l2-ctl, available from: -+ git://git.linuxtv.org/v4l-utils.git -+ -+$ sudo modprobe bcm2835-v4l2 -+ -+Turn on the overlay: -+ -+$ v4l2-ctl --overlay=1 -+ -+Turn off the overlay: -+ -+$ v4l2-ctl --overlay=0 -+ -+Set the capture format for video: -+ -+$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 -+ -+(Note: 1088 not 1080). -+ -+Capture: -+ -+$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 -+ -+Stills capture: -+ -+$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 -+$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg -+ -+List of available formats: -+ -+$ v4l2-ctl --list-formats -diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig -index 6a1334b..8fdd7a0 100644 ---- a/drivers/media/platform/Kconfig -+++ b/drivers/media/platform/Kconfig -@@ -116,6 +116,7 @@ config VIDEO_S3C_CAMIF - source "drivers/media/platform/soc_camera/Kconfig" - source "drivers/media/platform/exynos4-is/Kconfig" - source "drivers/media/platform/s5p-tv/Kconfig" -+source "drivers/media/platform/bcm2835/Kconfig" - - endif # V4L_PLATFORM_DRIVERS - -diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile -index a49936b..414b28c 100644 ---- a/drivers/media/platform/Makefile -+++ b/drivers/media/platform/Makefile -@@ -46,4 +46,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ - - obj-y += omap/ - -+obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ -+ - ccflags-y += -I$(srctree)/drivers/media/i2c -diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig -new file mode 100644 -index 0000000..a8fd172 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/Kconfig -@@ -0,0 +1,25 @@ -+# Broadcom VideoCore IV v4l2 camera support -+ -+config VIDEO_BCM2835 -+ bool "Broadcom BCM2835 camera interface driver" -+ depends on VIDEO_V4L2 && ARCH_BCM2708 -+ ---help--- -+ Say Y here to enable camera host interface devices for -+ Broadcom BCM2835 SoC. This operates over the VCHIQ interface -+ to a service running on VideoCore. -+ -+ -+if VIDEO_BCM2835 -+ -+config VIDEO_BCM2835_MMAL -+ tristate "Broadcom BM2835 MMAL camera interface driver" -+ depends on BCM2708_VCHIQ -+ select VIDEOBUF2_VMALLOC -+ ---help--- -+ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface -+ -+ To compile this driver as a module, choose M here: the -+ module will be called bcm2835-v4l2.o -+ -+ -+endif # VIDEO_BM2835 -diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile -new file mode 100644 -index 0000000..f17c79c ---- /dev/null -+++ b/drivers/media/platform/bcm2835/Makefile -@@ -0,0 +1,5 @@ -+bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o -+ -+obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o -+ -+ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -new file mode 100644 -index 0000000..1c9d9d5 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -0,0 +1,1476 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "mmal-common.h" -+#include "mmal-encodings.h" -+#include "mmal-vchiq.h" -+#include "mmal-msg.h" -+#include "mmal-parameters.h" -+#include "bcm2835-camera.h" -+ -+#define BM2835_MMAL_VERSION "0.0.2" -+#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" -+ -+#define MAX_WIDTH 2592 -+#define MAX_HEIGHT 1944 -+#define MIN_BUFFER_SIZE (80*1024) -+ -+#define MAX_VIDEO_MODE_WIDTH 1280 -+#define MAX_VIDEO_MODE_HEIGHT 720 -+ -+MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); -+MODULE_AUTHOR("Vincent Sanders"); ++module_init(vchiq_init); ++module_exit(vchiq_exit); +MODULE_LICENSE("GPL"); -+MODULE_VERSION(BM2835_MMAL_VERSION); -+ -+int bcm2835_v4l2_debug; -+module_param_named(debug, bcm2835_v4l2_debug, int, 0644); -+MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); -+ -+static struct bm2835_mmal_dev *gdev; /* global device data */ -+ -+/* video formats */ -+static struct mmal_fmt formats[] = { -+ { -+ .name = "4:2:0, packed YUV", -+ .fourcc = V4L2_PIX_FMT_YUV420, -+ .mmal = MMAL_ENCODING_I420, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, YUYV", -+ .fourcc = V4L2_PIX_FMT_YUYV, -+ .mmal = MMAL_ENCODING_YUYV, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB24 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .mmal = MMAL_ENCODING_BGR24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "JPEG", -+ .fourcc = V4L2_PIX_FMT_JPEG, -+ .mmal = MMAL_ENCODING_JPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, -+ }, -+ { -+ .name = "H264", -+ .fourcc = V4L2_PIX_FMT_H264, -+ .mmal = MMAL_ENCODING_H264, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ } -+}; -+ -+static struct mmal_fmt *get_format(struct v4l2_format *f) -+{ -+ struct mmal_fmt *fmt; -+ unsigned int k; -+ -+ for (k = 0; k < ARRAY_SIZE(formats); k++) { -+ fmt = &formats[k]; -+ if (fmt->fourcc == f->fmt.pix.pixelformat) -+ break; -+ } -+ -+ if (k == ARRAY_SIZE(formats)) -+ return NULL; -+ -+ return &formats[k]; -+} -+ -+/* ------------------------------------------------------------------ -+ Videobuf queue operations -+ ------------------------------------------------------------------*/ -+ -+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, -+ unsigned int *nbuffers, unsigned int *nplanes, -+ unsigned int sizes[], void *alloc_ctxs[]) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ unsigned long size; -+ -+ /* refuse queue setup if port is not configured */ -+ if (dev->capture.port == NULL) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: capture port not configured\n", __func__); -+ return -EINVAL; -+ } -+ -+ size = dev->capture.port->current_buffer.size; -+ if (size == 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: capture port buffer size is zero\n", __func__); -+ return -EINVAL; -+ } -+ -+ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) -+ *nbuffers = (dev->capture.port->current_buffer.num + 2); -+ -+ *nplanes = 1; -+ -+ sizes[0] = size; -+ -+ /* -+ * videobuf2-vmalloc allocator is context-less so no need to set -+ * alloc_ctxs array. -+ */ -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ return 0; -+} -+ -+static int buffer_prepare(struct vb2_buffer *vb) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); -+ unsigned long size; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ BUG_ON(dev->capture.port == NULL); -+ BUG_ON(dev->capture.fmt == NULL); -+ -+ size = dev->capture.stride * dev->capture.height; -+ if (vb2_plane_size(vb, 0) < size) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s data will not fit into plane (%lu < %lu)\n", -+ __func__, vb2_plane_size(vb, 0), size); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static inline bool is_capturing(struct bm2835_mmal_dev *dev) -+{ -+ return dev->capture.camera_port == -+ &dev-> -+ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; -+} -+ -+static void buffer_cb(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ int status, -+ struct mmal_buffer *buf, -+ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) -+{ -+ struct bm2835_mmal_dev *dev = port->cb_ctx; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", -+ __func__, status, buf, length, mmal_flags, pts); -+ -+ if (status != 0) { -+ /* error in transfer */ -+ if (buf != NULL) { -+ /* there was a buffer with the error so return it */ -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); -+ } -+ return; -+ } else if (length == 0) { -+ /* stream ended */ -+ if (buf != NULL) { -+ /* this should only ever happen if the port is -+ * disabled and there are buffers still queued -+ */ -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); -+ pr_debug("Empty buffer"); -+ } else if (dev->capture.frame_count) { -+ /* grab another frame */ -+ if (is_capturing(dev)) { -+ pr_debug("Grab another frame"); -+ vchiq_mmal_port_parameter_set( -+ instance, -+ dev->capture. -+ camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture. -+ frame_count, -+ sizeof(dev->capture.frame_count)); -+ } -+ } else { -+ /* signal frame completion */ -+ complete(&dev->capture.frame_cmplt); -+ } -+ } else { -+ if (dev->capture.frame_count) { -+ if (dev->capture.vc_start_timestamp != -1) { -+ s64 runtime_us = pts - -+ dev->capture.vc_start_timestamp; -+ u32 div = 0; -+ u32 rem = 0; -+ -+ div = -+ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); -+ buf->vb.v4l2_buf.timestamp.tv_sec = -+ dev->capture.kernel_start_ts.tv_sec - 1 + -+ div; -+ buf->vb.v4l2_buf.timestamp.tv_usec = -+ dev->capture.kernel_start_ts.tv_usec + rem; -+ -+ if (buf->vb.v4l2_buf.timestamp.tv_usec >= -+ USEC_PER_SEC) { -+ buf->vb.v4l2_buf.timestamp.tv_sec++; -+ buf->vb.v4l2_buf.timestamp.tv_usec -= -+ USEC_PER_SEC; -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Convert start time %d.%06d and %llu" -+ "with offset %llu to %d.%06d\n", -+ (int)dev->capture.kernel_start_ts. -+ tv_sec, -+ (int)dev->capture.kernel_start_ts. -+ tv_usec, -+ dev->capture.vc_start_timestamp, pts, -+ (int)buf->vb.v4l2_buf.timestamp.tv_sec, -+ (int)buf->vb.v4l2_buf.timestamp. -+ tv_usec); -+ } else { -+ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); -+ } -+ -+ vb2_set_plane_payload(&buf->vb, 0, length); -+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); -+ -+ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && -+ is_capturing(dev)) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Grab another frame as buffer has EOS"); -+ vchiq_mmal_port_parameter_set( -+ instance, -+ dev->capture. -+ camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture. -+ frame_count, -+ sizeof(dev->capture.frame_count)); -+ } -+ } else { -+ /* signal frame completion */ -+ complete(&dev->capture.frame_cmplt); -+ } -+ } -+} -+ -+static int enable_camera(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ if (!dev->camera_use_count) { -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed enabling camera, ret %d\n", ret); -+ return -EINVAL; -+ } -+ } -+ dev->camera_use_count++; -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, "enabled camera (refcount %d)\n", -+ dev->camera_use_count); -+ return 0; -+} -+ -+static int disable_camera(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ if (!dev->camera_use_count) { -+ v4l2_err(&dev->v4l2_dev, -+ "Disabled the camera when already disabled\n"); -+ return -EINVAL; -+ } -+ dev->camera_use_count--; -+ if (!dev->camera_use_count) { -+ unsigned int i = 0xFFFFFFFF; -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Disabling camera\n"); -+ ret = -+ vchiq_mmal_component_disable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed disabling camera, ret %d\n", ret); -+ return -EINVAL; -+ } -+ vchiq_mmal_port_parameter_set( -+ dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]->control, -+ MMAL_PARAMETER_CAMERA_NUM, &i, -+ sizeof(i)); -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Camera refcount now %d\n", dev->camera_use_count); -+ return 0; -+} -+ -+static void buffer_queue(struct vb2_buffer *vb) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); -+ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); -+ int ret; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: dev:%p buf:%p\n", __func__, dev, buf); -+ -+ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); -+ buf->buffer_size = vb2_plane_size(&buf->vb, 0); -+ -+ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); -+ if (ret < 0) -+ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", -+ __func__); -+} -+ -+static int start_streaming(struct vb2_queue *vq, unsigned int count) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ int ret; -+ int parameter_size; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ /* ensure a format has actually been set */ -+ if (dev->capture.port == NULL) -+ return -EINVAL; -+ -+ if (enable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); -+ return -EINVAL; -+ } -+ -+ /*init_completion(&dev->capture.frame_cmplt); */ -+ -+ /* enable frame capture */ -+ dev->capture.frame_count = 1; -+ -+ /* if the preview is not already running, wait for a few frames for AGC -+ * to settle down. -+ */ -+ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) -+ msleep(300); -+ -+ /* enable the connection from camera to encoder (if applicable) */ -+ if (dev->capture.camera_port != dev->capture.port -+ && dev->capture.camera_port) { -+ ret = vchiq_mmal_port_enable(dev->instance, -+ dev->capture.camera_port, NULL); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to enable encode tunnel - error %d\n", -+ ret); -+ return -1; -+ } -+ } -+ -+ /* Get VC timestamp at this point in time */ -+ parameter_size = sizeof(dev->capture.vc_start_timestamp); -+ if (vchiq_mmal_port_parameter_get(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_SYSTEM_TIME, -+ &dev->capture.vc_start_timestamp, -+ ¶meter_size)) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to get VC start time - update your VC f/w\n"); -+ -+ /* Flag to indicate just to rely on kernel timestamps */ -+ dev->capture.vc_start_timestamp = -1; -+ } else -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Start time %lld size %d\n", -+ dev->capture.vc_start_timestamp, parameter_size); -+ -+ v4l2_get_timestamp(&dev->capture.kernel_start_ts); -+ -+ /* enable the camera port */ -+ dev->capture.port->cb_ctx = dev; -+ ret = -+ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to enable capture port - error %d\n", ret); -+ return -1; -+ } -+ -+ /* capture the first frame */ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture.frame_count, -+ sizeof(dev->capture.frame_count)); -+ return 0; -+} -+ -+/* abort streaming and wait for last buffer */ -+static int stop_streaming(struct vb2_queue *vq) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", -+ __func__, dev); -+ -+ init_completion(&dev->capture.frame_cmplt); -+ dev->capture.frame_count = 0; -+ -+ /* ensure a format has actually been set */ -+ if (dev->capture.port == NULL) -+ return -EINVAL; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); -+ -+ /* stop capturing frames */ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ dev->capture.camera_port, -+ MMAL_PARAMETER_CAPTURE, -+ &dev->capture.frame_count, -+ sizeof(dev->capture.frame_count)); -+ -+ /* wait for last frame to complete */ -+ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); -+ if (ret <= 0) -+ v4l2_err(&dev->v4l2_dev, -+ "error %d waiting for frame completion\n", ret); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "disabling connection\n"); -+ -+ /* disable the connection from camera to encoder */ -+ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); -+ if (!ret && dev->capture.camera_port != dev->capture.port) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "disabling port\n"); -+ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); -+ } else if (dev->capture.camera_port != dev->capture.port) { -+ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", -+ ret); -+ } -+ -+ if (disable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); -+ return -EINVAL; -+ } -+ -+ return ret; -+} -+ -+static void bm2835_mmal_lock(struct vb2_queue *vq) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ mutex_lock(&dev->mutex); -+} -+ -+static void bm2835_mmal_unlock(struct vb2_queue *vq) -+{ -+ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -+ mutex_unlock(&dev->mutex); -+} -+ -+static struct vb2_ops bm2835_mmal_video_qops = { -+ .queue_setup = queue_setup, -+ .buf_prepare = buffer_prepare, -+ .buf_queue = buffer_queue, -+ .start_streaming = start_streaming, -+ .stop_streaming = stop_streaming, -+ .wait_prepare = bm2835_mmal_unlock, -+ .wait_finish = bm2835_mmal_lock, -+}; -+ -+/* ------------------------------------------------------------------ -+ IOCTL operations -+ ------------------------------------------------------------------*/ -+ -+/* overlay ioctl */ -+static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_fmtdesc *f) -+{ -+ struct mmal_fmt *fmt; -+ -+ if (f->index >= ARRAY_SIZE(formats)) -+ return -EINVAL; -+ -+ fmt = &formats[f->index]; -+ -+ strlcpy(f->description, fmt->name, sizeof(f->description)); -+ f->pixelformat = fmt->fourcc; -+ -+ return 0; -+} -+ -+static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ f->fmt.win = dev->overlay; -+ -+ return 0; -+} -+ -+static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ /* Only support one format so get the current one. */ -+ vidioc_g_fmt_vid_overlay(file, priv, f); -+ -+ /* todo: allow the size and/or offset to be changed. */ -+ return 0; -+} -+ -+static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ vidioc_try_fmt_vid_overlay(file, priv, f); -+ -+ dev->overlay = f->fmt.win; -+ -+ /* todo: program the preview port parameters */ -+ return 0; -+} -+ -+static int vidioc_overlay(struct file *file, void *f, unsigned int on) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct vchiq_mmal_port *src; -+ struct vchiq_mmal_port *dst; -+ struct mmal_parameter_displayregion prev_config = { -+ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | -+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, -+ .layer = PREVIEW_LAYER, -+ .alpha = 255, -+ .fullscreen = 0, -+ .dest_rect = { -+ .x = dev->overlay.w.left, -+ .y = dev->overlay.w.top, -+ .width = dev->overlay.w.width, -+ .height = dev->overlay.w.height, -+ }, -+ }; -+ -+ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || -+ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) -+ return 0; /* already in requested state */ -+ -+ src = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; -+ -+ if (!on) { -+ /* disconnect preview ports and disable component */ -+ ret = vchiq_mmal_port_disable(dev->instance, src); -+ if (!ret) -+ ret = -+ vchiq_mmal_port_connect_tunnel(dev->instance, src, -+ NULL); -+ if (ret >= 0) -+ ret = vchiq_mmal_component_disable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ -+ disable_camera(dev); -+ return ret; -+ } -+ -+ /* set preview port format and connect it to output */ -+ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; -+ -+ ret = vchiq_mmal_port_set_format(dev->instance, src); -+ if (ret < 0) -+ goto error; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, -+ MMAL_PARAMETER_DISPLAYREGION, -+ &prev_config, sizeof(prev_config)); -+ if (ret < 0) -+ goto error; -+ -+ if (enable_camera(dev) < 0) -+ goto error; -+ -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ if (ret < 0) -+ goto error; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", -+ src, dst); -+ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); -+ if (!ret) -+ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); -+error: -+ return ret; -+} -+ -+static int vidioc_g_fbuf(struct file *file, void *fh, -+ struct v4l2_framebuffer *a) -+{ -+ /* The video overlay must stay within the framebuffer and can't be -+ positioned independently. */ -+ a->flags = V4L2_FBUF_FLAG_OVERLAY; -+ -+ /* todo: v4l2_framebuffer still needs more info filling in -+ * in order to pass the v4l2-compliance test. */ -+ -+ return 0; -+} -+ -+/* input ioctls */ -+static int vidioc_enum_input(struct file *file, void *priv, -+ struct v4l2_input *inp) -+{ -+ /* only a single camera input */ -+ if (inp->index != 0) -+ return -EINVAL; -+ -+ inp->type = V4L2_INPUT_TYPE_CAMERA; -+ sprintf(inp->name, "Camera %u", inp->index); -+ return 0; -+} -+ -+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -+{ -+ *i = 0; -+ return 0; -+} -+ -+static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -+{ -+ if (i != 0) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+/* capture ioctls */ -+static int vidioc_querycap(struct file *file, void *priv, -+ struct v4l2_capability *cap) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ u32 major; -+ u32 minor; -+ -+ vchiq_mmal_version(dev->instance, &major, &minor); -+ -+ strcpy(cap->driver, "bm2835 mmal"); -+ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", -+ major, minor); -+ -+ snprintf(cap->bus_info, sizeof(cap->bus_info), -+ "platform:%s", dev->v4l2_dev.name); -+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | -+ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; -+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -+ -+ return 0; -+} -+ -+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_fmtdesc *f) -+{ -+ struct mmal_fmt *fmt; -+ -+ if (f->index >= ARRAY_SIZE(formats)) -+ return -EINVAL; -+ -+ fmt = &formats[f->index]; -+ -+ strlcpy(f->description, fmt->name, sizeof(f->description)); -+ f->pixelformat = fmt->fourcc; -+ return 0; -+} -+ -+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ -+ f->fmt.pix.width = dev->capture.width; -+ f->fmt.pix.height = dev->capture.height; -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; -+ f->fmt.pix.bytesperline = -+ (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; -+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG -+ && f->fmt.pix.sizeimage < (100 << 10)) { -+ /* Need a minimum size for JPEG to account for EXIF. */ -+ f->fmt.pix.sizeimage = (100 << 10); -+ } -+ -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || -+ dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ f->fmt.pix.priv = 0; -+ -+ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -+ __func__); -+ return 0; -+} -+ -+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct mmal_fmt *mfmt; -+ -+ mfmt = get_format(f); -+ if (!mfmt) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Fourcc format (0x%08x) unknown.\n", -+ f->fmt.pix.pixelformat); -+ f->fmt.pix.pixelformat = formats[0].fourcc; -+ mfmt = get_format(f); -+ } -+ -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ /* image must be a multiple of 32 pixels wide and 16 lines high */ -+ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, -+ &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); -+ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; -+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -+ if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) -+ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; -+ -+ if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || -+ mfmt->fourcc == V4L2_PIX_FMT_UYVY) -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ f->fmt.pix.priv = 0; -+ -+ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -+ __func__); -+ return 0; -+} -+ -+static int mmal_setup_components(struct bm2835_mmal_dev *dev, -+ struct v4l2_format *f) -+{ -+ int ret; -+ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; -+ struct vchiq_mmal_component *encode_component = NULL; -+ struct mmal_fmt *mfmt = get_format(f); -+ -+ BUG_ON(!mfmt); -+ -+ if (dev->capture.encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "vid_cap - disconnect previous tunnel\n"); -+ -+ /* Disconnect any previous connection */ -+ vchiq_mmal_port_connect_tunnel(dev->instance, -+ dev->capture.camera_port, NULL); -+ dev->capture.camera_port = NULL; -+ ret = vchiq_mmal_component_disable(dev->instance, -+ dev->capture. -+ encode_component); -+ if (ret) -+ v4l2_err(&dev->v4l2_dev, -+ "Failed to disable encode component %d\n", -+ ret); -+ -+ dev->capture.encode_component = NULL; -+ } -+ /* format dependant port setup */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_CAMERA: -+ /* Make a further decision on port based on resolution */ -+ if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH -+ && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) -+ camera_port = port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]; -+ else -+ camera_port = port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE]; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; -+ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; -+ camera_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE]; -+ break; -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; -+ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ camera_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]; -+ break; -+ default: -+ break; -+ } -+ -+ if (!port) -+ return -EINVAL; -+ -+ if (encode_component) -+ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; -+ else -+ camera_port->format.encoding = mfmt->mmal; -+ -+ camera_port->format.encoding_variant = 0; -+ camera_port->es.video.width = f->fmt.pix.width; -+ camera_port->es.video.height = f->fmt.pix.height; -+ camera_port->es.video.crop.x = 0; -+ camera_port->es.video.crop.y = 0; -+ camera_port->es.video.crop.width = f->fmt.pix.width; -+ camera_port->es.video.crop.height = f->fmt.pix.height; -+ camera_port->es.video.frame_rate.num = 30; -+ camera_port->es.video.frame_rate.den = 1; -+ -+ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); -+ -+ if (!ret -+ && camera_port == -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO]) { -+ bool overlay_enabled = -+ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; -+ struct vchiq_mmal_port *preview_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; -+ /* Preview and encode ports need to match on resolution */ -+ if (overlay_enabled) { -+ /* Need to disable the overlay before we can update -+ * the resolution -+ */ -+ ret = -+ vchiq_mmal_port_disable(dev->instance, -+ preview_port); -+ if (!ret) -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ preview_port, -+ NULL); -+ } -+ preview_port->es.video.width = f->fmt.pix.width; -+ preview_port->es.video.height = f->fmt.pix.height; -+ preview_port->es.video.crop.x = 0; -+ preview_port->es.video.crop.y = 0; -+ preview_port->es.video.crop.width = f->fmt.pix.width; -+ preview_port->es.video.crop.height = f->fmt.pix.height; -+ preview_port->es.video.frame_rate.num = 30; -+ preview_port->es.video.frame_rate.den = 1; -+ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); -+ if (overlay_enabled) { -+ ret = vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ preview_port, -+ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); -+ if (!ret) -+ ret = vchiq_mmal_port_enable(dev->instance, -+ preview_port, -+ NULL); -+ } -+ } -+ -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s failed to set format\n", __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; -+ } else { -+ if (encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "vid_cap - set up encode comp\n"); -+ -+ /* configure buffering */ -+ camera_port->current_buffer.size = -+ camera_port->recommended_buffer.size; -+ camera_port->current_buffer.num = -+ camera_port->recommended_buffer.num; -+ -+ port->format.encoding = mfmt->mmal; -+ port->format.encoding_variant = 0; -+ /* Set any encoding specific parameters */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ port->format.bitrate = -+ dev->capture.encode_bitrate; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ /* Could set EXIF parameters here */ -+ break; -+ default: -+ break; -+ } -+ ret = vchiq_mmal_port_set_format(dev->instance, port); -+ -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s failed to set format\n", __func__); -+ } else { -+ ret = vchiq_mmal_component_enable( -+ dev->instance, -+ encode_component); -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s Failed to enable encode components\n", -+ __func__); -+ } else { -+ /* configure buffering */ -+ port->current_buffer.num = 1; -+ port->current_buffer.size = -+ f->fmt.pix.sizeimage; -+ if (port->format.encoding == -+ MMAL_ENCODING_JPEG) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "JPEG - fiddle buffer size\n"); -+ port->current_buffer.size = -+ (f->fmt.pix.sizeimage < -+ (100 << 10)) -+ ? (100 << 10) : f->fmt.pix. -+ sizeimage; -+ } -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "vid_cap - current_buffer.size being set to %d\n", -+ f->fmt.pix.sizeimage); -+ port->current_buffer.alignment = 0; -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ camera_port, -+ &encode_component->input[0]); -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to create connection\n", -+ __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; -+ } -+ } -+ } -+ } else { -+ /* configure buffering */ -+ camera_port->current_buffer.num = 1; -+ camera_port->current_buffer.size = f->fmt.pix.sizeimage; -+ camera_port->current_buffer.alignment = 0; -+ } -+ -+ if (!ret) { -+ dev->capture.fmt = mfmt; -+ dev->capture.stride = f->fmt.pix.bytesperline; -+ dev->capture.width = port->es.video.crop.width; -+ dev->capture.height = port->es.video.crop.height; -+ -+ /* select port for capture */ -+ dev->capture.port = port; -+ dev->capture.camera_port = camera_port; -+ dev->capture.encode_component = encode_component; -+ } -+ } -+ -+ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ -+ return ret; -+} -+ -+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, -+ struct v4l2_format *f) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct mmal_fmt *mfmt; -+ -+ /* try the format to set valid parameters */ -+ ret = vidioc_try_fmt_vid_cap(file, priv, f); -+ if (ret) { -+ v4l2_err(&dev->v4l2_dev, -+ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); -+ return ret; -+ } -+ -+ /* if a capture is running refuse to set format */ -+ if (vb2_is_busy(&dev->capture.vb_vidq)) { -+ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); -+ return -EBUSY; -+ } -+ -+ /* If the format is unsupported v4l2 says we should switch to -+ * a supported one and not return an error. */ -+ mfmt = get_format(f); -+ if (!mfmt) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Fourcc format (0x%08x) unknown.\n", -+ f->fmt.pix.pixelformat); -+ f->fmt.pix.pixelformat = formats[0].fourcc; -+ mfmt = get_format(f); -+ } -+ -+ ret = mmal_setup_components(dev, f); -+ if (ret != 0) -+ v4l2_err(&dev->v4l2_dev, -+ "%s: failed to setup mmal components: %d\n", -+ __func__, ret); -+ -+ return ret; -+} -+ -+static const struct v4l2_ioctl_ops camera0_ioctl_ops = { -+ /* overlay */ -+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, -+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, -+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, -+ .vidioc_overlay = vidioc_overlay, -+ .vidioc_g_fbuf = vidioc_g_fbuf, -+ -+ /* inputs */ -+ .vidioc_enum_input = vidioc_enum_input, -+ .vidioc_g_input = vidioc_g_input, -+ .vidioc_s_input = vidioc_s_input, -+ -+ /* capture */ -+ .vidioc_querycap = vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, -+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -+ -+ /* buffer management */ -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ -+ .vidioc_log_status = v4l2_ctrl_log_status, -+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+/* ------------------------------------------------------------------ -+ Driver init/finalise -+ ------------------------------------------------------------------*/ -+ -+static const struct v4l2_file_operations camera0_fops = { -+ .owner = THIS_MODULE, -+ .open = v4l2_fh_open, -+ .release = vb2_fop_release, -+ .read = vb2_fop_read, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ -+ .mmap = vb2_fop_mmap, -+}; -+ -+static struct video_device vdev_template = { -+ .name = "camera0", -+ .fops = &camera0_fops, -+ .ioctl_ops = &camera0_ioctl_ops, -+ .release = video_device_release_empty, -+}; -+ -+static int set_camera_parameters(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *camera) -+{ -+ int ret; -+ struct mmal_parameter_camera_config cam_config = { -+ .max_stills_w = MAX_WIDTH, -+ .max_stills_h = MAX_HEIGHT, -+ .stills_yuv422 = 1, -+ .one_shot_stills = 1, -+ .max_preview_video_w = 1920, -+ .max_preview_video_h = 1088, -+ .num_preview_video_frames = 3, -+ .stills_capture_circular_buffer_height = 0, -+ .fast_preview_resume = 0, -+ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC -+ }; -+ -+ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, -+ MMAL_PARAMETER_CAMERA_CONFIG, -+ &cam_config, sizeof(cam_config)); -+ return ret; -+} -+ -+/* MMAL instance and component init */ -+static int __init mmal_init(struct bm2835_mmal_dev *dev) -+{ -+ int ret; -+ struct mmal_es_format *format; -+ -+ ret = vchiq_mmal_init(&dev->instance); -+ if (ret < 0) -+ return ret; -+ -+ /* get the camera component ready */ -+ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", -+ &dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) -+ goto unreg_mmal; -+ -+ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < -+ MMAL_CAMERA_PORT_COUNT) { -+ ret = -EINVAL; -+ goto unreg_camera; -+ } -+ -+ ret = set_camera_parameters(dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ if (ret < 0) -+ goto unreg_camera; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ format->encoding_variant = MMAL_ENCODING_I420; -+ -+ format->es->video.width = 1024; -+ format->es->video.height = 768; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 1024; -+ format->es->video.crop.height = 768; -+ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -+ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ format->encoding_variant = MMAL_ENCODING_I420; -+ -+ format->es->video.width = 1024; -+ format->es->video.height = 768; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 1024; -+ format->es->video.crop.height = 768; -+ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -+ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ -+ format = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE].format; -+ -+ format->encoding = MMAL_ENCODING_OPAQUE; -+ -+ format->es->video.width = 2592; -+ format->es->video.height = 1944; -+ format->es->video.crop.x = 0; -+ format->es->video.crop.y = 0; -+ format->es->video.crop.width = 2592; -+ format->es->video.crop.height = 1944; -+ format->es->video.frame_rate.num = 30; -+ format->es->video.frame_rate.den = 1; -+ -+ dev->capture.width = format->es->video.width; -+ dev->capture.height = format->es->video.height; -+ dev->capture.fmt = &formats[0]; -+ dev->capture.encode_component = NULL; -+ -+ /* get the preview component ready */ -+ ret = vchiq_mmal_component_init( -+ dev->instance, "ril.video_render", -+ &dev->component[MMAL_COMPONENT_PREVIEW]); -+ if (ret < 0) -+ goto unreg_camera; -+ -+ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { -+ ret = -EINVAL; -+ pr_debug("too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); -+ goto unreg_preview; -+ } -+ -+ /* get the image encoder component ready */ -+ ret = vchiq_mmal_component_init( -+ dev->instance, "ril.image_encode", -+ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ if (ret < 0) -+ goto unreg_preview; -+ -+ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { -+ ret = -EINVAL; -+ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, -+ 1); -+ goto unreg_image_encoder; -+ } -+ -+ /* get the video encoder component ready */ -+ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", -+ &dev-> -+ component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ if (ret < 0) -+ goto unreg_image_encoder; -+ -+ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { -+ ret = -EINVAL; -+ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", -+ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, -+ 1); -+ goto unreg_vid_encoder; -+ } -+ -+ { -+ unsigned int enable = 1; -+ vchiq_mmal_port_parameter_set( -+ dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, -+ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, -+ &enable, sizeof(enable)); -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, -+ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, -+ &enable, -+ sizeof(enable)); -+ } -+ ret = bm2835_mmal_set_all_camera_controls(dev); -+ if (ret < 0) -+ goto unreg_vid_encoder; -+ -+ return 0; -+ -+unreg_vid_encoder: -+ pr_err("Cleanup: Destroy video encoder\n"); -+ vchiq_mmal_component_finalise( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ -+unreg_image_encoder: -+ pr_err("Cleanup: Destroy image encoder\n"); -+ vchiq_mmal_component_finalise( -+ dev->instance, -+ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ -+unreg_preview: -+ pr_err("Cleanup: Destroy video render\n"); -+ vchiq_mmal_component_finalise(dev->instance, -+ dev->component[MMAL_COMPONENT_PREVIEW]); -+ -+unreg_camera: -+ pr_err("Cleanup: Destroy camera\n"); -+ vchiq_mmal_component_finalise(dev->instance, -+ dev->component[MMAL_COMPONENT_CAMERA]); -+ -+unreg_mmal: -+ vchiq_mmal_finalise(dev->instance); -+ return ret; -+} -+ -+static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, -+ struct video_device *vfd) -+{ -+ int ret; -+ -+ *vfd = vdev_template; -+ -+ vfd->v4l2_dev = &dev->v4l2_dev; -+ -+ vfd->lock = &dev->mutex; -+ -+ vfd->queue = &dev->capture.vb_vidq; -+ -+ /* video device needs to be able to access instance data */ -+ video_set_drvdata(vfd, dev); -+ -+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); -+ if (ret < 0) -+ return ret; -+ -+ v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", -+ video_device_node_name(vfd)); -+ -+ return 0; -+} -+ -+static struct v4l2_format default_v4l2_format = { -+ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, -+ .fmt.pix.width = 1024, -+ .fmt.pix.bytesperline = 1024 * 3 / 2, -+ .fmt.pix.height = 768, -+ .fmt.pix.sizeimage = 1<<18, -+}; -+ -+static int __init bm2835_mmal_init(void) -+{ -+ int ret; -+ struct bm2835_mmal_dev *dev; -+ struct vb2_queue *q; -+ -+ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); -+ if (!dev) -+ return -ENOMEM; -+ -+ /* setup device defaults */ -+ dev->overlay.w.left = 150; -+ dev->overlay.w.top = 50; -+ dev->overlay.w.width = 1024; -+ dev->overlay.w.height = 768; -+ dev->overlay.clipcount = 0; -+ dev->overlay.field = V4L2_FIELD_NONE; -+ -+ dev->capture.fmt = &formats[3]; /* JPEG */ -+ -+ /* v4l device registration */ -+ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), -+ "%s", BM2835_MMAL_MODULE_NAME); -+ ret = v4l2_device_register(NULL, &dev->v4l2_dev); -+ if (ret) -+ goto free_dev; -+ -+ /* setup v4l controls */ -+ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); -+ if (ret < 0) -+ goto unreg_dev; -+ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; -+ -+ /* mmal init */ -+ ret = mmal_init(dev); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ /* initialize queue */ -+ q = &dev->capture.vb_vidq; -+ memset(q, 0, sizeof(*q)); -+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; -+ q->drv_priv = dev; -+ q->buf_struct_size = sizeof(struct mmal_buffer); -+ q->ops = &bm2835_mmal_video_qops; -+ q->mem_ops = &vb2_vmalloc_memops; -+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ ret = vb2_queue_init(q); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ -+ mutex_init(&dev->mutex); -+ -+ /* initialise video devices */ -+ ret = bm2835_mmal_init_device(dev, &dev->vdev); -+ if (ret < 0) -+ goto unreg_dev; -+ -+ ret = mmal_setup_components(dev, &default_v4l2_format); -+ if (ret < 0) { -+ v4l2_err(&dev->v4l2_dev, -+ "%s: could not setup components\n", __func__); -+ goto unreg_dev; -+ } -+ -+ v4l2_info(&dev->v4l2_dev, -+ "Broadcom 2835 MMAL video capture ver %s loaded.\n", -+ BM2835_MMAL_VERSION); -+ -+ gdev = dev; -+ return 0; -+ -+unreg_dev: -+ v4l2_ctrl_handler_free(&dev->ctrl_handler); -+ v4l2_device_unregister(&dev->v4l2_dev); -+ -+free_dev: -+ kfree(dev); -+ -+ v4l2_err(&dev->v4l2_dev, -+ "%s: error %d while loading driver\n", -+ BM2835_MMAL_MODULE_NAME, ret); -+ -+ return ret; -+} -+ -+static void __exit bm2835_mmal_exit(void) -+{ -+ if (!gdev) -+ return; -+ -+ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", -+ video_device_node_name(&gdev->vdev)); -+ -+ video_unregister_device(&gdev->vdev); -+ -+ if (gdev->capture.encode_component) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, -+ "mmal_exit - disconnect tunnel\n"); -+ vchiq_mmal_port_connect_tunnel(gdev->instance, -+ gdev->capture.camera_port, NULL); -+ vchiq_mmal_component_disable(gdev->instance, -+ gdev->capture.encode_component); -+ } -+ vchiq_mmal_component_disable(gdev->instance, -+ gdev->component[MMAL_COMPONENT_CAMERA]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev-> -+ component[MMAL_COMPONENT_VIDEO_ENCODE]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev-> -+ component[MMAL_COMPONENT_IMAGE_ENCODE]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev->component[MMAL_COMPONENT_PREVIEW]); -+ -+ vchiq_mmal_component_finalise(gdev->instance, -+ gdev->component[MMAL_COMPONENT_CAMERA]); -+ -+ vchiq_mmal_finalise(gdev->instance); -+ -+ v4l2_ctrl_handler_free(&gdev->ctrl_handler); -+ -+ v4l2_device_unregister(&gdev->v4l2_dev); -+ -+ kfree(gdev); -+} -+ -+module_init(bm2835_mmal_init); -+module_exit(bm2835_mmal_exit); -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h ++MODULE_AUTHOR("Broadcom Corporation"); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h new file mode 100644 -index 0000000..883eab7 +index 0000000..d1e2741 --- /dev/null -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.h -@@ -0,0 +1,113 @@ -+/* -+ * Broadcom BM2835 V4L2 driver ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h +@@ -0,0 +1,223 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. + * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. + * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * core driver device ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + -+#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ ++#ifndef VCHIQ_ARM_H ++#define VCHIQ_ARM_H + -+enum { -+ MMAL_COMPONENT_CAMERA = 0, -+ MMAL_COMPONENT_PREVIEW, -+ MMAL_COMPONENT_IMAGE_ENCODE, -+ MMAL_COMPONENT_VIDEO_ENCODE, -+ MMAL_COMPONENT_COUNT -+}; -+ -+enum { -+ MMAL_CAMERA_PORT_PREVIEW = 0, -+ MMAL_CAMERA_PORT_VIDEO, -+ MMAL_CAMERA_PORT_CAPTURE, -+ MMAL_CAMERA_PORT_COUNT -+}; -+ -+#define PREVIEW_FRAME_RATE_NUM 30 -+#define PREVIEW_FRAME_RATE_DEN 1 -+ -+#define PREVIEW_LAYER 2 -+ -+extern int bcm2835_v4l2_debug; -+ -+struct bm2835_mmal_dev { -+ /* v4l2 devices */ -+ struct v4l2_device v4l2_dev; -+ struct video_device vdev; -+ struct mutex mutex; -+ -+ /* controls */ -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; -+ struct mmal_colourfx colourfx; -+ int hflip; -+ int vflip; -+ -+ /* allocated mmal instance and components */ -+ struct vchiq_mmal_instance *instance; -+ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; -+ int camera_use_count; -+ -+ struct v4l2_window overlay; -+ -+ struct { -+ unsigned int width; /* width */ -+ unsigned int height; /* height */ -+ unsigned int stride; /* stride */ -+ struct mmal_fmt *fmt; -+ -+ /* H264 encode bitrate */ -+ int encode_bitrate; -+ /* H264 bitrate mode. CBR/VBR */ -+ int encode_bitrate_mode; -+ /* JPEG Q-factor */ -+ int q_factor; -+ -+ struct vb2_queue vb_vidq; -+ -+ /* VC start timestamp for streaming */ -+ s64 vc_start_timestamp; -+ /* Kernel start timestamp for streaming */ -+ struct timeval kernel_start_ts; -+ -+ struct vchiq_mmal_port *port; /* port being used for capture */ -+ /* camera port being used for capture */ -+ struct vchiq_mmal_port *camera_port; -+ /* component being used for encode */ -+ struct vchiq_mmal_component *encode_component; -+ /* number of frames remaining which driver should capture */ -+ unsigned int frame_count; -+ /* last frame completion */ -+ struct completion frame_cmplt; -+ -+ } capture; -+ -+}; -+ -+int bm2835_mmal_init_controls( -+ struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl_handler *hdl); -+ -+int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); -+ -+ -+/* Debug helpers */ -+ -+#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ -+{ \ -+ v4l2_dbg(level, debug, dev, \ -+"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ -+ desc == NULL ? "" : desc, \ -+ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ -+ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ -+ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ -+} -diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c -new file mode 100644 -index 0000000..d1408e5 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/controls.c -@@ -0,0 +1,725 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "mmal-common.h" -+#include "mmal-vchiq.h" -+#include "mmal-parameters.h" -+#include "bcm2835-camera.h" -+ -+/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. -+ * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. -+ */ -+static const s64 ev_bias_qmenu[] = { -+ -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 -+}; -+ -+/* Supported ISO values -+ * ISOO = auto ISO -+ */ -+static const s64 iso_qmenu[] = { -+ 0, 100, 200, 400, 800, -+}; -+ -+/* Supported video encode modes */ -+static const s64 bitrate_mode_qmenu[] = { -+ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, -+ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, -+}; -+ -+ -+enum bm2835_mmal_ctrl_type { -+ MMAL_CONTROL_TYPE_STD, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ MMAL_CONTROL_TYPE_INT_MENU, -+ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ -+}; -+ -+struct bm2835_mmal_v4l2_ctrl; -+ -+typedef int(bm2835_mmal_v4l2_ctrl_cb)( -+ struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); -+ -+struct bm2835_mmal_v4l2_ctrl { -+ u32 id; /* v4l2 control identifier */ -+ enum bm2835_mmal_ctrl_type type; -+ /* control minimum value or -+ * mask for MMAL_CONTROL_TYPE_STD_MENU */ -+ s32 min; -+ s32 max; /* maximum value of control */ -+ s32 def; /* default value of control */ -+ s32 step; /* step size of the control */ -+ const s64 *imenu; /* integer menu array */ -+ u32 mmal_id; /* mmal parameter id */ -+ bm2835_mmal_v4l2_ctrl_cb *setter; -+}; -+ -+struct v4l2_to_mmal_effects_setting { -+ u32 v4l2_effect; -+ u32 mmal_effect; -+ s32 col_fx_enable; -+ s32 col_fx_fixed_cbcr; -+ u32 u; -+ u32 v; -+ u32 num_effect_params; -+ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; -+}; -+ -+static const struct v4l2_to_mmal_effects_setting -+ v4l2_to_mmal_effects_values[] = { -+ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, -+ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, -+ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, -+ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, -+ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, -+ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, -+ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, -+ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } -+}; -+ -+ -+/* control handlers*/ -+ -+static int ctrl_set_rational(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ -+ } rational_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ rational_value.num = ctrl->val; -+ rational_value.den = 100; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &rational_value, -+ sizeof(rational_value)); -+} -+ -+static int ctrl_set_value(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ u32 u32_value; -+ struct vchiq_mmal_component *camera; -+ -+ camera = dev->component[MMAL_COMPONENT_CAMERA]; -+ -+ u32_value = ((ctrl->val % 360) / 90) * 90; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ -+ return ret; -+} -+ -+static int ctrl_set_flip(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ u32 u32_value; -+ struct vchiq_mmal_component *camera; -+ -+ if (ctrl->id == V4L2_CID_HFLIP) -+ dev->hflip = ctrl->val; -+ else -+ dev->vflip = ctrl->val; -+ -+ camera = dev->component[MMAL_COMPONENT_CAMERA]; -+ -+ if (dev->hflip && dev->vflip) -+ u32_value = MMAL_PARAM_MIRROR_BOTH; -+ else if (dev->hflip) -+ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; -+ else if (dev->vflip) -+ u32_value = MMAL_PARAM_MIRROR_VERTICAL; -+ else -+ u32_value = MMAL_PARAM_MIRROR_NONE; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ if (ret < 0) -+ return ret; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+ -+ return ret; -+ -+} -+ -+static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_AUTO: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ break; -+ -+ case V4L2_EXPOSURE_MANUAL: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; -+ break; -+ -+ case V4L2_EXPOSURE_SHUTTER_PRIORITY: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; -+ break; -+ -+ case V4L2_EXPOSURE_APERTURE_PRIORITY: -+ u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; -+ break; -+ -+ } -+ -+ /* todo: what about the other ten modes there are MMAL parameters for */ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_METERING_AVERAGE: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; -+ break; -+ -+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; -+ break; -+ -+ case V4L2_EXPOSURE_METERING_SPOT: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; -+ break; -+ -+ /* todo matrix weighting not added to Linux API till 3.9 -+ case V4L2_EXPOSURE_METERING_MATRIX: -+ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; -+ break; -+ */ -+ -+ } -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ switch (ctrl->val) { -+ case V4L2_WHITE_BALANCE_MANUAL: -+ u32_value = MMAL_PARAM_AWBMODE_OFF; -+ break; -+ -+ case V4L2_WHITE_BALANCE_AUTO: -+ u32_value = MMAL_PARAM_AWBMODE_AUTO; -+ break; -+ -+ case V4L2_WHITE_BALANCE_INCANDESCENT: -+ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLUORESCENT: -+ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLUORESCENT_H: -+ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; -+ break; -+ -+ case V4L2_WHITE_BALANCE_HORIZON: -+ u32_value = MMAL_PARAM_AWBMODE_HORIZON; -+ break; -+ -+ case V4L2_WHITE_BALANCE_DAYLIGHT: -+ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; -+ break; -+ -+ case V4L2_WHITE_BALANCE_FLASH: -+ u32_value = MMAL_PARAM_AWBMODE_FLASH; -+ break; -+ -+ case V4L2_WHITE_BALANCE_CLOUDY: -+ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; -+ break; -+ -+ case V4L2_WHITE_BALANCE_SHADE: -+ u32_value = MMAL_PARAM_AWBMODE_SHADE; -+ break; -+ -+ } -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = -EINVAL; -+ int i, j; -+ struct vchiq_mmal_port *control; -+ struct mmal_parameter_imagefx_parameters imagefx; -+ -+ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { -+ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { -+ -+ imagefx.effect = -+ v4l2_to_mmal_effects_values[i].mmal_effect; -+ imagefx.num_effect_params = -+ v4l2_to_mmal_effects_values[i].num_effect_params; -+ -+ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) -+ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; -+ -+ for (j = 0; j < imagefx.num_effect_params; j++) -+ imagefx.effect_parameter[j] = -+ v4l2_to_mmal_effects_values[i].effect_params[j]; -+ -+ dev->colourfx.enable = -+ v4l2_to_mmal_effects_values[i].col_fx_enable; -+ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { -+ dev->colourfx.u = -+ v4l2_to_mmal_effects_values[i].u; -+ dev->colourfx.v = -+ v4l2_to_mmal_effects_values[i].v; -+ } -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ ret = vchiq_mmal_port_parameter_set( -+ dev->instance, control, -+ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, -+ &imagefx, sizeof(imagefx)); -+ if (ret) -+ goto exit; -+ -+ ret = vchiq_mmal_port_parameter_set( -+ dev->instance, control, -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &dev->colourfx, sizeof(dev->colourfx)); -+ } -+ } -+ -+exit: -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", -+ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, -+ dev->colourfx.enable ? "true" : "false", -+ dev->colourfx.u, dev->colourfx.v, -+ ret, (ret == 0 ? 0 : -EINVAL)); -+ return (ret == 0 ? 0 : EINVAL); -+} -+ -+static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = -EINVAL; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; -+ dev->colourfx.enable = ctrl->val & 0xff; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &dev->colourfx, sizeof(dev->colourfx)); -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -+ mmal_ctrl, ctrl->id, ctrl->val, ret, -+ (ret == 0 ? 0 : -EINVAL)); -+ return (ret == 0 ? 0 : EINVAL); -+} -+ -+static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret; -+ struct vchiq_mmal_port *encoder_out; -+ -+ dev->capture.encode_bitrate = ctrl->val; -+ -+ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, -+ mmal_ctrl->mmal_id, -+ &ctrl->val, sizeof(ctrl->val)); -+ ret = 0; -+ return ret; -+} -+ -+static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 bitrate_mode; -+ struct vchiq_mmal_port *encoder_out; -+ -+ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ dev->capture.encode_bitrate_mode = ctrl->val; -+ switch (ctrl->val) { -+ default: -+ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: -+ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; -+ break; -+ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: -+ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; -+ break; -+ } -+ -+ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, -+ mmal_ctrl->mmal_id, -+ &bitrate_mode, -+ sizeof(bitrate_mode)); -+ return 0; -+} -+ -+static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *jpeg_out; -+ -+ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct bm2835_mmal_dev *dev = -+ container_of(ctrl->handler, struct bm2835_mmal_dev, -+ ctrl_handler); -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; -+ -+ if ((mmal_ctrl == NULL) || -+ (mmal_ctrl->id != ctrl->id) || -+ (mmal_ctrl->setter == NULL)) { -+ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); -+ return -EINVAL; -+ } -+ -+ return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+} -+ -+static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { -+ .s_ctrl = bm2835_mmal_s_ctrl, -+}; -+ -+ -+ -+static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { -+ { -+ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_SATURATION, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, -+ -100, 100, 0, 1, NULL, -+ MMAL_PARAMETER_CONTRAST, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, -+ 0, 100, 50, 1, NULL, -+ MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational -+ }, -+ { -+ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, -+ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, -+ MMAL_PARAMETER_ISO, &ctrl_set_value -+ }, -+ { -+ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value -+ }, -+/* { -+ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL -+ }, -+*/ { -+ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, -+ MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure -+ }, -+/* todo this needs mixing in with set exposure -+ { -+ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, -+ }, -+ */ -+ { -+ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, -+ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, -+ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, -+ MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value -+ }, -+ { -+ V4L2_CID_EXPOSURE_METERING, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, -+ MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode -+ }, -+ { -+ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -+ MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode -+ }, -+ { -+ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, 15, V4L2_COLORFX_NONE, 0, NULL, -+ MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect -+ }, -+ { -+ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, -+ 0, 0xffff, 0x8080, 1, NULL, -+ MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx -+ }, -+ { -+ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, -+ 0, 360, 0, 90, NULL, -+ MMAL_PARAMETER_ROTATION, &ctrl_set_rotate -+ }, -+ { -+ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ }, -+ { -+ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, -+ 0, 1, 0, 1, NULL, -+ MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, -+ 0, 0, bitrate_mode_qmenu, -+ MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, -+ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, -+ MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate -+ }, -+ { -+ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, -+ 0, 100, -+ 30, 1, NULL, -+ MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor -+ }, -+}; -+ -+int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) -+{ -+ int c; -+ int ret; -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) { -+ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], -+ &v4l2_ctrls[c]); -+ if (ret) -+ break; -+ } -+ } -+ return ret; -+} -+ -+int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl_handler *hdl) -+{ -+ int c; -+ const struct bm2835_mmal_v4l2_ctrl *ctrl; -+ -+ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ ctrl = &v4l2_ctrls[c]; -+ -+ switch (ctrl->type) { -+ case MMAL_CONTROL_TYPE_STD: -+ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->min, ctrl->max, ctrl->step, ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_STD_MENU: -+ dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->max, ctrl->min, ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_INT_MENU: -+ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, -+ &bm2835_mmal_ctrl_ops, ctrl->id, -+ ctrl->max, ctrl->def, ctrl->imenu); -+ break; -+ -+ case MMAL_CONTROL_TYPE_CLUSTER: -+ /* skip this entry when constructing controls */ -+ continue; -+ } -+ -+ if (hdl->error) -+ break; -+ -+ dev->ctrls[c]->priv = (void *)ctrl; -+ } -+ -+ if (hdl->error) { -+ pr_err("error adding control %d/%d id 0x%x\n", c, -+ V4L2_CTRL_COUNT, ctrl->id); -+ return hdl->error; -+ } -+ -+ for (c = 0; c < V4L2_CTRL_COUNT; c++) { -+ ctrl = &v4l2_ctrls[c]; -+ -+ switch (ctrl->type) { -+ case MMAL_CONTROL_TYPE_CLUSTER: -+ v4l2_ctrl_auto_cluster(ctrl->min, -+ &dev->ctrls[c+1], -+ ctrl->max, -+ ctrl->def); -+ break; -+ -+ case MMAL_CONTROL_TYPE_STD: -+ case MMAL_CONTROL_TYPE_STD_MENU: -+ case MMAL_CONTROL_TYPE_INT_MENU: -+ break; -+ } -+ -+ } -+ -+ return 0; -+} -diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h -new file mode 100644 -index 0000000..84abbb3 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-common.h -@@ -0,0 +1,51 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * MMAL structures -+ * -+ */ -+ -+#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) -+#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') -+ -+/** Special value signalling that time is not known */ -+#define MMAL_TIME_UNKNOWN (1LL<<63) -+ -+/* mapping between v4l and mmal video modes */ -+struct mmal_fmt { -+ char *name; -+ u32 fourcc; /* v4l2 format id */ -+ u32 mmal; -+ int depth; -+ u32 mmal_component; /* MMAL component index to be used to encode */ -+}; -+ -+/* buffer for one video frame */ -+struct mmal_buffer { -+ /* v4l buffer data -- must be first */ -+ struct vb2_buffer vb; -+ -+ /* list of buffers available */ -+ struct list_head list; -+ -+ void *buffer; /* buffer pointer */ -+ unsigned long buffer_size; /* size of allocated buffer */ -+}; -+ -+/* */ -+struct mmal_colourfx { -+ s32 enable; -+ u32 u; -+ u32 v; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h -new file mode 100644 -index 0000000..856e80e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-encodings.h -@@ -0,0 +1,93 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') -+#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') -+#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') -+#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') -+#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') -+#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') -+#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') -+#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') -+#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') -+#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') -+#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') -+#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') -+#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') -+#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') -+ -+#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') -+#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') -+#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') -+#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') -+#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') -+#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') -+ -+#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') -+#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') -+#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') -+#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') -+#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') -+#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') -+#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') -+#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') -+#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') -+#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') -+#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') -+#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') -+#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') -+#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') -+#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') -+#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') -+#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') -+#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') -+#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') -+#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') -+#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') -+ -+/** SAND Video (YUVUV128) format, native format understood by VideoCore. -+ * This format is *not* opaque - if requested you will receive full frames -+ * of YUV_UV video. -+ */ -+#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') -+ -+/** VideoCore opaque image format, image handles are returned to -+ * the host but not the actual image data. -+ */ -+#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') -+ -+/** An EGL image handle -+ */ -+#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') -+ -+/* }@ */ -+ -+/** \name Pre-defined audio encodings */ -+/* @{ */ -+#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') -+#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') -+#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') -+#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') -+#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') -+#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') -+ -+/* Pre-defined H264 encoding variants */ -+ -+/** ISO 14496-10 Annex B byte stream format */ -+#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 -+/** ISO 14496-15 AVC stream format */ -+#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') -+/** Implicitly delineated NAL units without emulation prevention */ -+#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') -diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h -new file mode 100644 -index 0000000..66e8a6e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-common.h -@@ -0,0 +1,50 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#ifndef MMAL_MSG_COMMON_H -+#define MMAL_MSG_COMMON_H -+ -+enum mmal_msg_status { -+ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ -+ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ -+ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ -+ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ -+ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ -+ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ -+ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ -+ MMAL_MSG_STATUS_EIO, /**< I/O error */ -+ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ -+ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ -+ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ -+ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ -+ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ -+ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ -+ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ -+ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ -+}; -+ -+struct mmal_rect { -+ s32 x; /**< x coordinate (from left) */ -+ s32 y; /**< y coordinate (from top) */ -+ s32 width; /**< width */ -+ s32 height; /**< height */ -+}; -+ -+struct mmal_rational { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ -+}; -+ -+#endif /* MMAL_MSG_COMMON_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h -new file mode 100644 -index 0000000..123d86e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-format.h -@@ -0,0 +1,81 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+#ifndef MMAL_MSG_FORMAT_H -+#define MMAL_MSG_FORMAT_H -+ -+#include "mmal-msg-common.h" -+ -+/* MMAL_ES_FORMAT_T */ -+ -+ -+struct mmal_audio_format { -+ u32 channels; /**< Number of audio channels */ -+ u32 sample_rate; /**< Sample rate */ -+ -+ u32 bits_per_sample; /**< Bits per sample */ -+ u32 block_align; /**< Size of a block of data */ -+}; -+ -+struct mmal_video_format { -+ u32 width; /**< Width of frame in pixels */ -+ u32 height; /**< Height of frame in rows of pixels */ -+ struct mmal_rect crop; /**< Visible region of the frame */ -+ struct mmal_rational frame_rate; /**< Frame rate */ -+ struct mmal_rational par; /**< Pixel aspect ratio */ -+ -+ /* FourCC specifying the color space of the video stream. See the -+ * \ref MmalColorSpace "pre-defined color spaces" for some examples. -+ */ -+ u32 color_space; -+}; -+ -+struct mmal_subpicture_format { -+ u32 x_offset; -+ u32 y_offset; -+}; -+ -+union mmal_es_specific_format { -+ struct mmal_audio_format audio; -+ struct mmal_video_format video; -+ struct mmal_subpicture_format subpicture; -+}; -+ -+/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ -+struct mmal_es_format { -+ u32 type; /* enum mmal_es_type */ -+ -+ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ -+ u32 encoding_variant; /* FourCC specifying the specific -+ * encoding variant of the elementary -+ * stream. -+ */ -+ -+ union mmal_es_specific_format *es; /* TODO: pointers in -+ * message serialisation?!? -+ */ -+ /* Type specific -+ * information for the -+ * elementary stream -+ */ -+ -+ u32 bitrate; /**< Bitrate in bits per second */ -+ u32 flags; /**< Flags describing properties of the elementary stream. */ -+ -+ u32 extradata_size; /**< Size of the codec specific data */ -+ u8 *extradata; /**< Codec specific data */ -+}; -+ -+#endif /* MMAL_MSG_FORMAT_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h -new file mode 100644 -index 0000000..a55c1ea ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg-port.h -@@ -0,0 +1,107 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* MMAL_PORT_TYPE_T */ -+enum mmal_port_type { -+ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ -+ MMAL_PORT_TYPE_CONTROL, /**< Control port */ -+ MMAL_PORT_TYPE_INPUT, /**< Input port */ -+ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ -+ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ -+}; -+ -+/** The port is pass-through and doesn't need buffer headers allocated */ -+#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 -+/** The port wants to allocate the buffer payloads. -+ * This signals a preference that payload allocation should be done -+ * on this port for efficiency reasons. */ -+#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 -+/** The port supports format change events. -+ * This applies to input ports and is used to let the client know -+ * whether the port supports being reconfigured via a format -+ * change event (i.e. without having to disable the port). */ -+#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 -+ -+/* mmal port structure (MMAL_PORT_T) -+ * -+ * most elements are informational only, the pointer values for -+ * interogation messages are generally provided as additional -+ * strucures within the message. When used to set values only teh -+ * buffer_num, buffer_size and userdata parameters are writable. -+ */ -+struct mmal_port { -+ void *priv; /* Private member used by the framework */ -+ const char *name; /* Port name. Used for debugging purposes (RO) */ -+ -+ u32 type; /* Type of the port (RO) enum mmal_port_type */ -+ u16 index; /* Index of the port in its type list (RO) */ -+ u16 index_all; /* Index of the port in the list of all ports (RO) */ -+ -+ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ -+ struct mmal_es_format *format; /* Format of the elementary stream */ -+ -+ u32 buffer_num_min; /* Minimum number of buffers the port -+ * requires (RO). This is set by the -+ * component. -+ */ -+ -+ u32 buffer_size_min; /* Minimum size of buffers the port -+ * requires (RO). This is set by the -+ * component. -+ */ -+ -+ u32 buffer_alignment_min; /* Minimum alignment requirement for -+ * the buffers (RO). A value of -+ * zero means no special alignment -+ * requirements. This is set by the -+ * component. -+ */ -+ -+ u32 buffer_num_recommended; /* Number of buffers the port -+ * recommends for optimal -+ * performance (RO). A value of -+ * zero means no special -+ * recommendation. This is set -+ * by the component. -+ */ -+ -+ u32 buffer_size_recommended; /* Size of buffers the port -+ * recommends for optimal -+ * performance (RO). A value of -+ * zero means no special -+ * recommendation. This is set -+ * by the component. -+ */ -+ -+ u32 buffer_num; /* Actual number of buffers the port will use. -+ * This is set by the client. -+ */ -+ -+ u32 buffer_size; /* Actual maximum size of the buffers that -+ * will be sent to the port. This is set by -+ * the client. -+ */ -+ -+ void *component; /* Component this port belongs to (Read Only) */ -+ -+ void *userdata; /* Field reserved for use by the client */ -+ -+ u32 capabilities; /* Flags describing the capabilities of a -+ * port (RO). Bitwise combination of \ref -+ * portcapabilities "Port capabilities" -+ * values. -+ */ -+ -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h -new file mode 100644 -index 0000000..67b1076 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-msg.h -@@ -0,0 +1,404 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* all the data structures which serialise the MMAL protocol. note -+ * these are directly mapped onto the recived message data. -+ * -+ * BEWARE: They seem to *assume* pointers are u32 and that there is no -+ * structure padding! -+ * -+ * NOTE: this implementation uses kernel types to ensure sizes. Rather -+ * than assigning values to enums to force their size the -+ * implementation uses fixed size types and not the enums (though the -+ * comments have the actual enum type -+ */ -+ -+#define VC_MMAL_VER 15 -+#define VC_MMAL_MIN_VER 10 -+#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") -+ -+/* max total message size is 512 bytes */ -+#define MMAL_MSG_MAX_SIZE 512 -+/* with six 32bit header elements max payload is therefore 488 bytes */ -+#define MMAL_MSG_MAX_PAYLOAD 488 -+ -+#include "mmal-msg-common.h" -+#include "mmal-msg-format.h" -+#include "mmal-msg-port.h" -+ -+enum mmal_msg_type { -+ MMAL_MSG_TYPE_QUIT = 1, -+ MMAL_MSG_TYPE_SERVICE_CLOSED, -+ MMAL_MSG_TYPE_GET_VERSION, -+ MMAL_MSG_TYPE_COMPONENT_CREATE, -+ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ -+ MMAL_MSG_TYPE_COMPONENT_ENABLE, -+ MMAL_MSG_TYPE_COMPONENT_DISABLE, -+ MMAL_MSG_TYPE_PORT_INFO_GET, -+ MMAL_MSG_TYPE_PORT_INFO_SET, -+ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ -+ MMAL_MSG_TYPE_BUFFER_FROM_HOST, -+ MMAL_MSG_TYPE_BUFFER_TO_HOST, -+ MMAL_MSG_TYPE_GET_STATS, -+ MMAL_MSG_TYPE_PORT_PARAMETER_SET, -+ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ -+ MMAL_MSG_TYPE_EVENT_TO_HOST, -+ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, -+ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, -+ MMAL_MSG_TYPE_CONSUME_MEM, -+ MMAL_MSG_TYPE_LMK, /* 20 */ -+ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, -+ MMAL_MSG_TYPE_DRM_GET_LHS32, -+ MMAL_MSG_TYPE_DRM_GET_TIME, -+ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, -+ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ -+ MMAL_MSG_TYPE_HOST_LOG, -+ MMAL_MSG_TYPE_MSG_LAST -+}; -+ -+/* port action request messages differ depending on the action type */ -+enum mmal_msg_port_action_type { -+ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ -+ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ -+ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ -+ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ -+ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ -+}; -+ -+struct mmal_msg_header { -+ u32 magic; -+ u32 type; /** enum mmal_msg_type */ -+ -+ /* Opaque handle to the control service */ -+ struct mmal_control_service *control_service; -+ -+ struct mmal_msg_context *context; /** a u32 per message context */ -+ u32 status; /** The status of the vchiq operation */ -+ u32 padding; -+}; -+ -+/* Send from VC to host to report version */ -+struct mmal_msg_version { -+ u32 flags; -+ u32 major; -+ u32 minor; -+ u32 minimum; -+}; -+ -+/* request to VC to create component */ -+struct mmal_msg_component_create { -+ void *client_component; /* component context */ -+ char name[128]; -+ u32 pid; /* For debug */ -+}; -+ -+/* reply from VC to component creation request */ -+struct mmal_msg_component_create_reply { -+ u32 status; /** enum mmal_msg_status - how does this differ to -+ * the one in the header? -+ */ -+ u32 component_handle; /* VideoCore handle for component */ -+ u32 input_num; /* Number of input ports */ -+ u32 output_num; /* Number of output ports */ -+ u32 clock_num; /* Number of clock ports */ -+}; -+ -+/* request to VC to destroy a component */ -+struct mmal_msg_component_destroy { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_destroy_reply { -+ u32 status; /** The component destruction status */ -+}; -+ -+ -+/* request and reply to VC to enable a component */ -+struct mmal_msg_component_enable { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_enable_reply { -+ u32 status; /** The component enable status */ -+}; -+ -+ -+/* request and reply to VC to disable a component */ -+struct mmal_msg_component_disable { -+ u32 component_handle; -+}; -+ -+struct mmal_msg_component_disable_reply { -+ u32 status; /** The component disable status */ -+}; -+ -+/* request to VC to get port information */ -+struct mmal_msg_port_info_get { -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 index; /* port index to query */ -+}; -+ -+/* reply from VC to get port info request */ -+struct mmal_msg_port_info_get_reply { -+ u32 status; /** enum mmal_msg_status */ -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 port_index; /* port indexed in query */ -+ s32 found; /* unused */ -+ u32 port_handle; /**< Handle to use for this port */ -+ struct mmal_port port; -+ struct mmal_es_format format; /* elementry stream format */ -+ union mmal_es_specific_format es; /* es type specific data */ -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ -+}; -+ -+/* request to VC to set port information */ -+struct mmal_msg_port_info_set { -+ u32 component_handle; -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 port_index; /* port indexed in query */ -+ struct mmal_port port; -+ struct mmal_es_format format; -+ union mmal_es_specific_format es; -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; -+}; -+ -+/* reply from VC to port info set request */ -+struct mmal_msg_port_info_set_reply { -+ u32 status; -+ u32 component_handle; /* component handle port is associated with */ -+ u32 port_type; /* enum mmal_msg_port_type */ -+ u32 index; /* port indexed in query */ -+ s32 found; /* unused */ -+ u32 port_handle; /**< Handle to use for this port */ -+ struct mmal_port port; -+ struct mmal_es_format format; -+ union mmal_es_specific_format es; -+ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; -+}; -+ -+ -+/* port action requests that take a mmal_port as a parameter */ -+struct mmal_msg_port_action_port { -+ u32 component_handle; -+ u32 port_handle; -+ u32 action; /* enum mmal_msg_port_action_type */ -+ struct mmal_port port; -+}; -+ -+/* port action requests that take handles as a parameter */ -+struct mmal_msg_port_action_handle { -+ u32 component_handle; -+ u32 port_handle; -+ u32 action; /* enum mmal_msg_port_action_type */ -+ u32 connect_component_handle; -+ u32 connect_port_handle; -+}; -+ -+struct mmal_msg_port_action_reply { -+ u32 status; /** The port action operation status */ -+}; -+ -+ -+ -+ -+/* MMAL buffer transfer */ -+ -+/** Size of space reserved in a buffer message for short messages. */ -+#define MMAL_VC_SHORT_DATA 128 -+ -+/** Signals that the current payload is the end of the stream of data */ -+#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) -+/** Signals that the start of the current payload starts a frame */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) -+/** Signals that the end of the current payload ends a frame */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) -+/** Signals that the current payload contains only complete frames (>1) */ -+#define MMAL_BUFFER_HEADER_FLAG_FRAME \ -+ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) -+/** Signals that the current payload is a keyframe (i.e. self decodable) */ -+#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) -+/** Signals a discontinuity in the stream of data (e.g. after a seek). -+ * Can be used for instance by a decoder to reset its state */ -+#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) -+/** Signals a buffer containing some kind of config data for the component -+ * (e.g. codec config data) */ -+#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) -+/** Signals an encrypted payload */ -+#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) -+/** Signals a buffer containing side information */ -+#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) -+/** Signals a buffer which is the snapshot/postview image from a stills -+ * capture -+ */ -+#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) -+/** Signals a buffer which contains data known to be corrupted */ -+#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) -+/** Signals that a buffer failed to be transmitted */ -+#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) -+ -+struct mmal_driver_buffer { -+ u32 magic; -+ u32 component_handle; -+ u32 port_handle; -+ void *client_context; -+}; -+ -+/* buffer header */ -+struct mmal_buffer_header { -+ struct mmal_buffer_header *next; /* next header */ -+ void *priv; /* framework private data */ -+ u32 cmd; -+ void *data; -+ u32 alloc_size; -+ u32 length; -+ u32 offset; -+ u32 flags; -+ s64 pts; -+ s64 dts; -+ void *type; -+ void *user_data; -+}; -+ -+struct mmal_buffer_header_type_specific { -+ union { -+ struct { -+ u32 planes; -+ u32 offset[4]; -+ u32 pitch[4]; -+ u32 flags; -+ } video; -+ } u; -+}; -+ -+struct mmal_msg_buffer_from_host { -+ /* The front 32 bytes of the buffer header are copied -+ * back to us in the reply to allow for context. This -+ * area is used to store two mmal_driver_buffer structures to -+ * allow for multiple concurrent service users. -+ */ -+ /* control data */ -+ struct mmal_driver_buffer drvbuf; -+ -+ /* referenced control data for passthrough buffer management */ -+ struct mmal_driver_buffer drvbuf_ref; -+ struct mmal_buffer_header buffer_header; /* buffer header itself */ -+ struct mmal_buffer_header_type_specific buffer_header_type_specific; -+ s32 is_zero_copy; -+ s32 has_reference; -+ -+ /** allows short data to be xfered in control message */ -+ u32 payload_in_message; -+ u8 short_data[MMAL_VC_SHORT_DATA]; -+}; -+ -+ -+/* port parameter setting */ -+ -+#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 -+ -+struct mmal_msg_port_parameter_set { -+ u32 component_handle; /* component */ -+ u32 port_handle; /* port */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; -+}; -+ -+struct mmal_msg_port_parameter_set_reply { -+ u32 status; /** enum mmal_msg_status todo: how does this -+ * differ to the one in the header? -+ */ -+}; -+ -+/* port parameter getting */ -+ -+struct mmal_msg_port_parameter_get { -+ u32 component_handle; /* component */ -+ u32 port_handle; /* port */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+}; -+ -+struct mmal_msg_port_parameter_get_reply { -+ u32 status; /* Status of mmal_port_parameter_get call */ -+ u32 id; /* Parameter ID */ -+ u32 size; /* Parameter size */ -+ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; -+}; -+ -+/* event messages */ -+#define MMAL_WORKER_EVENT_SPACE 256 -+ -+struct mmal_msg_event_to_host { -+ void *client_component; /* component context */ -+ -+ u32 port_type; -+ u32 port_num; -+ -+ u32 cmd; -+ u32 length; -+ u8 data[MMAL_WORKER_EVENT_SPACE]; -+ struct mmal_buffer_header *delayed_buffer; -+}; -+ -+/* all mmal messages are serialised through this structure */ -+struct mmal_msg { -+ /* header */ -+ struct mmal_msg_header h; -+ /* payload */ -+ union { -+ struct mmal_msg_version version; -+ -+ struct mmal_msg_component_create component_create; -+ struct mmal_msg_component_create_reply component_create_reply; -+ -+ struct mmal_msg_component_destroy component_destroy; -+ struct mmal_msg_component_destroy_reply component_destroy_reply; -+ -+ struct mmal_msg_component_enable component_enable; -+ struct mmal_msg_component_enable_reply component_enable_reply; -+ -+ struct mmal_msg_component_disable component_disable; -+ struct mmal_msg_component_disable_reply component_disable_reply; -+ -+ struct mmal_msg_port_info_get port_info_get; -+ struct mmal_msg_port_info_get_reply port_info_get_reply; -+ -+ struct mmal_msg_port_info_set port_info_set; -+ struct mmal_msg_port_info_set_reply port_info_set_reply; -+ -+ struct mmal_msg_port_action_port port_action_port; -+ struct mmal_msg_port_action_handle port_action_handle; -+ struct mmal_msg_port_action_reply port_action_reply; -+ -+ struct mmal_msg_buffer_from_host buffer_from_host; -+ -+ struct mmal_msg_port_parameter_set port_parameter_set; -+ struct mmal_msg_port_parameter_set_reply -+ port_parameter_set_reply; -+ struct mmal_msg_port_parameter_get -+ port_parameter_get; -+ struct mmal_msg_port_parameter_get_reply -+ port_parameter_get_reply; -+ -+ struct mmal_msg_event_to_host event_to_host; -+ -+ u8 payload[MMAL_MSG_MAX_PAYLOAD]; -+ } u; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h -new file mode 100644 -index 0000000..c611b58 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-parameters.h -@@ -0,0 +1,539 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ */ -+ -+/* common parameters */ -+ -+/** @name Parameter groups -+ * Parameters are divided into groups, and then allocated sequentially within -+ * a group using an enum. -+ * @{ -+ */ -+ -+/** Common parameter ID group, used with many types of component. */ -+#define MMAL_PARAMETER_GROUP_COMMON (0<<16) -+/** Camera-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) -+/** Video-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) -+/** Audio-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) -+/** Clock-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) -+/** Miracast-specific parameter ID group. */ -+#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) -+ -+/* Common parameters */ -+enum mmal_parameter_common_type { -+ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ -+ = MMAL_PARAMETER_GROUP_COMMON, -+ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ -+ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ -+ -+ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ -+ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, -+ -+ /** MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ZERO_COPY, -+ -+ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ -+ MMAL_PARAMETER_BUFFER_REQUIREMENTS, -+ -+ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ -+ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ -+ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ -+ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ -+ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ -+ MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ -+}; -+ -+/* camera parameters */ -+ -+enum mmal_parameter_camera_type { -+ /* 0 */ -+ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ -+ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION -+ = MMAL_PARAMETER_GROUP_CAMERA, -+ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ -+ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ -+ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ -+ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ -+ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ -+ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ -+ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ -+ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ -+ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ -+ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ -+ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ -+ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ -+ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ -+ -+ /* 0x10 */ -+ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ -+ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ -+ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ -+ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ -+ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ -+ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ -+ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ -+ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ -+ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ -+ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ -+ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ -+ /* 0x20 */ -+ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ -+ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ -+ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ -+ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ -+ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ -+ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ -+ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ -+ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ -+ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ -+ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ -+ -+ /* 0x30 */ -+ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ -+ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ -+ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAMERA_MIN_ISO, -+ -+ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ -+ MMAL_PARAMETER_CAMERA_USE_CASE, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_CAPTURE_STATS_PASS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ENABLE_REGISTER_FILE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, -+ -+ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ -+ MMAL_PARAMETER_CONFIGFILE_REGISTERS, -+ -+ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ -+ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, -+ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ -+ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ -+ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ -+ -+ /* 0x40 */ -+ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+}; -+ -+enum mmal_parameter_camera_config_timestamp_mode { -+ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ -+ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value -+ * for the frame timestamp -+ */ -+ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp -+ * but subtract the -+ * timestamp of the first -+ * frame sent to give a -+ * zero based timestamp. -+ */ -+}; -+ -+/* camera configuration parameter */ -+struct mmal_parameter_camera_config { -+ /* Parameters for setting up the image pools */ -+ u32 max_stills_w; /* Max size of stills capture */ -+ u32 max_stills_h; -+ u32 stills_yuv422; /* Allow YUV422 stills capture */ -+ u32 one_shot_stills; /* Continuous or one shot stills captures. */ -+ -+ u32 max_preview_video_w; /* Max size of the preview or video -+ * capture frames -+ */ -+ u32 max_preview_video_h; -+ u32 num_preview_video_frames; -+ -+ /** Sets the height of the circular buffer for stills capture. */ -+ u32 stills_capture_circular_buffer_height; -+ -+ /** Allows preview/encode to resume as fast as possible after the stills -+ * input frame has been received, and then processes the still frame in -+ * the background whilst preview/encode has resumed. -+ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. -+ */ -+ u32 fast_preview_resume; -+ -+ /** Selects algorithm for timestamping frames if -+ * there is no clock component connected. -+ * enum mmal_parameter_camera_config_timestamp_mode -+ */ -+ s32 use_stc_timestamp; -+}; -+ -+ -+enum mmal_parameter_exposuremode { -+ MMAL_PARAM_EXPOSUREMODE_OFF, -+ MMAL_PARAM_EXPOSUREMODE_AUTO, -+ MMAL_PARAM_EXPOSUREMODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, -+ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, -+ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, -+ MMAL_PARAM_EXPOSUREMODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMODE_SNOW, -+ MMAL_PARAM_EXPOSUREMODE_BEACH, -+ MMAL_PARAM_EXPOSUREMODE_VERYLONG, -+ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, -+ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, -+ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, -+}; -+ -+enum mmal_parameter_exposuremeteringmode { -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, -+}; -+ -+enum mmal_parameter_awbmode { -+ MMAL_PARAM_AWBMODE_OFF, -+ MMAL_PARAM_AWBMODE_AUTO, -+ MMAL_PARAM_AWBMODE_SUNLIGHT, -+ MMAL_PARAM_AWBMODE_CLOUDY, -+ MMAL_PARAM_AWBMODE_SHADE, -+ MMAL_PARAM_AWBMODE_TUNGSTEN, -+ MMAL_PARAM_AWBMODE_FLUORESCENT, -+ MMAL_PARAM_AWBMODE_INCANDESCENT, -+ MMAL_PARAM_AWBMODE_FLASH, -+ MMAL_PARAM_AWBMODE_HORIZON, -+}; -+ -+enum mmal_parameter_imagefx { -+ MMAL_PARAM_IMAGEFX_NONE, -+ MMAL_PARAM_IMAGEFX_NEGATIVE, -+ MMAL_PARAM_IMAGEFX_SOLARIZE, -+ MMAL_PARAM_IMAGEFX_POSTERIZE, -+ MMAL_PARAM_IMAGEFX_WHITEBOARD, -+ MMAL_PARAM_IMAGEFX_BLACKBOARD, -+ MMAL_PARAM_IMAGEFX_SKETCH, -+ MMAL_PARAM_IMAGEFX_DENOISE, -+ MMAL_PARAM_IMAGEFX_EMBOSS, -+ MMAL_PARAM_IMAGEFX_OILPAINT, -+ MMAL_PARAM_IMAGEFX_HATCH, -+ MMAL_PARAM_IMAGEFX_GPEN, -+ MMAL_PARAM_IMAGEFX_PASTEL, -+ MMAL_PARAM_IMAGEFX_WATERCOLOUR, -+ MMAL_PARAM_IMAGEFX_FILM, -+ MMAL_PARAM_IMAGEFX_BLUR, -+ MMAL_PARAM_IMAGEFX_SATURATION, -+ MMAL_PARAM_IMAGEFX_COLOURSWAP, -+ MMAL_PARAM_IMAGEFX_WASHEDOUT, -+ MMAL_PARAM_IMAGEFX_POSTERISE, -+ MMAL_PARAM_IMAGEFX_COLOURPOINT, -+ MMAL_PARAM_IMAGEFX_COLOURBALANCE, -+ MMAL_PARAM_IMAGEFX_CARTOON, -+}; -+ -+/** Manner of video rate control */ -+enum mmal_parameter_rate_control_mode { -+ MMAL_VIDEO_RATECONTROL_DEFAULT, -+ MMAL_VIDEO_RATECONTROL_VARIABLE, -+ MMAL_VIDEO_RATECONTROL_CONSTANT, -+ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, -+ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES -+}; -+ -+/* video parameters */ -+ -+enum mmal_parameter_video_type { -+ /** @ref MMAL_DISPLAYREGION_T */ -+ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ -+ MMAL_PARAMETER_SUPPORTED_PROFILES, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ -+ MMAL_PARAMETER_PROFILE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_INTRAPERIOD, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ -+ MMAL_PARAMETER_RATECONTROL, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ -+ MMAL_PARAMETER_NALUNITFORMAT, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Setting the value to zero resets to the default (one slice per frame). -+ */ -+ MMAL_PARAMETER_MB_ROWS_PER_SLICE, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ -+ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ -+ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ -+ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ -+ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, -+ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ -+ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ -+ MMAL_PARAMETER_VIDEO_BIT_RATE, -+ -+ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ -+ MMAL_PARAMETER_VIDEO_FRAME_RATE, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, -+ -+ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Changing this parameter from the default can reduce frame rate -+ * because image buffers need to be re-pitched. -+ */ -+ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. -+ * Changing this parameter from the default can reduce frame rate -+ * because image buffers need to be re-pitched. -+ */ -+ MMAL_PARAMETER_VIDEO_ALIGN_VERT, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, -+ -+ /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, -+ -+ /**< @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, -+ -+ /* H264 specific parameters */ -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, -+ -+ /** @ref MMAL_PARAMETER_UINT32_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ -+ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, -+ -+ /** @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, -+ -+ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ -+ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, -+ -+ /** @ref MMAL_PARAMETER_BYTES_T */ -+ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 -+}; -+ -+/** Valid mirror modes */ -+enum mmal_parameter_mirror { -+ MMAL_PARAM_MIRROR_NONE, -+ MMAL_PARAM_MIRROR_VERTICAL, -+ MMAL_PARAM_MIRROR_HORIZONTAL, -+ MMAL_PARAM_MIRROR_BOTH, -+}; -+ -+enum mmal_parameter_displaytransform { -+ MMAL_DISPLAY_ROT0 = 0, -+ MMAL_DISPLAY_MIRROR_ROT0 = 1, -+ MMAL_DISPLAY_MIRROR_ROT180 = 2, -+ MMAL_DISPLAY_ROT180 = 3, -+ MMAL_DISPLAY_MIRROR_ROT90 = 4, -+ MMAL_DISPLAY_ROT270 = 5, -+ MMAL_DISPLAY_ROT90 = 6, -+ MMAL_DISPLAY_MIRROR_ROT270 = 7, -+}; -+ -+enum mmal_parameter_displaymode { -+ MMAL_DISPLAY_MODE_FILL = 0, -+ MMAL_DISPLAY_MODE_LETTERBOX = 1, -+}; -+ -+enum mmal_parameter_displayset { -+ MMAL_DISPLAY_SET_NONE = 0, -+ MMAL_DISPLAY_SET_NUM = 1, -+ MMAL_DISPLAY_SET_FULLSCREEN = 2, -+ MMAL_DISPLAY_SET_TRANSFORM = 4, -+ MMAL_DISPLAY_SET_DEST_RECT = 8, -+ MMAL_DISPLAY_SET_SRC_RECT = 0x10, -+ MMAL_DISPLAY_SET_MODE = 0x20, -+ MMAL_DISPLAY_SET_PIXEL = 0x40, -+ MMAL_DISPLAY_SET_NOASPECT = 0x80, -+ MMAL_DISPLAY_SET_LAYER = 0x100, -+ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, -+ MMAL_DISPLAY_SET_ALPHA = 0x400, -+}; -+ -+struct mmal_parameter_displayregion { -+ /** Bitfield that indicates which fields are set and should be -+ * used. All other fields will maintain their current value. -+ * \ref MMAL_DISPLAYSET_T defines the bits that can be -+ * combined. -+ */ -+ u32 set; -+ -+ /** Describes the display output device, with 0 typically -+ * being a directly connected LCD display. The actual values -+ * will depend on the hardware. Code using hard-wired numbers -+ * (e.g. 2) is certain to fail. -+ */ -+ -+ u32 display_num; -+ /** Indicates that we are using the full device screen area, -+ * rather than a window of the display. If zero, then -+ * dest_rect is used to specify a region of the display to -+ * use. -+ */ -+ -+ s32 fullscreen; -+ /** Indicates any rotation or flipping used to map frames onto -+ * the natural display orientation. -+ */ -+ u32 transform; /* enum mmal_parameter_displaytransform */ -+ -+ /** Where to display the frame within the screen, if -+ * fullscreen is zero. -+ */ -+ struct vchiq_mmal_rect dest_rect; -+ -+ /** Indicates which area of the frame to display. If all -+ * values are zero, the whole frame will be used. -+ */ -+ struct vchiq_mmal_rect src_rect; -+ -+ /** If set to non-zero, indicates that any display scaling -+ * should disregard the aspect ratio of the frame region being -+ * displayed. -+ */ -+ s32 noaspect; -+ -+ /** Indicates how the image should be scaled to fit the -+ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates -+ * that the image should fill the screen by potentially -+ * cropping the frames. Setting \code mode \endcode to \code -+ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the -+ * source region should be displayed and black bars added if -+ * necessary. -+ */ -+ u32 mode; /* enum mmal_parameter_displaymode */ -+ -+ /** If non-zero, defines the width of a source pixel relative -+ * to \code pixel_y \endcode. If zero, then pixels default to -+ * being square. -+ */ -+ u32 pixel_x; -+ -+ /** If non-zero, defines the height of a source pixel relative -+ * to \code pixel_x \endcode. If zero, then pixels default to -+ * being square. -+ */ -+ u32 pixel_y; -+ -+ /** Sets the relative depth of the images, with greater values -+ * being in front of smaller values. -+ */ -+ u32 layer; -+ -+ /** Set to non-zero to ensure copy protection is used on -+ * output. -+ */ -+ s32 copyprotect_required; -+ -+ /** Level of opacity of the layer, where zero is fully -+ * transparent and 255 is fully opaque. -+ */ -+ u32 alpha; -+}; -+ -+#define MMAL_MAX_IMAGEFX_PARAMETERS 5 -+ -+struct mmal_parameter_imagefx_parameters { -+ enum mmal_parameter_imagefx effect; -+ u32 num_effect_params; -+ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; -+}; -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c -new file mode 100644 -index 0000000..a06fb44 ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.c -@@ -0,0 +1,1916 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * V4L2 driver MMAL vchiq interface code -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include +#include -+#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include ++#include "vchiq_core.h" ++#include "vchiq_debugfs.h" + -+#include "mmal-common.h" -+#include "mmal-vchiq.h" -+#include "mmal-msg.h" + -+#define USE_VCHIQ_ARM -+#include "interface/vchi/vchi.h" ++enum vc_suspend_status { ++ VC_SUSPEND_FORCE_CANCELED = -3, /* Force suspend canceled, too busy */ ++ VC_SUSPEND_REJECTED = -2, /* Videocore rejected suspend request */ ++ VC_SUSPEND_FAILED = -1, /* Videocore suspend failed */ ++ VC_SUSPEND_IDLE = 0, /* VC active, no suspend actions */ ++ VC_SUSPEND_REQUESTED, /* User has requested suspend */ ++ VC_SUSPEND_IN_PROGRESS, /* Slot handler has recvd suspend request */ ++ VC_SUSPEND_SUSPENDED /* Videocore suspend succeeded */ ++}; + -+/* maximum number of components supported */ -+#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++enum vc_resume_status { ++ VC_RESUME_FAILED = -1, /* Videocore resume failed */ ++ VC_RESUME_IDLE = 0, /* VC suspended, no resume actions */ ++ VC_RESUME_REQUESTED, /* User has requested resume */ ++ VC_RESUME_IN_PROGRESS, /* Slot handler has received resume request */ ++ VC_RESUME_RESUMED /* Videocore resumed successfully (active) */ ++}; + -+/*#define FULL_MSG_DUMP 1*/ + -+#ifdef DEBUG -+static const char *const msg_type_names[] = { -+ "UNKNOWN", -+ "QUIT", ++enum USE_TYPE_E { ++ USE_TYPE_SERVICE, ++ USE_TYPE_SERVICE_NO_RESUME, ++ USE_TYPE_VCHIQ ++}; ++ ++ ++ ++typedef struct vchiq_arm_state_struct { ++ /* Keepalive-related data */ ++ struct task_struct *ka_thread; ++ struct completion ka_evt; ++ atomic_t ka_use_count; ++ atomic_t ka_use_ack_count; ++ atomic_t ka_release_count; ++ ++ struct completion vc_suspend_complete; ++ struct completion vc_resume_complete; ++ ++ rwlock_t susp_res_lock; ++ enum vc_suspend_status vc_suspend_state; ++ enum vc_resume_status vc_resume_state; ++ ++ unsigned int wake_address; ++ ++ struct timer_list suspend_timer; ++ int suspend_timer_timeout; ++ int suspend_timer_running; ++ ++ /* Global use count for videocore. ++ ** This is equal to the sum of the use counts for all services. When ++ ** this hits zero the videocore suspend procedure will be initiated. ++ */ ++ int videocore_use_count; ++ ++ /* Use count to track requests from videocore peer. ++ ** This use count is not associated with a service, so needs to be ++ ** tracked separately with the state. ++ */ ++ int peer_use_count; ++ ++ /* Flag to indicate whether resume is blocked. This happens when the ++ ** ARM is suspending ++ */ ++ struct completion resume_blocker; ++ int resume_blocked; ++ struct completion blocked_blocker; ++ int blocked_count; ++ ++ int autosuspend_override; ++ ++ /* Flag to indicate that the first vchiq connect has made it through. ++ ** This means that both sides should be fully ready, and we should ++ ** be able to suspend after this point. ++ */ ++ int first_connect; ++ ++ unsigned long long suspend_start_time; ++ unsigned long long sleep_start_time; ++ unsigned long long resume_start_time; ++ unsigned long long last_wake_time; ++ ++} VCHIQ_ARM_STATE_T; ++ ++extern int vchiq_arm_log_level; ++extern int vchiq_susp_log_level; ++ ++extern int __init ++vchiq_platform_init(VCHIQ_STATE_T *state); ++ ++extern void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATE_T * ++vchiq_get_state(void); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_force_suspend(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_arm_allow_resume(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_vcresume(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state); ++ ++extern int ++vchiq_check_resume(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_check_suspend(VCHIQ_STATE_T *state); ++ VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_platform_use_suspend_timer(void); ++ ++extern void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_videocore_wanted(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ enum USE_TYPE_E use_type); ++extern VCHIQ_STATUS_T ++vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_DEBUGFS_NODE_T * ++vchiq_instance_get_debugfs_node(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_pid(VCHIQ_INSTANCE_T instance); ++ ++extern int ++vchiq_instance_get_trace(VCHIQ_INSTANCE_T instance); ++ ++extern void ++vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace); ++ ++extern void ++set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_suspend_status new_state); ++ ++extern void ++set_resume_state(VCHIQ_ARM_STATE_T *arm_state, ++ enum vc_resume_status new_state); ++ ++extern void ++start_suspend_timer(VCHIQ_ARM_STATE_T *arm_state); ++ ++ ++#endif /* VCHIQ_ARM_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h +new file mode 100644 +index 0000000..df64581 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_build_info.h +@@ -0,0 +1,37 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++const char *vchiq_get_build_hostname(void); ++const char *vchiq_get_build_version(void); ++const char *vchiq_get_build_time(void); ++const char *vchiq_get_build_date(void); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +new file mode 100644 +index 0000000..d2797db +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +@@ -0,0 +1,69 @@ ++/** ++ * Copyright (c) 2010-2014 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CFG_H ++#define VCHIQ_CFG_H ++ ++#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') ++/* The version of VCHIQ - change with any non-trivial change */ ++#define VCHIQ_VERSION 8 ++/* The minimum compatible version - update to match VCHIQ_VERSION with any ++** incompatible change */ ++#define VCHIQ_VERSION_MIN 3 ++ ++/* The version that introduced the VCHIQ_IOC_LIB_VERSION ioctl */ ++#define VCHIQ_VERSION_LIB_VERSION 7 ++ ++/* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ ++#define VCHIQ_VERSION_CLOSE_DELIVERED 7 ++ ++/* The version that made it safe to use SYNCHRONOUS mode */ ++#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 ++ ++#define VCHIQ_MAX_STATES 1 ++#define VCHIQ_MAX_SERVICES 4096 ++#define VCHIQ_MAX_SLOTS 128 ++#define VCHIQ_MAX_SLOTS_PER_SIDE 64 ++ ++#define VCHIQ_NUM_CURRENT_BULKS 32 ++#define VCHIQ_NUM_SERVICE_BULKS 4 ++ ++#ifndef VCHIQ_ENABLE_DEBUG ++#define VCHIQ_ENABLE_DEBUG 1 ++#endif ++ ++#ifndef VCHIQ_ENABLE_STATS ++#define VCHIQ_ENABLE_STATS 1 ++#endif ++ ++#endif /* VCHIQ_CFG_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c +new file mode 100644 +index 0000000..5efc62f +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c +@@ -0,0 +1,120 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_connected.h" ++#include "vchiq_core.h" ++#include "vchiq_killable.h" ++#include ++#include ++ ++#define MAX_CALLBACKS 10 ++ ++static int g_connected; ++static int g_num_deferred_callbacks; ++static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[MAX_CALLBACKS]; ++static int g_once_init; ++static struct mutex g_connected_mutex; ++ ++/**************************************************************************** ++* ++* Function to initialize our lock. ++* ++***************************************************************************/ ++ ++static void connected_init(void) ++{ ++ if (!g_once_init) { ++ mutex_init(&g_connected_mutex); ++ g_once_init = 1; ++ } ++} ++ ++/**************************************************************************** ++* ++* This function is used to defer initialization until the vchiq stack is ++* initialized. If the stack is already initialized, then the callback will ++* be made immediately, otherwise it will be deferred until ++* vchiq_call_connected_callbacks is called. ++* ++***************************************************************************/ ++ ++void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback) ++{ ++ connected_init(); ++ ++ if (mutex_lock_interruptible(&g_connected_mutex) != 0) ++ return; ++ ++ if (g_connected) ++ /* We're already connected. Call the callback immediately. */ ++ ++ callback(); ++ else { ++ if (g_num_deferred_callbacks >= MAX_CALLBACKS) ++ vchiq_log_error(vchiq_core_log_level, ++ "There already %d callback registered - " ++ "please increase MAX_CALLBACKS", ++ g_num_deferred_callbacks); ++ else { ++ g_deferred_callback[g_num_deferred_callbacks] = ++ callback; ++ g_num_deferred_callbacks++; ++ } ++ } ++ mutex_unlock(&g_connected_mutex); ++} ++ ++/**************************************************************************** ++* ++* This function is called by the vchiq stack once it has been connected to ++* the videocore and clients can start to use the stack. ++* ++***************************************************************************/ ++ ++void vchiq_call_connected_callbacks(void) ++{ ++ int i; ++ ++ connected_init(); ++ ++ if (mutex_lock_interruptible(&g_connected_mutex) != 0) ++ return; ++ ++ for (i = 0; i < g_num_deferred_callbacks; i++) ++ g_deferred_callback[i](); ++ ++ g_num_deferred_callbacks = 0; ++ g_connected = 1; ++ mutex_unlock(&g_connected_mutex); ++} ++EXPORT_SYMBOL(vchiq_add_connected_callback); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h +new file mode 100644 +index 0000000..863b3e3 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h +@@ -0,0 +1,50 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CONNECTED_H ++#define VCHIQ_CONNECTED_H ++ ++/* ---- Include Files ----------------------------------------------------- */ ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++typedef void (*VCHIQ_CONNECTED_CALLBACK_T)(void); ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_add_connected_callback(VCHIQ_CONNECTED_CALLBACK_T callback); ++void vchiq_call_connected_callbacks(void); ++ ++#endif /* VCHIQ_CONNECTED_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +new file mode 100644 +index 0000000..835688b +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -0,0 +1,3934 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_core.h" ++#include "vchiq_killable.h" ++ ++#define VCHIQ_SLOT_HANDLER_STACK 8192 ++ ++#define HANDLE_STATE_SHIFT 12 ++ ++#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index)) ++#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index)) ++#define SLOT_INDEX_FROM_DATA(state, data) \ ++ (((unsigned int)((char *)data - (char *)state->slot_data)) / \ ++ VCHIQ_SLOT_SIZE) ++#define SLOT_INDEX_FROM_INFO(state, info) \ ++ ((unsigned int)(info - state->slot_info)) ++#define SLOT_QUEUE_INDEX_FROM_POS(pos) \ ++ ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) ++ ++#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) ++ ++#define SRVTRACE_LEVEL(srv) \ ++ (((srv) && (srv)->trace) ? VCHIQ_LOG_TRACE : vchiq_core_msg_log_level) ++#define SRVTRACE_ENABLED(srv, lev) \ ++ (((srv) && (srv)->trace) || (vchiq_core_msg_log_level >= (lev))) ++ ++struct vchiq_open_payload { ++ int fourcc; ++ int client_id; ++ short version; ++ short version_min; ++}; ++ ++struct vchiq_openack_payload { ++ short version; ++}; ++ ++enum ++{ ++ QMFLAGS_IS_BLOCKING = (1 << 0), ++ QMFLAGS_NO_MUTEX_LOCK = (1 << 1), ++ QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2) ++}; ++ ++/* we require this for consistency between endpoints */ ++vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); ++vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); ++vchiq_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS)); ++vchiq_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SERVICES)); ++vchiq_static_assert(VCHIQ_VERSION >= VCHIQ_VERSION_MIN); ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++int vchiq_core_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_core_msg_log_level = VCHIQ_LOG_DEFAULT; ++int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; ++ ++static atomic_t pause_bulks_count = ATOMIC_INIT(0); ++ ++static DEFINE_SPINLOCK(service_spinlock); ++DEFINE_SPINLOCK(bulk_waiter_spinlock); ++DEFINE_SPINLOCK(quota_spinlock); ++ ++VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; ++static unsigned int handle_seq; ++ ++static const char *const srvstate_names[] = { ++ "FREE", ++ "HIDDEN", ++ "LISTENING", ++ "OPENING", ++ "OPEN", ++ "OPENSYNC", ++ "CLOSESENT", ++ "CLOSERECVD", ++ "CLOSEWAIT", ++ "CLOSED" ++}; ++ ++static const char *const reason_names[] = { ++ "SERVICE_OPENED", + "SERVICE_CLOSED", -+ "GET_VERSION", -+ "COMPONENT_CREATE", -+ "COMPONENT_DESTROY", -+ "COMPONENT_ENABLE", -+ "COMPONENT_DISABLE", -+ "PORT_INFO_GET", -+ "PORT_INFO_SET", -+ "PORT_ACTION", -+ "BUFFER_FROM_HOST", -+ "BUFFER_TO_HOST", -+ "GET_STATS", -+ "PORT_PARAMETER_SET", -+ "PORT_PARAMETER_GET", -+ "EVENT_TO_HOST", -+ "GET_CORE_STATS_FOR_PORT", -+ "OPAQUE_ALLOCATOR", -+ "CONSUME_MEM", -+ "LMK", -+ "OPAQUE_ALLOCATOR_DESC", -+ "DRM_GET_LHS32", -+ "DRM_GET_TIME", -+ "BUFFER_FROM_HOST_ZEROLEN", -+ "PORT_FLUSH", -+ "HOST_LOG", -+}; -+#endif -+ -+static const char *const port_action_type_names[] = { -+ "UNKNOWN", -+ "ENABLE", -+ "DISABLE", -+ "FLUSH", -+ "CONNECT", -+ "DISCONNECT", -+ "SET_REQUIREMENTS", ++ "MESSAGE_AVAILABLE", ++ "BULK_TRANSMIT_DONE", ++ "BULK_RECEIVE_DONE", ++ "BULK_TRANSMIT_ABORTED", ++ "BULK_RECEIVE_ABORTED" +}; + -+#if defined(DEBUG) -+#if defined(FULL_MSG_DUMP) -+#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ -+ do { \ -+ pr_debug(TITLE" type:%s(%d) length:%d\n", \ -+ msg_type_names[(MSG)->h.type], \ -+ (MSG)->h.type, (MSG_LEN)); \ -+ print_hex_dump(KERN_DEBUG, "<h.type], \ -+ (MSG)->h.type, (MSG_LEN)); \ -+ } -+#endif -+#else -+#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) -+#endif -+ -+/* normal message context */ -+struct mmal_msg_context { -+ union { -+ struct { -+ /* work struct for defered callback - must come first */ -+ struct work_struct work; -+ /* mmal instance */ -+ struct vchiq_mmal_instance *instance; -+ /* mmal port */ -+ struct vchiq_mmal_port *port; -+ /* actual buffer used to store bulk reply */ -+ struct mmal_buffer *buffer; -+ /* amount of buffer used */ -+ unsigned long buffer_used; -+ /* MMAL buffer flags */ -+ u32 mmal_flags; -+ /* Presentation and Decode timestamps */ -+ s64 pts; -+ s64 dts; -+ -+ int status; /* context status */ -+ -+ } bulk; /* bulk data */ -+ -+ struct { -+ /* message handle to release */ -+ VCHI_HELD_MSG_T msg_handle; -+ /* pointer to received message */ -+ struct mmal_msg *msg; -+ /* received message length */ -+ u32 msg_len; -+ /* completion upon reply */ -+ struct completion cmplt; -+ } sync; /* synchronous response */ -+ } u; -+ ++static const char *const conn_state_names[] = { ++ "DISCONNECTED", ++ "CONNECTING", ++ "CONNECTED", ++ "PAUSING", ++ "PAUSE_SENT", ++ "PAUSED", ++ "RESUMING", ++ "PAUSE_TIMEOUT", ++ "RESUME_TIMEOUT" +}; + -+struct vchiq_mmal_instance { -+ VCHI_SERVICE_HANDLE_T handle; + -+ /* ensure serialised access to service */ -+ struct mutex vchiq_mutex; ++static void ++release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header); + -+ /* ensure serialised access to bulk operations */ -+ struct mutex bulk_mutex; -+ -+ /* vmalloc page to receive scratch bulk xfers into */ -+ void *bulk_scratch; -+ -+ /* component to use next */ -+ int component_idx; -+ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; -+}; -+ -+static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance -+ *instance) ++static const char *msg_type_str(unsigned int msg_type) +{ -+ struct mmal_msg_context *msg_context; -+ -+ /* todo: should this be allocated from a pool to avoid kmalloc */ -+ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); -+ memset(msg_context, 0, sizeof(*msg_context)); -+ -+ return msg_context; ++ switch (msg_type) { ++ case VCHIQ_MSG_PADDING: return "PADDING"; ++ case VCHIQ_MSG_CONNECT: return "CONNECT"; ++ case VCHIQ_MSG_OPEN: return "OPEN"; ++ case VCHIQ_MSG_OPENACK: return "OPENACK"; ++ case VCHIQ_MSG_CLOSE: return "CLOSE"; ++ case VCHIQ_MSG_DATA: return "DATA"; ++ case VCHIQ_MSG_BULK_RX: return "BULK_RX"; ++ case VCHIQ_MSG_BULK_TX: return "BULK_TX"; ++ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE"; ++ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE"; ++ case VCHIQ_MSG_PAUSE: return "PAUSE"; ++ case VCHIQ_MSG_RESUME: return "RESUME"; ++ case VCHIQ_MSG_REMOTE_USE: return "REMOTE_USE"; ++ case VCHIQ_MSG_REMOTE_RELEASE: return "REMOTE_RELEASE"; ++ case VCHIQ_MSG_REMOTE_USE_ACTIVE: return "REMOTE_USE_ACTIVE"; ++ } ++ return "???"; +} + -+static void release_msg_context(struct mmal_msg_context *msg_context) ++static inline void ++vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate) +{ -+ kfree(msg_context); ++ vchiq_log_info(vchiq_core_log_level, "%d: srv:%d %s->%s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate], ++ srvstate_names[newstate]); ++ service->srvstate = newstate; +} + -+/* deals with receipt of event to host message */ -+static void event_to_host_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, u32 msg_len) ++VCHIQ_SERVICE_T * ++find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle) +{ -+ pr_debug("unhandled event\n"); -+ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", -+ msg->u.event_to_host.client_component, -+ msg->u.event_to_host.port_type, -+ msg->u.event_to_host.port_num, -+ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++ VCHIQ_SERVICE_T *service; ++ ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->handle == handle)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); ++ ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); ++ ++ return service; +} + -+/* workqueue scheduled callback -+ * -+ * we do this because it is important we do not call any other vchiq -+ * sync calls from witin the message delivery thread -+ */ -+static void buffer_work_cb(struct work_struct *work) ++VCHIQ_SERVICE_T * ++find_service_by_port(VCHIQ_STATE_T *state, int localport) +{ -+ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; -+ -+ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port, -+ msg_context->u.bulk.status, -+ msg_context->u.bulk.buffer, -+ msg_context->u.bulk.buffer_used, -+ msg_context->u.bulk.mmal_flags, -+ msg_context->u.bulk.dts, -+ msg_context->u.bulk.pts); -+ -+ /* release message context */ -+ release_msg_context(msg_context); -+} -+ -+/* enqueue a bulk receive for a given message context */ -+static int bulk_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ struct mmal_msg_context *msg_context) -+{ -+ unsigned long rd_len; -+ unsigned long flags = 0; -+ int ret; -+ -+ /* bulk mutex stops other bulk operations while we have a -+ * receive in progress - released in callback -+ */ -+ ret = mutex_lock_interruptible(&instance->bulk_mutex); -+ if (ret != 0) -+ return ret; -+ -+ rd_len = msg->u.buffer_from_host.buffer_header.length; -+ -+ /* take buffer from queue */ -+ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); -+ if (list_empty(&msg_context->u.bulk.port->buffers)) { -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ pr_err("buffer list empty trying to submit bulk receive\n"); -+ -+ /* todo: this is a serious error, we should never have -+ * commited a buffer_to_host operation to the mmal -+ * port without the buffer to back it up (underflow -+ * handling) and there is no obvious way to deal with -+ * this - how is the mmal servie going to react when -+ * we fail to do the xfer and reschedule a buffer when -+ * it arrives? perhaps a starved flag to indicate a -+ * waiting bulk receive? -+ */ -+ -+ mutex_unlock(&instance->bulk_mutex); -+ -+ return -EINVAL; ++ VCHIQ_SERVICE_T *service = NULL; ++ if ((unsigned int)localport <= VCHIQ_PORT_MAX) { ++ spin_lock(&service_spinlock); ++ service = state->services[localport]; ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + } + -+ msg_context->u.bulk.buffer = -+ list_entry(msg_context->u.bulk.port->buffers.next, -+ struct mmal_buffer, list); -+ list_del(&msg_context->u.bulk.buffer->list); ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid port %d", localport); + -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ -+ /* ensure we do not overrun the available buffer */ -+ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { -+ rd_len = msg_context->u.bulk.buffer->buffer_size; -+ pr_warn("short read as not enough receive buffer space\n"); -+ /* todo: is this the correct response, what happens to -+ * the rest of the message data? -+ */ -+ } -+ -+ /* store length */ -+ msg_context->u.bulk.buffer_used = rd_len; -+ msg_context->u.bulk.mmal_flags = -+ msg->u.buffer_from_host.buffer_header.flags; -+ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; -+ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; -+ -+ // only need to flush L1 cache here, as VCHIQ takes care of the L2 -+ // cache. -+ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); -+ -+ /* queue the bulk submission */ -+ vchi_service_use(instance->handle); -+ ret = vchi_bulk_queue_receive(instance->handle, -+ msg_context->u.bulk.buffer->buffer, -+ /* Actual receive needs to be a multiple -+ * of 4 bytes -+ */ -+ (rd_len + 3) & ~3, -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, -+ msg_context); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret != 0) { -+ /* callback will not be clearing the mutex */ -+ mutex_unlock(&instance->bulk_mutex); -+ } -+ -+ return ret; ++ return service; +} + -+/* enque a dummy bulk receive for a given message context */ -+static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ int ret; ++VCHIQ_SERVICE_T * ++find_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle) { ++ VCHIQ_SERVICE_T *service; + -+ /* bulk mutex stops other bulk operations while we have a -+ * receive in progress - released in callback -+ */ -+ ret = mutex_lock_interruptible(&instance->bulk_mutex); -+ if (ret != 0) -+ return ret; ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->handle == handle) && ++ (service->instance == instance)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + -+ /* zero length indicates this was a dummy transfer */ -+ msg_context->u.bulk.buffer_used = 0; ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); + -+ /* queue the bulk submission */ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_bulk_queue_receive(instance->handle, -+ instance->bulk_scratch, -+ 8, -+ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, -+ msg_context); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret != 0) { -+ /* callback will not be clearing the mutex */ -+ mutex_unlock(&instance->bulk_mutex); -+ } -+ -+ return ret; ++ return service; +} + -+/* data in message, memcpy from packet into output buffer */ -+static int inline_receive(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ struct mmal_msg_context *msg_context) -+{ -+ unsigned long flags = 0; ++VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle) { ++ VCHIQ_SERVICE_T *service; + -+ /* take buffer from queue */ -+ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); -+ if (list_empty(&msg_context->u.bulk.port->buffers)) { -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ pr_err("buffer list empty trying to receive inline\n"); ++ spin_lock(&service_spinlock); ++ service = handle_to_service(handle); ++ if (service && ++ ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_CLOSED)) && ++ (service->handle == handle) && ++ (service->instance == instance)) { ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; ++ } else ++ service = NULL; ++ spin_unlock(&service_spinlock); + -+ /* todo: this is a serious error, we should never have -+ * commited a buffer_to_host operation to the mmal -+ * port without the buffer to back it up (with -+ * underflow handling) and there is no obvious way to -+ * deal with this. Less bad than the bulk case as we -+ * can just drop this on the floor but...unhelpful -+ */ -+ return -EINVAL; -+ } ++ if (!service) ++ vchiq_log_info(vchiq_core_log_level, ++ "Invalid service handle 0x%x", handle); + -+ msg_context->u.bulk.buffer = -+ list_entry(msg_context->u.bulk.port->buffers.next, -+ struct mmal_buffer, list); -+ list_del(&msg_context->u.bulk.buffer->list); -+ -+ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); -+ -+ memcpy(msg_context->u.bulk.buffer->buffer, -+ msg->u.buffer_from_host.short_data, -+ msg->u.buffer_from_host.payload_in_message); -+ -+ msg_context->u.bulk.buffer_used = -+ msg->u.buffer_from_host.payload_in_message; -+ -+ return 0; ++ return service; +} + -+/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ -+static int -+buffer_from_host(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++VCHIQ_SERVICE_T * ++next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, ++ int *pidx) +{ -+ struct mmal_msg_context *msg_context; -+ struct mmal_msg m; -+ int ret; ++ VCHIQ_SERVICE_T *service = NULL; ++ int idx = *pidx; + -+ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); -+ -+ /* bulk mutex stops other bulk operations while we -+ * have a receive in progress -+ */ -+ if (mutex_lock_interruptible(&instance->bulk_mutex)) -+ return -EINTR; -+ -+ /* get context */ -+ msg_context = get_msg_context(instance); -+ if (msg_context == NULL) -+ return -ENOMEM; -+ -+ /* store bulk message context for when data arrives */ -+ msg_context->u.bulk.instance = instance; -+ msg_context->u.bulk.port = port; -+ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ -+ msg_context->u.bulk.buffer_used = 0; -+ -+ /* initialise work structure ready to schedule callback */ -+ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); -+ -+ /* prep the buffer from host message */ -+ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ -+ -+ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; -+ m.h.magic = MMAL_MAGIC; -+ m.h.context = msg_context; -+ m.h.status = 0; -+ -+ /* drvbuf is our private data passed back */ -+ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; -+ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; -+ m.u.buffer_from_host.drvbuf.port_handle = port->handle; -+ m.u.buffer_from_host.drvbuf.client_context = msg_context; -+ -+ /* buffer header */ -+ m.u.buffer_from_host.buffer_header.cmd = 0; -+ m.u.buffer_from_host.buffer_header.data = buf->buffer; -+ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; -+ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ -+ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ -+ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ -+ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; -+ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; -+ -+ /* clear buffer type sepecific data */ -+ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, -+ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); -+ -+ /* no payload in message */ -+ m.u.buffer_from_host.payload_in_message = 0; -+ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_msg_queue(instance->handle, &m, -+ sizeof(struct mmal_msg_header) + -+ sizeof(m.u.buffer_from_host), -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ if (ret != 0) { -+ release_msg_context(msg_context); -+ /* todo: is this correct error value? */ -+ } -+ -+ vchi_service_release(instance->handle); -+ -+ mutex_unlock(&instance->bulk_mutex); -+ -+ return ret; -+} -+ -+/* submit a buffer to the mmal sevice -+ * -+ * the buffer_from_host uses size data from the ports next available -+ * mmal_buffer and deals with there being no buffer available by -+ * incrementing the underflow for later -+ */ -+static int port_buffer_from_host(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_buffer *buf; -+ unsigned long flags = 0; -+ -+ if (!port->enabled) -+ return -EINVAL; -+ -+ /* peek buffer from queue */ -+ spin_lock_irqsave(&port->slock, flags); -+ if (list_empty(&port->buffers)) { -+ port->buffer_underflow++; -+ spin_unlock_irqrestore(&port->slock, flags); -+ return -ENOSPC; -+ } -+ -+ buf = list_entry(port->buffers.next, struct mmal_buffer, list); -+ -+ spin_unlock_irqrestore(&port->slock, flags); -+ -+ /* issue buffer to mmal service */ -+ ret = buffer_from_host(instance, port, buf); -+ if (ret) { -+ pr_err("adding buffer header failed\n"); -+ /* todo: how should this be dealt with */ -+ } -+ -+ return ret; -+} -+ -+/* deals with receipt of buffer to host message */ -+static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, u32 msg_len) -+{ -+ struct mmal_msg_context *msg_context; -+ -+ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", -+ instance, msg, msg_len); -+ -+ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { -+ msg_context = msg->u.buffer_from_host.drvbuf.client_context; -+ } else { -+ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); -+ return; -+ } -+ -+ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { -+ /* message reception had an error */ -+ pr_warn("error %d in reply\n", msg->h.status); -+ -+ msg_context->u.bulk.status = msg->h.status; -+ -+ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { -+ /* empty buffer */ -+ if (msg->u.buffer_from_host.buffer_header.flags & -+ MMAL_BUFFER_HEADER_FLAG_EOS) { -+ msg_context->u.bulk.status = -+ dummy_bulk_receive(instance, msg_context); -+ if (msg_context->u.bulk.status == 0) -+ return; /* successful bulk submission, bulk -+ * completion will trigger callback -+ */ -+ } else { -+ /* do callback with empty buffer - not EOS though */ -+ msg_context->u.bulk.status = 0; -+ msg_context->u.bulk.buffer_used = 0; -+ } -+ } else if (msg->u.buffer_from_host.payload_in_message == 0) { -+ /* data is not in message, queue a bulk receive */ -+ msg_context->u.bulk.status = -+ bulk_receive(instance, msg, msg_context); -+ if (msg_context->u.bulk.status == 0) -+ return; /* successful bulk submission, bulk -+ * completion will trigger callback -+ */ -+ -+ /* failed to submit buffer, this will end badly */ -+ pr_err("error %d on bulk submission\n", -+ msg_context->u.bulk.status); -+ -+ } else if (msg->u.buffer_from_host.payload_in_message <= -+ MMAL_VC_SHORT_DATA) { -+ /* data payload within message */ -+ msg_context->u.bulk.status = inline_receive(instance, msg, -+ msg_context); -+ } else { -+ pr_err("message with invalid short payload\n"); -+ -+ /* signal error */ -+ msg_context->u.bulk.status = -EINVAL; -+ msg_context->u.bulk.buffer_used = -+ msg->u.buffer_from_host.payload_in_message; -+ } -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(instance, msg_context->u.bulk.port); -+ -+ /* schedule the port callback */ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+static void bulk_receive_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ /* bulk receive operation complete */ -+ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port); -+ -+ msg_context->u.bulk.status = 0; -+ -+ /* schedule the port callback */ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+static void bulk_abort_cb(struct vchiq_mmal_instance *instance, -+ struct mmal_msg_context *msg_context) -+{ -+ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); -+ -+ /* bulk receive operation complete */ -+ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); -+ -+ /* replace the buffer header */ -+ port_buffer_from_host(msg_context->u.bulk.instance, -+ msg_context->u.bulk.port); -+ -+ msg_context->u.bulk.status = -EINTR; -+ -+ schedule_work(&msg_context->u.bulk.work); -+} -+ -+/* incoming event service callback */ -+static void service_callback(void *param, -+ const VCHI_CALLBACK_REASON_T reason, -+ void *bulk_ctx) -+{ -+ struct vchiq_mmal_instance *instance = param; -+ int status; -+ u32 msg_len; -+ struct mmal_msg *msg; -+ VCHI_HELD_MSG_T msg_handle; -+ -+ if (!instance) { -+ pr_err("Message callback passed NULL instance\n"); -+ return; -+ } -+ -+ switch (reason) { -+ case VCHI_CALLBACK_MSG_AVAILABLE: -+ status = vchi_msg_hold(instance->handle, (void **)&msg, -+ &msg_len, VCHI_FLAGS_NONE, &msg_handle); -+ if (status) { -+ pr_err("Unable to dequeue a message (%d)\n", status); ++ spin_lock(&service_spinlock); ++ while (idx < state->unused_service) { ++ VCHIQ_SERVICE_T *srv = state->services[idx++]; ++ if (srv && (srv->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (srv->instance == instance)) { ++ service = srv; ++ BUG_ON(service->ref_count == 0); ++ service->ref_count++; + break; + } ++ } ++ spin_unlock(&service_spinlock); + -+ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ *pidx = idx; + -+ /* handling is different for buffer messages */ -+ switch (msg->h.type) { ++ return service; ++} + -+ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: -+ vchi_held_msg_release(&msg_handle); -+ break; ++void ++lock_service(VCHIQ_SERVICE_T *service) ++{ ++ spin_lock(&service_spinlock); ++ BUG_ON(!service || (service->ref_count == 0)); ++ if (service) ++ service->ref_count++; ++ spin_unlock(&service_spinlock); ++} + -+ case MMAL_MSG_TYPE_EVENT_TO_HOST: -+ event_to_host_cb(instance, msg, msg_len); -+ vchi_held_msg_release(&msg_handle); ++void ++unlock_service(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ spin_lock(&service_spinlock); ++ BUG_ON(!service || (service->ref_count == 0)); ++ if (service && service->ref_count) { ++ service->ref_count--; ++ if (!service->ref_count) { ++ BUG_ON(service->srvstate != VCHIQ_SRVSTATE_FREE); ++ state->services[service->localport] = NULL; ++ } else ++ service = NULL; ++ } ++ spin_unlock(&service_spinlock); + -+ break; ++ if (service && service->userdata_term) ++ service->userdata_term(service->base.userdata); + -+ case MMAL_MSG_TYPE_BUFFER_TO_HOST: -+ buffer_to_host_cb(instance, msg, msg_len); -+ vchi_held_msg_release(&msg_handle); -+ break; ++ kfree(service); ++} + -+ default: -+ /* messages dependant on header context to complete */ ++int ++vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ int id; + -+ /* todo: the msg.context really ought to be sanity -+ * checked before we just use it, afaict it comes back -+ * and is used raw from the videocore. Perhaps it -+ * should be verified the address lies in the kernel -+ * address space. -+ */ -+ if (msg->h.context == NULL) { -+ pr_err("received message context was null!\n"); -+ vchi_held_msg_release(&msg_handle); -+ break; ++ id = service ? service->client_id : 0; ++ if (service) ++ unlock_service(service); ++ ++ return id; ++} ++ ++void * ++vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = handle_to_service(handle); ++ ++ return service ? service->base.userdata : NULL; ++} ++ ++int ++vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = handle_to_service(handle); ++ ++ return service ? service->base.fourcc : 0; ++} ++ ++static void ++mark_service_closing_internal(VCHIQ_SERVICE_T *service, int sh_thread) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ VCHIQ_SERVICE_QUOTA_T *service_quota; ++ ++ service->closing = 1; ++ ++ /* Synchronise with other threads. */ ++ mutex_lock(&state->recycle_mutex); ++ mutex_unlock(&state->recycle_mutex); ++ if (!sh_thread || (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT)) { ++ /* If we're pausing then the slot_mutex is held until resume ++ * by the slot handler. Therefore don't try to acquire this ++ * mutex if we're the slot handler and in the pause sent state. ++ * We don't need to in this case anyway. */ ++ mutex_lock(&state->slot_mutex); ++ mutex_unlock(&state->slot_mutex); ++ } ++ ++ /* Unblock any sending thread. */ ++ service_quota = &state->service_quotas[service->localport]; ++ up(&service_quota->quota_event); ++} ++ ++static void ++mark_service_closing(VCHIQ_SERVICE_T *service) ++{ ++ mark_service_closing_internal(service, 0); ++} ++ ++static inline VCHIQ_STATUS_T ++make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, void *bulk_userdata) ++{ ++ VCHIQ_STATUS_T status; ++ vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %x, %x)", ++ service->state->id, service->localport, reason_names[reason], ++ (unsigned int)header, (unsigned int)bulk_userdata); ++ status = service->base.callback(reason, header, service->handle, ++ bulk_userdata); ++ if (status == VCHIQ_ERROR) { ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: ignoring ERROR from callback to service %x", ++ service->state->id, service->handle); ++ status = VCHIQ_SUCCESS; ++ } ++ return status; ++} ++ ++inline void ++vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) ++{ ++ VCHIQ_CONNSTATE_T oldstate = state->conn_state; ++ vchiq_log_info(vchiq_core_log_level, "%d: %s->%s", state->id, ++ conn_state_names[oldstate], ++ conn_state_names[newstate]); ++ state->conn_state = newstate; ++ vchiq_platform_conn_state_changed(state, oldstate, newstate); ++} ++ ++static inline void ++remote_event_create(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ /* Don't clear the 'fired' flag because it may already have been set ++ ** by the other side. */ ++ sema_init(event->event, 0); ++} ++ ++static inline void ++remote_event_destroy(REMOTE_EVENT_T *event) ++{ ++ (void)event; ++} ++ ++static inline int ++remote_event_wait(REMOTE_EVENT_T *event) ++{ ++ if (!event->fired) { ++ event->armed = 1; ++ dsb(); ++ if (!event->fired) { ++ if (down_interruptible(event->event) != 0) { ++ event->armed = 0; ++ return 0; ++ } ++ } ++ event->armed = 0; ++ wmb(); ++ } ++ ++ event->fired = 0; ++ return 1; ++} ++ ++static inline void ++remote_event_signal_local(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ up(event->event); ++} ++ ++static inline void ++remote_event_poll(REMOTE_EVENT_T *event) ++{ ++ if (event->fired && event->armed) ++ remote_event_signal_local(event); ++} ++ ++void ++remote_event_pollall(VCHIQ_STATE_T *state) ++{ ++ remote_event_poll(&state->local->sync_trigger); ++ remote_event_poll(&state->local->sync_release); ++ remote_event_poll(&state->local->trigger); ++ remote_event_poll(&state->local->recycle); ++} ++ ++/* Round up message sizes so that any space at the end of a slot is always big ++** enough for a header. This relies on header size being a power of two, which ++** has been verified earlier by a static assertion. */ ++ ++static inline unsigned int ++calc_stride(unsigned int size) ++{ ++ /* Allow room for the header */ ++ size += sizeof(VCHIQ_HEADER_T); ++ ++ /* Round up */ ++ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) ++ - 1); ++} ++ ++/* Called by the slot handler thread */ ++static VCHIQ_SERVICE_T * ++get_listening_service(VCHIQ_STATE_T *state, int fourcc) ++{ ++ int i; ++ ++ WARN_ON(fourcc == VCHIQ_FOURCC_INVALID); ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && ++ (service->public_fourcc == fourcc) && ++ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && ++ (service->remoteport == VCHIQ_PORT_FREE)))) { ++ lock_service(service); ++ return service; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Called by the slot handler thread */ ++static VCHIQ_SERVICE_T * ++get_connected_service(VCHIQ_STATE_T *state, unsigned int port) ++{ ++ int i; ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) ++ && (service->remoteport == port)) { ++ lock_service(service); ++ return service; ++ } ++ } ++ return NULL; ++} ++ ++inline void ++request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type) ++{ ++ uint32_t value; ++ ++ if (service) { ++ do { ++ value = atomic_read(&service->poll_flags); ++ } while (atomic_cmpxchg(&service->poll_flags, value, ++ value | (1 << poll_type)) != value); ++ ++ do { ++ value = atomic_read(&state->poll_services[ ++ service->localport>>5]); ++ } while (atomic_cmpxchg( ++ &state->poll_services[service->localport>>5], ++ value, value | (1 << (service->localport & 0x1f))) ++ != value); ++ } ++ ++ state->poll_needed = 1; ++ wmb(); ++ ++ /* ... and ensure the slot handler runs. */ ++ remote_event_signal_local(&state->local->trigger); ++} ++ ++/* Called from queue_message, by the slot handler and application threads, ++** with slot_mutex held */ ++static VCHIQ_HEADER_T * ++reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ int tx_pos = state->local_tx_pos; ++ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK); ++ ++ if (space > slot_space) { ++ VCHIQ_HEADER_T *header; ++ /* Fill the remaining space with padding */ ++ WARN_ON(state->tx_data == NULL); ++ header = (VCHIQ_HEADER_T *) ++ (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++ header->msgid = VCHIQ_MSGID_PADDING; ++ header->size = slot_space - sizeof(VCHIQ_HEADER_T); ++ ++ tx_pos += slot_space; ++ } ++ ++ /* If necessary, get the next slot. */ ++ if ((tx_pos & VCHIQ_SLOT_MASK) == 0) { ++ int slot_index; ++ ++ /* If there is no free slot... */ ++ ++ if (down_trylock(&state->slot_available_event) != 0) { ++ /* ...wait for one. */ ++ ++ VCHIQ_STATS_INC(state, slot_stalls); ++ ++ /* But first, flush through the last slot. */ ++ state->local_tx_pos = tx_pos; ++ local->tx_pos = tx_pos; ++ remote_event_signal(&state->remote->trigger); ++ ++ if (!is_blocking || ++ (down_interruptible( ++ &state->slot_available_event) != 0)) ++ return NULL; /* No space available */ ++ } ++ ++ BUG_ON(tx_pos == ++ (state->slot_queue_available * VCHIQ_SLOT_SIZE)); ++ ++ slot_index = local->slot_queue[ ++ SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ state->tx_data = ++ (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ } ++ ++ state->local_tx_pos = tx_pos + space; ++ ++ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++} ++ ++/* Called by the recycle thread. */ ++static void ++process_free_queue(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ int slot_queue_available; ++ ++ /* Use a read memory barrier to ensure that any state that may have ++ ** been modified by another thread is not masked by stale prefetched ++ ** values. */ ++ rmb(); ++ ++ /* Find slots which have been freed by the other side, and return them ++ ** to the available queue. */ ++ slot_queue_available = state->slot_queue_available; ++ ++ while (slot_queue_available != local->slot_queue_recycle) { ++ unsigned int pos; ++ int slot_index = local->slot_queue[slot_queue_available++ & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ int data_found = 0; ++ ++ vchiq_log_trace(vchiq_core_log_level, "%d: pfq %d=%x %x %x", ++ state->id, slot_index, (unsigned int)data, ++ local->slot_queue_recycle, slot_queue_available); ++ ++ /* Initialise the bitmask for services which have used this ++ ** slot */ ++ BITSET_ZERO(service_found); ++ ++ pos = 0; ++ ++ while (pos < VCHIQ_SLOT_SIZE) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) { ++ int port = VCHIQ_MSG_SRCPORT(msgid); ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[port]; ++ int count; ++ spin_lock("a_spinlock); ++ count = service_quota->message_use_count; ++ if (count > 0) ++ service_quota->message_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ ++ if (count == service_quota->message_quota) ++ /* Signal the service that it ++ ** has dropped below its quota ++ */ ++ up(&service_quota->quota_event); ++ else if (count == 0) { ++ vchiq_log_error(vchiq_core_log_level, ++ "service %d " ++ "message_use_count=%d " ++ "(header %x, msgid %x, " ++ "header->msgid %x, " ++ "header->size %x)", ++ port, ++ service_quota-> ++ message_use_count, ++ (unsigned int)header, msgid, ++ header->msgid, ++ header->size); ++ WARN(1, "invalid message use count\n"); ++ } ++ if (!BITSET_IS_SET(service_found, port)) { ++ /* Set the found bit for this service */ ++ BITSET_SET(service_found, port); ++ ++ spin_lock("a_spinlock); ++ count = service_quota->slot_use_count; ++ if (count > 0) ++ service_quota->slot_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ ++ if (count > 0) { ++ /* Signal the service in case ++ ** it has dropped below its ++ ** quota */ ++ up(&service_quota->quota_event); ++ vchiq_log_trace( ++ vchiq_core_log_level, ++ "%d: pfq:%d %x@%x - " ++ "slot_use->%d", ++ state->id, port, ++ header->size, ++ (unsigned int)header, ++ count - 1); ++ } else { ++ vchiq_log_error( ++ vchiq_core_log_level, ++ "service %d " ++ "slot_use_count" ++ "=%d (header %x" ++ ", msgid %x, " ++ "header->msgid" ++ " %x, header->" ++ "size %x)", ++ port, count, ++ (unsigned int)header, ++ msgid, ++ header->msgid, ++ header->size); ++ WARN(1, "bad slot use count\n"); ++ } ++ } ++ ++ data_found = 1; + } + -+ /* fill in context values */ -+ msg->h.context->u.sync.msg_handle = msg_handle; -+ msg->h.context->u.sync.msg = msg; -+ msg->h.context->u.sync.msg_len = msg_len; -+ -+ /* todo: should this check (completion_done() -+ * == 1) for no one waiting? or do we need a -+ * flag to tell us the completion has been -+ * interrupted so we can free the message and -+ * its context. This probably also solves the -+ * message arriving after interruption todo -+ * below -+ */ -+ -+ /* complete message so caller knows it happened */ -+ complete(&msg->h.context->u.sync.cmplt); -+ break; ++ pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "pfq - pos %x: header %x, msgid %x, " ++ "header->msgid %x, header->size %x", ++ pos, (unsigned int)header, msgid, ++ header->msgid, header->size); ++ WARN(1, "invalid slot position\n"); ++ } + } + -+ break; -+ -+ case VCHI_CALLBACK_BULK_RECEIVED: -+ bulk_receive_cb(instance, bulk_ctx); -+ break; -+ -+ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: -+ bulk_abort_cb(instance, bulk_ctx); -+ break; -+ -+ case VCHI_CALLBACK_SERVICE_CLOSED: -+ /* TODO: consider if this requires action if received when -+ * driver is not explicitly closing the service -+ */ -+ break; -+ -+ default: -+ pr_err("Received unhandled message reason %d\n", reason); -+ break; -+ } -+} -+ -+static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, -+ struct mmal_msg *msg, -+ unsigned int payload_len, -+ struct mmal_msg **msg_out, -+ VCHI_HELD_MSG_T *msg_handle_out) -+{ -+ struct mmal_msg_context msg_context; -+ int ret; -+ -+ /* payload size must not cause message to exceed max size */ -+ if (payload_len > -+ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { -+ pr_err("payload length %d exceeds max:%d\n", payload_len, -+ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); -+ return -EINVAL; -+ } -+ -+ init_completion(&msg_context.u.sync.cmplt); -+ -+ msg->h.magic = MMAL_MAGIC; -+ msg->h.context = &msg_context; -+ msg->h.status = 0; -+ -+ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), -+ ">>> sync message"); -+ -+ vchi_service_use(instance->handle); -+ -+ ret = vchi_msg_queue(instance->handle, -+ msg, -+ sizeof(struct mmal_msg_header) + payload_len, -+ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); -+ -+ vchi_service_release(instance->handle); -+ -+ if (ret) { -+ pr_err("error %d queuing message\n", ret); -+ return ret; -+ } -+ -+ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); -+ if (ret <= 0) { -+ pr_err("error %d waiting for sync completion\n", ret); -+ if (ret == 0) -+ ret = -ETIME; -+ /* todo: what happens if the message arrives after aborting */ -+ return ret; -+ } -+ -+ *msg_out = msg_context.u.sync.msg; -+ *msg_handle_out = msg_context.u.sync.msg_handle; -+ -+ return 0; -+} -+ -+static void dump_port_info(struct vchiq_mmal_port *port) -+{ -+ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); -+ -+ pr_debug("buffer minimum num:%d size:%d align:%d\n", -+ port->minimum_buffer.num, -+ port->minimum_buffer.size, port->minimum_buffer.alignment); -+ -+ pr_debug("buffer recommended num:%d size:%d align:%d\n", -+ port->recommended_buffer.num, -+ port->recommended_buffer.size, -+ port->recommended_buffer.alignment); -+ -+ pr_debug("buffer current values num:%d size:%d align:%d\n", -+ port->current_buffer.num, -+ port->current_buffer.size, port->current_buffer.alignment); -+ -+ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", -+ port->format.type, -+ port->format.encoding, port->format.encoding_variant); -+ -+ pr_debug(" bitrate:%d flags:0x%x\n", -+ port->format.bitrate, port->format.flags); -+ -+ if (port->format.type == MMAL_ES_TYPE_VIDEO) { -+ pr_debug -+ ("es video format: width:%d height:%d colourspace:0x%x\n", -+ port->es.video.width, port->es.video.height, -+ port->es.video.color_space); -+ -+ pr_debug(" : crop xywh %d,%d,%d,%d\n", -+ port->es.video.crop.x, -+ port->es.video.crop.y, -+ port->es.video.crop.width, port->es.video.crop.height); -+ pr_debug(" : framerate %d/%d aspect %d/%d\n", -+ port->es.video.frame_rate.num, -+ port->es.video.frame_rate.den, -+ port->es.video.par.num, port->es.video.par.den); -+ } -+} -+ -+static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) -+{ -+ -+ /* todo do readonly fields need setting at all? */ -+ p->type = port->type; -+ p->index = port->index; -+ p->index_all = 0; -+ p->is_enabled = port->enabled; -+ p->buffer_num_min = port->minimum_buffer.num; -+ p->buffer_size_min = port->minimum_buffer.size; -+ p->buffer_alignment_min = port->minimum_buffer.alignment; -+ p->buffer_num_recommended = port->recommended_buffer.num; -+ p->buffer_size_recommended = port->recommended_buffer.size; -+ -+ /* only three writable fields in a port */ -+ p->buffer_num = port->current_buffer.num; -+ p->buffer_size = port->current_buffer.size; -+ p->userdata = port; -+} -+ -+static int port_info_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ pr_debug("setting port info port %p\n", port); -+ if (!port) -+ return -1; -+ dump_port_info(port); -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; -+ -+ m.u.port_info_set.component_handle = port->component->handle; -+ m.u.port_info_set.port_type = port->type; -+ m.u.port_info_set.port_index = port->index; -+ -+ port_to_mmal_msg(port, &m.u.port_info_set.port); -+ -+ /* elementry stream format setup */ -+ m.u.port_info_set.format.type = port->format.type; -+ m.u.port_info_set.format.encoding = port->format.encoding; -+ m.u.port_info_set.format.encoding_variant = -+ port->format.encoding_variant; -+ m.u.port_info_set.format.bitrate = port->format.bitrate; -+ m.u.port_info_set.format.flags = port->format.flags; -+ -+ memcpy(&m.u.port_info_set.es, &port->es, -+ sizeof(union mmal_es_specific_format)); -+ -+ m.u.port_info_set.format.extradata_size = port->format.extradata_size; -+ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, -+ port->format.extradata_size); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_info_set), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ /* return operation status */ -+ ret = -rmsg->u.port_info_get_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, -+ port->component->handle, port->handle); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+ -+} -+ -+/* use port info get message to retrive port information */ -+static int port_info_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ /* port info time */ -+ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; -+ m.u.port_info_get.component_handle = port->component->handle; -+ m.u.port_info_get.port_type = port->type; -+ m.u.port_info_get.index = port->index; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_info_get), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ /* return operation status */ -+ ret = -rmsg->u.port_info_get_reply.status; -+ if (ret != MMAL_MSG_STATUS_SUCCESS) -+ goto release_msg; -+ -+ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) -+ port->enabled = false; -+ else -+ port->enabled = true; -+ -+ /* copy the values out of the message */ -+ port->handle = rmsg->u.port_info_get_reply.port_handle; -+ -+ /* port type and index cached to use on port info set becuase -+ * it does not use a port handle -+ */ -+ port->type = rmsg->u.port_info_get_reply.port_type; -+ port->index = rmsg->u.port_info_get_reply.port_index; -+ -+ port->minimum_buffer.num = -+ rmsg->u.port_info_get_reply.port.buffer_num_min; -+ port->minimum_buffer.size = -+ rmsg->u.port_info_get_reply.port.buffer_size_min; -+ port->minimum_buffer.alignment = -+ rmsg->u.port_info_get_reply.port.buffer_alignment_min; -+ -+ port->recommended_buffer.alignment = -+ rmsg->u.port_info_get_reply.port.buffer_alignment_min; -+ port->recommended_buffer.num = -+ rmsg->u.port_info_get_reply.port.buffer_num_recommended; -+ -+ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; -+ port->current_buffer.size = -+ rmsg->u.port_info_get_reply.port.buffer_size; -+ -+ /* stream format */ -+ port->format.type = rmsg->u.port_info_get_reply.format.type; -+ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; -+ port->format.encoding_variant = -+ rmsg->u.port_info_get_reply.format.encoding_variant; -+ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; -+ port->format.flags = rmsg->u.port_info_get_reply.format.flags; -+ -+ /* elementry stream format */ -+ memcpy(&port->es, -+ &rmsg->u.port_info_get_reply.es, -+ sizeof(union mmal_es_specific_format)); -+ port->format.es = &port->es; -+ -+ port->format.extradata_size = -+ rmsg->u.port_info_get_reply.format.extradata_size; -+ memcpy(port->format.extradata, -+ rmsg->u.port_info_get_reply.extradata, -+ port->format.extradata_size); -+ -+ pr_debug("received port info\n"); -+ dump_port_info(port); -+ -+release_msg: -+ -+ pr_debug("%s:result:%d component:0x%x port:%d\n", -+ __func__, ret, port->component->handle, port->handle); -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* create comonent on vc */ -+static int create_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component, -+ const char *name) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ /* build component create message */ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; -+ m.u.component_create.client_component = component; -+ strncpy(m.u.component_create.name, name, -+ sizeof(m.u.component_create.name)); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_create), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_create_reply.status; -+ if (ret != MMAL_MSG_STATUS_SUCCESS) -+ goto release_msg; -+ -+ /* a valid component response received */ -+ component->handle = rmsg->u.component_create_reply.component_handle; -+ component->inputs = rmsg->u.component_create_reply.input_num; -+ component->outputs = rmsg->u.component_create_reply.output_num; -+ component->clocks = rmsg->u.component_create_reply.clock_num; -+ -+ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", -+ component->handle, -+ component->inputs, component->outputs, component->clocks); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* destroys a component on vc */ -+static int destroy_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; -+ m.u.component_destroy.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_destroy), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_destroy_reply.status; -+ -+release_msg: -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* enable a component on vc */ -+static int enable_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; -+ m.u.component_enable.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_enable), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_enable_reply.status; -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* disable a component on vc */ -+static int disable_component(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; -+ m.u.component_disable.component_handle = component->handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.component_disable), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.component_disable_reply.status; -+ -+release_msg: -+ -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* get version of mmal implementation */ -+static int get_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, u32 *minor_out) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_GET_VERSION; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.version), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != m.h.type) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ *major_out = rmsg->u.version.major; -+ *minor_out = rmsg->u.version.minor; -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* do a port action with a port as a parameter */ -+static int port_action_port(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ enum mmal_msg_port_action_type action_type) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; -+ m.u.port_action_port.component_handle = port->component->handle; -+ m.u.port_action_port.port_handle = port->handle; -+ m.u.port_action_port.action = action_type; -+ -+ port_to_mmal_msg(port, &m.u.port_action_port.port); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_action_port), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_action_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", -+ __func__, -+ ret, port->component->handle, port->handle, -+ port_action_type_names[action_type], action_type); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* do a port action with handles as parameters */ -+static int port_action_handle(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ enum mmal_msg_port_action_type action_type, -+ u32 connect_component_handle, -+ u32 connect_port_handle) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; -+ -+ m.u.port_action_handle.component_handle = port->component->handle; -+ m.u.port_action_handle.port_handle = port->handle; -+ m.u.port_action_handle.action = action_type; -+ -+ m.u.port_action_handle.connect_component_handle = -+ connect_component_handle; -+ m.u.port_action_handle.connect_port_handle = connect_port_handle; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(m.u.port_action_handle), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_action_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ -+ " connect component:0x%x connect port:%d\n", -+ __func__, -+ ret, port->component->handle, port->handle, -+ port_action_type_names[action_type], -+ action_type, connect_component_handle, connect_port_handle); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+static int port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter_id, void *value, u32 value_size) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; -+ -+ m.u.port_parameter_set.component_handle = port->component->handle; -+ m.u.port_parameter_set.port_handle = port->handle; -+ m.u.port_parameter_set.id = parameter_id; -+ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; -+ memcpy(&m.u.port_parameter_set.value, value, value_size); -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ (4 * sizeof(u32)) + value_size, -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { -+ /* got an unexpected message type in reply */ -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_parameter_set_reply.status; -+ -+ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", -+ __func__, -+ ret, port->component->handle, port->handle, parameter_id); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+static int port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter_id, void *value, u32 *value_size) -+{ -+ int ret; -+ struct mmal_msg m; -+ struct mmal_msg *rmsg; -+ VCHI_HELD_MSG_T rmsg_handle; -+ -+ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; -+ -+ m.u.port_parameter_get.component_handle = port->component->handle; -+ m.u.port_parameter_get.port_handle = port->handle; -+ m.u.port_parameter_get.id = parameter_id; -+ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; -+ -+ ret = send_synchronous_mmal_msg(instance, &m, -+ sizeof(struct -+ mmal_msg_port_parameter_get), -+ &rmsg, &rmsg_handle); -+ if (ret) -+ return ret; -+ -+ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { -+ /* got an unexpected message type in reply */ -+ pr_err("Incorrect reply type %d\n", rmsg->h.type); -+ ret = -EINVAL; -+ goto release_msg; -+ } -+ -+ ret = -rmsg->u.port_parameter_get_reply.status; -+ if (ret) { -+ /* Copy only as much as we have space for -+ * but report true size of parameter -+ */ -+ memcpy(value, &rmsg->u.port_parameter_get_reply.value, -+ *value_size); -+ *value_size = rmsg->u.port_parameter_get_reply.size; -+ } else -+ memcpy(value, &rmsg->u.port_parameter_get_reply.value, -+ rmsg->u.port_parameter_get_reply.size); -+ -+ pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, -+ ret, port->component->handle, port->handle, parameter_id); -+ -+release_msg: -+ vchi_held_msg_release(&rmsg_handle); -+ -+ return ret; -+} -+ -+/* disables a port and drains buffers from it */ -+static int port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ struct list_head *q, *buf_head; -+ unsigned long flags = 0; -+ -+ if (!port->enabled) -+ return 0; -+ -+ port->enabled = false; -+ -+ ret = port_action_port(instance, port, -+ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); -+ if (ret == 0) { -+ -+ /* drain all queued buffers on port */ -+ spin_lock_irqsave(&port->slock, flags); -+ -+ list_for_each_safe(buf_head, q, &port->buffers) { -+ struct mmal_buffer *mmalbuf; -+ mmalbuf = list_entry(buf_head, struct mmal_buffer, -+ list); -+ list_del(buf_head); -+ if (port->buffer_cb) -+ port->buffer_cb(instance, -+ port, 0, mmalbuf, 0, 0, -+ MMAL_TIME_UNKNOWN, -+ MMAL_TIME_UNKNOWN); ++ if (data_found) { ++ int count; ++ spin_lock("a_spinlock); ++ count = state->data_use_count; ++ if (count > 0) ++ state->data_use_count = ++ count - 1; ++ spin_unlock("a_spinlock); ++ if (count == state->data_quota) ++ up(&state->data_quota_event); + } + -+ spin_unlock_irqrestore(&port->slock, flags); -+ -+ ret = port_info_get(instance, port); ++ state->slot_queue_available = slot_queue_available; ++ up(&state->slot_available_event); + } -+ -+ return ret; +} + -+/* enable a port */ -+static int port_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) ++/* Called by the slot handler and application threads */ ++static VCHIQ_STATUS_T ++queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ int msgid, const VCHIQ_ELEMENT_T *elements, ++ int count, int size, int flags) +{ -+ unsigned int hdr_count; -+ struct list_head *buf_head; -+ int ret; ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; ++ VCHIQ_HEADER_T *header; ++ int type = VCHIQ_MSG_TYPE(msgid); + -+ if (port->enabled) -+ return 0; ++ unsigned int stride; + -+ /* ensure there are enough buffers queued to cover the buffer headers */ -+ if (port->buffer_cb != NULL) { -+ hdr_count = 0; -+ list_for_each(buf_head, &port->buffers) { -+ hdr_count++; ++ local = state->local; ++ ++ stride = calc_stride(size); ++ ++ WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); ++ ++ if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && ++ (mutex_lock_interruptible(&state->slot_mutex) != 0)) ++ return VCHIQ_RETRY; ++ ++ if (type == VCHIQ_MSG_DATA) { ++ int tx_end_index; ++ ++ BUG_ON(!service); ++ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); ++ ++ if (service->closing) { ++ /* The service has been closed */ ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_ERROR; + } -+ if (hdr_count < port->current_buffer.num) -+ return -ENOSPC; -+ } + -+ ret = port_action_port(instance, port, -+ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); -+ if (ret) -+ goto done; ++ service_quota = &state->service_quotas[service->localport]; + -+ port->enabled = true; ++ spin_lock("a_spinlock); + -+ if (port->buffer_cb) { -+ /* send buffer headers to videocore */ -+ hdr_count = 1; -+ list_for_each(buf_head, &port->buffers) { -+ struct mmal_buffer *mmalbuf; -+ mmalbuf = list_entry(buf_head, struct mmal_buffer, -+ list); -+ ret = buffer_from_host(instance, port, mmalbuf); -+ if (ret) -+ goto done; ++ /* Ensure this service doesn't use more than its quota of ++ ** messages or slots */ ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); + -+ hdr_count++; -+ if (hdr_count > port->current_buffer.num) ++ /* Ensure data messages don't use more than their quota of ++ ** slots */ ++ while ((tx_end_index != state->previous_data_index) && ++ (state->data_use_count == state->data_quota)) { ++ VCHIQ_STATS_INC(state, data_stalls); ++ spin_unlock("a_spinlock); ++ mutex_unlock(&state->slot_mutex); ++ ++ if (down_interruptible(&state->data_quota_event) ++ != 0) ++ return VCHIQ_RETRY; ++ ++ mutex_lock(&state->slot_mutex); ++ spin_lock("a_spinlock); ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); ++ if ((tx_end_index == state->previous_data_index) || ++ (state->data_use_count < state->data_quota)) { ++ /* Pass the signal on to other waiters */ ++ up(&state->data_quota_event); + break; -+ } -+ } -+ -+ ret = port_info_get(instance, port); -+ -+done: -+ return ret; -+} -+ -+/* ------------------------------------------------------------------ -+ * Exported API -+ *------------------------------------------------------------------*/ -+ -+int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_info_set(instance, port); -+ if (ret) -+ goto release_unlock; -+ -+ /* read what has actually been set */ -+ ret = port_info_get(instance, port); -+ -+release_unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+ -+} -+ -+int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, void *value, u32 value_size) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_parameter_set(instance, port, parameter, value, value_size); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, void *value, u32 *value_size) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ ret = port_parameter_get(instance, port, parameter, value, value_size); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* enable a port -+ * -+ * enables a port and queues buffers for satisfying callbacks if we -+ * provide a callback handler -+ */ -+int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ vchiq_mmal_buffer_cb buffer_cb) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ /* already enabled - noop */ -+ if (port->enabled) { -+ ret = 0; -+ goto unlock; -+ } -+ -+ port->buffer_cb = buffer_cb; -+ -+ ret = port_enable(instance, port); -+ -+unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (!port->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; -+ } -+ -+ ret = port_disable(instance, port); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* ports will be connected in a tunneled manner so data buffers -+ * are not handled by client. -+ */ -+int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *src, -+ struct vchiq_mmal_port *dst) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ /* disconnect ports if connected */ -+ if (src->connected != NULL) { -+ ret = port_disable(instance, src); -+ if (ret) { -+ pr_err("failed disabling src port(%d)\n", ret); -+ goto release_unlock; ++ } + } + -+ /* do not need to disable the destination port as they -+ * are connected and it is done automatically -+ */ -+ -+ ret = port_action_handle(instance, src, -+ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, -+ src->connected->component->handle, -+ src->connected->handle); -+ if (ret < 0) { -+ pr_err("failed disconnecting src port\n"); -+ goto release_unlock; ++ while ((service_quota->message_use_count == ++ service_quota->message_quota) || ++ ((tx_end_index != service_quota->previous_tx_index) && ++ (service_quota->slot_use_count == ++ service_quota->slot_quota))) { ++ spin_unlock("a_spinlock); ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: qm:%d %s,%x - quota stall " ++ "(msg %d, slot %d)", ++ state->id, service->localport, ++ msg_type_str(type), size, ++ service_quota->message_use_count, ++ service_quota->slot_use_count); ++ VCHIQ_SERVICE_STATS_INC(service, quota_stalls); ++ mutex_unlock(&state->slot_mutex); ++ if (down_interruptible(&service_quota->quota_event) ++ != 0) ++ return VCHIQ_RETRY; ++ if (service->closing) ++ return VCHIQ_ERROR; ++ if (mutex_lock_interruptible(&state->slot_mutex) != 0) ++ return VCHIQ_RETRY; ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { ++ /* The service has been closed */ ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_ERROR; ++ } ++ spin_lock("a_spinlock); ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS( ++ state->local_tx_pos + stride - 1); + } -+ src->connected->enabled = false; -+ src->connected = NULL; ++ ++ spin_unlock("a_spinlock); + } + -+ if (dst == NULL) { -+ /* do not make new connection */ -+ ret = 0; -+ pr_debug("not making new connection\n"); -+ goto release_unlock; ++ header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); ++ ++ if (!header) { ++ if (service) ++ VCHIQ_SERVICE_STATS_INC(service, slot_stalls); ++ /* In the event of a failure, return the mutex to the ++ state it was in */ ++ if (!(flags & QMFLAGS_NO_MUTEX_LOCK)) ++ mutex_unlock(&state->slot_mutex); ++ return VCHIQ_RETRY; + } + -+ /* copy src port format to dst */ -+ dst->format.encoding = src->format.encoding; -+ dst->es.video.width = src->es.video.width; -+ dst->es.video.height = src->es.video.height; -+ dst->es.video.crop.x = src->es.video.crop.x; -+ dst->es.video.crop.y = src->es.video.crop.y; -+ dst->es.video.crop.width = src->es.video.crop.width; -+ dst->es.video.crop.height = src->es.video.crop.height; -+ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; -+ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ if (type == VCHIQ_MSG_DATA) { ++ int i, pos; ++ int tx_end_index; ++ int slot_use_count; + -+ /* set new format */ -+ ret = port_info_set(instance, dst); -+ if (ret) { -+ pr_debug("setting port info failed\n"); -+ goto release_unlock; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: qm %s@%x,%x (%d->%d)", ++ state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ ++ BUG_ON(!service); ++ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); ++ ++ for (i = 0, pos = 0; i < (unsigned int)count; ++ pos += elements[i++].size) ++ if (elements[i].size) { ++ if (vchiq_copy_from_user ++ (header->data + pos, elements[i].data, ++ (size_t) elements[i].size) != ++ VCHIQ_SUCCESS) { ++ mutex_unlock(&state->slot_mutex); ++ VCHIQ_SERVICE_STATS_INC(service, ++ error_count); ++ return VCHIQ_ERROR; ++ } ++ if (i == 0) { ++ if (SRVTRACE_ENABLED(service, ++ VCHIQ_LOG_INFO)) ++ vchiq_log_dump_mem("Sent", 0, ++ header->data + pos, ++ min(64u, ++ elements[0].size)); ++ } ++ } ++ ++ spin_lock("a_spinlock); ++ service_quota->message_use_count++; ++ ++ tx_end_index = ++ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1); ++ ++ /* If this transmission can't fit in the last slot used by any ++ ** service, the data_use_count must be increased. */ ++ if (tx_end_index != state->previous_data_index) { ++ state->previous_data_index = tx_end_index; ++ state->data_use_count++; ++ } ++ ++ /* If this isn't the same slot last used by this service, ++ ** the service's slot_use_count must be increased. */ ++ if (tx_end_index != service_quota->previous_tx_index) { ++ service_quota->previous_tx_index = tx_end_index; ++ slot_use_count = ++service_quota->slot_use_count; ++ } else { ++ slot_use_count = 0; ++ } ++ ++ spin_unlock("a_spinlock); ++ ++ if (slot_use_count) ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: qm:%d %s,%x - slot_use->%d (hdr %p)", ++ state->id, service->localport, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, ++ slot_use_count, header); ++ ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); ++ } else { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: qm %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ if (size != 0) { ++ WARN_ON(!((count == 1) && (size == elements[0].size))); ++ memcpy(header->data, elements[0].data, ++ elements[0].size); ++ } ++ VCHIQ_STATS_INC(state, ctrl_tx_count); + } + -+ /* read what has actually been set */ -+ ret = port_info_get(instance, dst); -+ if (ret) { -+ pr_debug("read back port info failed\n"); -+ goto release_unlock; ++ header->msgid = msgid; ++ header->size = size; ++ ++ { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ VCHIQ_MSG_TYPE(msgid), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid), ++ size); + } + -+ /* connect two ports together */ -+ ret = port_action_handle(instance, src, -+ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, -+ dst->component->handle, dst->handle); -+ if (ret < 0) { -+ pr_debug("connecting port %d:%d to %d:%d failed\n", -+ src->component->handle, src->handle, -+ dst->component->handle, dst->handle); -+ goto release_unlock; -+ } -+ src->connected = dst; ++ /* Make sure the new header is visible to the peer. */ ++ wmb(); + -+release_unlock: ++ /* Make the new tx_pos visible to the peer. */ ++ local->tx_pos = state->local_tx_pos; ++ wmb(); + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (service && (type == VCHIQ_MSG_CLOSE)) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); + -+ return ret; ++ if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) ++ mutex_unlock(&state->slot_mutex); ++ ++ remote_event_signal(&state->remote->trigger); ++ ++ return VCHIQ_SUCCESS; +} + -+int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ struct mmal_buffer *buffer) ++/* Called by the slot handler and application threads */ ++static VCHIQ_STATUS_T ++queue_message_sync(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ int msgid, const VCHIQ_ELEMENT_T *elements, ++ int count, int size, int is_blocking) +{ -+ unsigned long flags = 0; ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_HEADER_T *header; + -+ spin_lock_irqsave(&port->slock, flags); -+ list_add_tail(&buffer->list, &port->buffers); -+ spin_unlock_irqrestore(&port->slock, flags); ++ local = state->local; + -+ /* the port previously underflowed because it was missing a -+ * mmal_buffer which has just been added, submit that buffer -+ * to the mmal service. -+ */ -+ if (port->buffer_underflow) { -+ port_buffer_from_host(instance, port); -+ port->buffer_underflow--; ++ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) && ++ (mutex_lock_interruptible(&state->sync_mutex) != 0)) ++ return VCHIQ_RETRY; ++ ++ remote_event_wait(&local->sync_release); ++ ++ rmb(); ++ ++ header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ local->slot_sync); ++ ++ { ++ int oldmsgid = header->msgid; ++ if (oldmsgid != VCHIQ_MSGID_PADDING) ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: qms - msgid %x, not PADDING", ++ state->id, oldmsgid); + } + -+ return 0; ++ if (service) { ++ int i, pos; ++ ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: qms %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ ++ for (i = 0, pos = 0; i < (unsigned int)count; ++ pos += elements[i++].size) ++ if (elements[i].size) { ++ if (vchiq_copy_from_user ++ (header->data + pos, elements[i].data, ++ (size_t) elements[i].size) != ++ VCHIQ_SUCCESS) { ++ mutex_unlock(&state->sync_mutex); ++ VCHIQ_SERVICE_STATS_INC(service, ++ error_count); ++ return VCHIQ_ERROR; ++ } ++ if (i == 0) { ++ if (vchiq_sync_log_level >= ++ VCHIQ_LOG_TRACE) ++ vchiq_log_dump_mem("Sent Sync", ++ 0, header->data + pos, ++ min(64u, ++ elements[0].size)); ++ } ++ } ++ ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); ++ } else { ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: qms %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ if (size != 0) { ++ WARN_ON(!((count == 1) && (size == elements[0].size))); ++ memcpy(header->data, elements[0].data, ++ elements[0].size); ++ } ++ VCHIQ_STATS_INC(state, ctrl_tx_count); ++ } ++ ++ header->size = size; ++ header->msgid = msgid; ++ ++ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ ++ vchiq_log_trace(vchiq_sync_log_level, ++ "Sent Sync Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ VCHIQ_MSG_TYPE(msgid), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid), ++ size); ++ } ++ ++ /* Make sure the new header is visible to the peer. */ ++ wmb(); ++ ++ remote_event_signal(&state->remote->sync_trigger); ++ ++ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) ++ mutex_unlock(&state->sync_mutex); ++ ++ return VCHIQ_SUCCESS; +} + -+/* Initialise a mmal component and its ports -+ * -+ */ -+int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, -+ const char *name, -+ struct vchiq_mmal_component **component_out) ++static inline void ++claim_slot(VCHIQ_SLOT_INFO_T *slot) +{ -+ int ret; -+ int idx; /* port index */ -+ struct vchiq_mmal_component *component; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { -+ ret = -EINVAL; /* todo is this correct error? */ -+ goto unlock; -+ } -+ -+ component = &instance->component[instance->component_idx]; -+ -+ ret = create_component(instance, component, name); -+ if (ret < 0) -+ goto unlock; -+ -+ /* ports info needs gathering */ -+ component->control.type = MMAL_PORT_TYPE_CONTROL; -+ component->control.index = 0; -+ component->control.component = component; -+ spin_lock_init(&component->control.slock); -+ INIT_LIST_HEAD(&component->control.buffers); -+ ret = port_info_get(instance, &component->control); -+ if (ret < 0) -+ goto release_component; -+ -+ for (idx = 0; idx < component->inputs; idx++) { -+ component->input[idx].type = MMAL_PORT_TYPE_INPUT; -+ component->input[idx].index = idx; -+ component->input[idx].component = component; -+ spin_lock_init(&component->input[idx].slock); -+ INIT_LIST_HEAD(&component->input[idx].buffers); -+ ret = port_info_get(instance, &component->input[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ for (idx = 0; idx < component->outputs; idx++) { -+ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; -+ component->output[idx].index = idx; -+ component->output[idx].component = component; -+ spin_lock_init(&component->output[idx].slock); -+ INIT_LIST_HEAD(&component->output[idx].buffers); -+ ret = port_info_get(instance, &component->output[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ for (idx = 0; idx < component->clocks; idx++) { -+ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; -+ component->clock[idx].index = idx; -+ component->clock[idx].component = component; -+ spin_lock_init(&component->clock[idx].slock); -+ INIT_LIST_HEAD(&component->clock[idx].buffers); -+ ret = port_info_get(instance, &component->clock[idx]); -+ if (ret < 0) -+ goto release_component; -+ } -+ -+ instance->component_idx++; -+ -+ *component_out = component; -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return 0; -+ -+release_component: -+ destroy_component(instance, component); -+unlock: -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; ++ slot->use_count++; +} + -+/* -+ * cause a mmal component to be destroyed -+ */ -+int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) ++static void ++release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info, ++ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_T *service) +{ -+ int ret; ++ int release_count; + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; ++ mutex_lock(&state->recycle_mutex); + -+ if (component->enabled) -+ ret = disable_component(instance, component); ++ if (header) { ++ int msgid = header->msgid; ++ if (((msgid & VCHIQ_MSGID_CLAIMED) == 0) || ++ (service && service->closing)) { ++ mutex_unlock(&state->recycle_mutex); ++ return; ++ } + -+ ret = destroy_component(instance, component); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; -+} -+ -+/* -+ * cause a mmal component to be enabled -+ */ -+int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (component->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; ++ /* Rewrite the message header to prevent a double ++ ** release */ ++ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; + } + -+ ret = enable_component(instance, component); -+ if (ret == 0) -+ component->enabled = true; ++ release_count = slot_info->release_count; ++ slot_info->release_count = ++release_count; + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (release_count == slot_info->use_count) { ++ int slot_queue_recycle; ++ /* Add to the freed queue */ + -+ return ret; -+} ++ /* A read barrier is necessary here to prevent speculative ++ ** fetches of remote->slot_queue_recycle from overtaking the ++ ** mutex. */ ++ rmb(); + -+/* -+ * cause a mmal component to be enabled -+ */ -+int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component) -+{ -+ int ret; ++ slot_queue_recycle = state->remote->slot_queue_recycle; ++ state->remote->slot_queue[slot_queue_recycle & ++ VCHIQ_SLOT_QUEUE_MASK] = ++ SLOT_INDEX_FROM_INFO(state, slot_info); ++ state->remote->slot_queue_recycle = slot_queue_recycle + 1; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: release_slot %d - recycle->%x", ++ state->id, SLOT_INDEX_FROM_INFO(state, slot_info), ++ state->remote->slot_queue_recycle); + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ if (!component->enabled) { -+ mutex_unlock(&instance->vchiq_mutex); -+ return 0; ++ /* A write barrier is necessary, but remote_event_signal ++ ** contains one. */ ++ remote_event_signal(&state->remote->recycle); + } + -+ ret = disable_component(instance, component); -+ if (ret == 0) -+ component->enabled = false; -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ return ret; ++ mutex_unlock(&state->recycle_mutex); +} + -+int vchiq_mmal_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, u32 *minor_out) ++/* Called by the slot handler - don't hold the bulk mutex */ ++static VCHIQ_STATUS_T ++notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue, ++ int retry_poll) +{ -+ int ret; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: nb:%d %cx - p=%x rn=%x r=%x", ++ service->state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->process, queue->remote_notify, queue->remove); + -+ ret = get_version(instance, major_out, minor_out); ++ if (service->state->is_master) { ++ while (queue->remote_notify != queue->process) { ++ VCHIQ_BULK_T *bulk = ++ &queue->bulks[BULK_INDEX(queue->remote_notify)]; ++ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE; ++ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, ++ service->remoteport); ++ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 }; ++ /* Only reply to non-dummy bulk requests */ ++ if (bulk->remote_data) { ++ status = queue_message(service->state, NULL, ++ msgid, &element, 1, 4, 0); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ queue->remote_notify++; ++ } ++ } else { ++ queue->remote_notify = queue->process; ++ } + -+ mutex_unlock(&instance->vchiq_mutex); ++ if (status == VCHIQ_SUCCESS) { ++ while (queue->remove != queue->remote_notify) { ++ VCHIQ_BULK_T *bulk = ++ &queue->bulks[BULK_INDEX(queue->remove)]; + -+ return ret; -+} ++ /* Only generate callbacks for non-dummy bulk ++ ** requests, and non-terminated services */ ++ if (bulk->data && service->instance) { ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) { ++ if (bulk->dir == VCHIQ_BULK_TRANSMIT) { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ++ bulk_tx_bytes, ++ bulk->actual); ++ } else { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ++ bulk_rx_bytes, ++ bulk->actual); ++ } ++ } else { ++ VCHIQ_SERVICE_STATS_INC(service, ++ bulk_aborted_count); ++ } ++ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) { ++ struct bulk_waiter *waiter; ++ spin_lock(&bulk_waiter_spinlock); ++ waiter = bulk->userdata; ++ if (waiter) { ++ waiter->actual = bulk->actual; ++ up(&waiter->event); ++ } ++ spin_unlock(&bulk_waiter_spinlock); ++ } else if (bulk->mode == ++ VCHIQ_BULK_MODE_CALLBACK) { ++ VCHIQ_REASON_T reason = (bulk->dir == ++ VCHIQ_BULK_TRANSMIT) ? ++ ((bulk->actual == ++ VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_TRANSMIT_ABORTED : ++ VCHIQ_BULK_TRANSMIT_DONE) : ++ ((bulk->actual == ++ VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_RECEIVE_ABORTED : ++ VCHIQ_BULK_RECEIVE_DONE); ++ status = make_service_callback(service, ++ reason, NULL, bulk->userdata); ++ if (status == VCHIQ_RETRY) ++ break; ++ } ++ } + -+int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) -+{ -+ int status = 0; ++ queue->remove++; ++ up(&service->bulk_remove_event); ++ } ++ if (!retry_poll) ++ status = VCHIQ_SUCCESS; ++ } + -+ if (instance == NULL) -+ return -EINVAL; -+ -+ if (mutex_lock_interruptible(&instance->vchiq_mutex)) -+ return -EINTR; -+ -+ vchi_service_use(instance->handle); -+ -+ status = vchi_service_close(instance->handle); -+ if (status != 0) -+ pr_err("mmal-vchiq: VCHIQ close failed"); -+ -+ mutex_unlock(&instance->vchiq_mutex); -+ -+ vfree(instance->bulk_scratch); -+ -+ kfree(instance); ++ if (status == VCHIQ_RETRY) ++ request_poll(service->state, service, ++ (queue == &service->bulk_tx) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); + + return status; +} + -+int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++/* Called by the slot handler thread */ ++static void ++poll_services(VCHIQ_STATE_T *state) +{ -+ int status; -+ struct vchiq_mmal_instance *instance; -+ static VCHI_CONNECTION_T *vchi_connection; -+ static VCHI_INSTANCE_T vchi_instance; -+ SERVICE_CREATION_T params = { -+ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), -+ VC_MMAL_SERVER_NAME, -+ vchi_connection, -+ 0, /* rx fifo size (unused) */ -+ 0, /* tx fifo size (unused) */ -+ service_callback, -+ NULL, /* service callback parameter */ -+ 1, /* unaligned bulk receives */ -+ 1, /* unaligned bulk transmits */ -+ 0 /* want crc check on bulk transfers */ -+ }; ++ int group, i; + -+ /* compile time checks to ensure structure size as they are -+ * directly (de)serialised from memory. -+ */ ++ for (group = 0; group < BITSET_SIZE(state->unused_service); group++) { ++ uint32_t flags; ++ flags = atomic_xchg(&state->poll_services[group], 0); ++ for (i = 0; flags; i++) { ++ if (flags & (1 << i)) { ++ VCHIQ_SERVICE_T *service = ++ find_service_by_port(state, ++ (group<<5) + i); ++ uint32_t service_flags; ++ flags &= ~(1 << i); ++ if (!service) ++ continue; ++ service_flags = ++ atomic_xchg(&service->poll_flags, 0); ++ if (service_flags & ++ (1 << VCHIQ_POLL_REMOVE)) { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: ps - remove %d<->%d", ++ state->id, service->localport, ++ service->remoteport); + -+ /* ensure the header structure has packed to the correct size */ -+ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ /* Make it look like a client, because ++ it must be removed and not left in ++ the LISTENING state. */ ++ service->public_fourcc = ++ VCHIQ_FOURCC_INVALID; + -+ /* ensure message structure does not exceed maximum length */ -+ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); -+ -+ /* mmal port struct is correct size */ -+ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); -+ -+ /* create a vchi instance */ -+ status = vchi_initialise(&vchi_instance); -+ if (status) { -+ pr_err("Failed to initialise VCHI instance (status=%d)\n", -+ status); -+ return -EIO; -+ } -+ -+ status = vchi_connect(NULL, 0, vchi_instance); -+ if (status) { -+ pr_err("Failed to connect VCHI instance (status=%d)\n", status); -+ return -EIO; -+ } -+ -+ instance = kmalloc(sizeof(*instance), GFP_KERNEL); -+ memset(instance, 0, sizeof(*instance)); -+ -+ mutex_init(&instance->vchiq_mutex); -+ mutex_init(&instance->bulk_mutex); -+ -+ instance->bulk_scratch = vmalloc(PAGE_SIZE); -+ -+ params.callback_param = instance; -+ -+ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); -+ if (status) { -+ pr_err("Failed to open VCHI service connection (status=%d)\n", -+ status); -+ goto err_close_services; -+ } -+ -+ vchi_service_release(instance->handle); -+ -+ *out_instance = instance; -+ -+ return 0; -+ -+err_close_services: -+ -+ vchi_service_close(instance->handle); -+ vfree(instance->bulk_scratch); -+ kfree(instance); -+ return -ENODEV; -+} -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h -new file mode 100644 -index 0000000..9d1d11e ---- /dev/null -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.h -@@ -0,0 +1,178 @@ -+/* -+ * Broadcom BM2835 V4L2 driver -+ * -+ * Copyright © 2013 Raspberry Pi (Trading) Ltd. -+ * -+ * This file is subject to the terms and conditions of the GNU General Public -+ * License. See the file COPYING in the main directory of this archive -+ * for more details. -+ * -+ * Authors: Vincent Sanders -+ * Dave Stevenson -+ * Simon Mellor -+ * Luke Diamand -+ * -+ * MMAL interface to VCHIQ message passing -+ */ -+ -+#ifndef MMAL_VCHIQ_H -+#define MMAL_VCHIQ_H -+ -+#include "mmal-msg-format.h" -+ -+#define MAX_PORT_COUNT 4 -+ -+/* Maximum size of the format extradata. */ -+#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 -+ -+struct vchiq_mmal_instance; -+ -+enum vchiq_mmal_es_type { -+ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ -+ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ -+ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ -+ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ -+ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ -+}; -+ -+/* rectangle, used lots so it gets its own struct */ -+struct vchiq_mmal_rect { -+ s32 x; -+ s32 y; -+ s32 width; -+ s32 height; -+}; -+ -+struct vchiq_mmal_port_buffer { -+ unsigned int num; /* number of buffers */ -+ u32 size; /* size of buffers */ -+ u32 alignment; /* alignment of buffers */ -+}; -+ -+struct vchiq_mmal_port; -+ -+typedef void (*vchiq_mmal_buffer_cb)( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ int status, struct mmal_buffer *buffer, -+ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); -+ -+struct vchiq_mmal_port { -+ bool enabled; -+ u32 handle; -+ u32 type; /* port type, cached to use on port info set */ -+ u32 index; /* port index, cached to use on port info set */ -+ -+ /* component port belongs to, allows simple deref */ -+ struct vchiq_mmal_component *component; -+ -+ struct vchiq_mmal_port *connected; /* port conencted to */ -+ -+ /* buffer info */ -+ struct vchiq_mmal_port_buffer minimum_buffer; -+ struct vchiq_mmal_port_buffer recommended_buffer; -+ struct vchiq_mmal_port_buffer current_buffer; -+ -+ /* stream format */ -+ struct mmal_es_format format; -+ /* elementry stream format */ -+ union mmal_es_specific_format es; -+ -+ /* data buffers to fill */ -+ struct list_head buffers; -+ /* lock to serialise adding and removing buffers from list */ -+ spinlock_t slock; -+ /* count of how many buffer header refils have failed because -+ * there was no buffer to satisfy them -+ */ -+ int buffer_underflow; -+ /* callback on buffer completion */ -+ vchiq_mmal_buffer_cb buffer_cb; -+ /* callback context */ -+ void *cb_ctx; -+}; -+ -+struct vchiq_mmal_component { -+ bool enabled; -+ u32 handle; /* VideoCore handle for component */ -+ u32 inputs; /* Number of input ports */ -+ u32 outputs; /* Number of output ports */ -+ u32 clocks; /* Number of clock ports */ -+ struct vchiq_mmal_port control; /* control port */ -+ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ -+ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ -+ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ -+}; -+ -+ -+int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); -+int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); -+ -+/* Initialise a mmal component and its ports -+* -+*/ -+int vchiq_mmal_component_init( -+ struct vchiq_mmal_instance *instance, -+ const char *name, -+ struct vchiq_mmal_component **component_out); -+ -+int vchiq_mmal_component_finalise( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+int vchiq_mmal_component_enable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+int vchiq_mmal_component_disable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_component *component); -+ -+ -+ -+/* enable a mmal port -+ * -+ * enables a port and if a buffer callback provided enque buffer -+ * headers as apropriate for the port. -+ */ -+int vchiq_mmal_port_enable( -+ struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ vchiq_mmal_buffer_cb buffer_cb); -+ -+/* disable a port -+ * -+ * disable a port will dequeue any pending buffers -+ */ -+int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port); -+ -+ -+int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, -+ void *value, -+ u32 value_size); -+ -+int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ u32 parameter, -+ void *value, -+ u32 *value_size); -+ -+int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port); -+ -+int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *src, -+ struct vchiq_mmal_port *dst); -+ -+int vchiq_mmal_version(struct vchiq_mmal_instance *instance, -+ u32 *major_out, -+ u32 *minor_out); -+ -+int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, -+ struct vchiq_mmal_port *port, -+ struct mmal_buffer *buf); -+ -+#endif /* MMAL_VCHIQ_H */ - -From 28e03357f47e284d1670f6b9aac95557d60ecb75 Mon Sep 17 00:00:00 2001 -From: Dave Stevenson -Date: Mon, 9 Dec 2013 10:58:01 +0000 -Subject: [PATCH 029/172] V4L2: Fixes from 6by9 - -V4L2: Fix EV values. Add manual shutter speed control - -V4L2 EV values should be in units of 1/1000. Corrected. -Add support for V4L2_CID_EXPOSURE_ABSOLUTE which should -give manual shutter control. Requires manual exposure mode -to be selected first. - -Signed-off-by: Dave Stevenson - -V4L2: Correct JPEG Q-factor range - -Should be 1-100, not 0-100 - -Signed-off-by: Dave Stevenson - -V4L2: Fix issue of driver jamming if STREAMON failed. - -Fix issue where the driver was left in a partially enabled -state if STREAMON failed, and would then reject many IOCTLs -as it thought it was streaming. - -Signed-off-by: Dave Stevenson - -V4L2: Fix ISO controls. - -Driver was passing the index to the GPU, and not the desired -ISO value. - -Signed-off-by: Dave Stevenson - -V4L2: Add flicker avoidance controls - -Add support for V4L2_CID_POWER_LINE_FREQUENCY to set flicker -avoidance frequencies. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for frame rate control. - -Add support for frame rate (or time per frame as V4L2 -inverts it) control via s_parm. - -Signed-off-by: Dave Stevenson - -V4L2: Improve G_FBUF handling so we pass conformance - -Return some sane numbers for get framebuffer so that -we pass conformance. - -Signed-off-by: Dave Stevenson - -V4L2: Fix information advertised through g_vidfmt - -Width and height were being stored based on incorrect -values. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for inline H264 headers - -Add support for V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER -to control H264 inline headers. -Requires firmware fix to work correctly, otherwise format -has to be set to H264 before this parameter is set. - -Signed-off-by: Dave Stevenson - -V4L2: Fix JPEG timestamp issue - -JPEG images were coming through from the GPU with timestamp -of 0. Detect this and give current system time instead -of some invalid value. - -Signed-off-by: Dave Stevenson - -V4L2: Fix issue when switching down JPEG resolution. - -JPEG buffer size calculation is based on input resolution. -Input resolution was being configured after output port -format. Caused failures if switching from one JPEG resolution -to a smaller one. - -Signed-off-by: Dave Stevenson - -V4L2: Enable MJPEG encoding - -Requires GPU firmware update to support MJPEG encoder. - -Signed-off-by: Dave Stevenson - -V4L2: Correct flag settings for compressed formats - -Set flags field correctly on enum_fmt_vid_cap for compressed -image formats. - -Signed-off-by: Dave Stevenson - -V4L2: H264 profile & level ctrls, FPS control and auto exp pri - -Several control handling updates. -H264 profile and level controls. -Timeperframe/FPS reworked to add V4L2_CID_EXPOSURE_AUTO_PRIORITY to -select whether AE is allowed to override the framerate specified. - -Signed-off-by: Dave Stevenson - -V4L2: Correct BGR24 to RGB24 in format table - -Signed-off-by: Dave Stevenson - -V4L2: Add additional pixel formats. Correct colourspace - -Adds the other flavours of YUYV, and NV12. -Corrects the overlay advertised colourspace. - -Signed-off-by: Dave Stevenson - -V4L2: Drop logging msg from info to debug - -Signed-off-by: Dave Stevenson - -V4L2: Initial pass at scene modes. - -Only supports exposure mode and metering modes. - -Signed-off-by: Dave Stevenson - -V4L2: Add manual white balance control. - -Adds support for V4L2_CID_RED_BALANCE and -V4L2_CID_BLUE_BALANCE. Only has an effect if -V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE has -V4L2_WHITE_BALANCE_MANUAL selected. - -Signed-off-by: Dave Stevenson - -config: Enable V4L / MMAL driver - -V4L2: Increase the MMAL timeout to 3sec - -MJPEG codec flush is now taking longer and results -in a kernel panic if the driver has stopped waiting for -the result when it finally completes. -Increase the timeout value from 1 to 3secs. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for setting H264_I_PERIOD - -Adds support for the parameter V4L2_CID_MPEG_VIDEO_H264_I_PERIOD -to set the frequency with which I frames are produced. - -Signed-off-by: Dave Stevenson - -V4L2: Enable GPU function for removing padding from images. - -GPU can now support arbitrary strides, although may require -additional processing to achieve it. Enable this feature -so that the images delivered are the size requested. - -Signed-off-by: Dave Stevenson - -V4L2: Add support for V4L2_PIX_FMT_BGR32 - -Signed-off-by: Dave Stevenson - -V4L2: Set the colourspace to avoid odd YUV-RGB conversions - -Removes the amiguity from the conversion routines and stops -them dropping back to the SD vs HD choice of coeffs. - -Signed-off-by: Dave Stevenson - -V4L2: Make video/still threshold a run-time param - -Move the define for at what resolution the driver -switches from a video mode capture to a stills mode -capture to module parameters. - -Signed-off-by: Dave Stevenson - -V4L2: Fix incorrect pool sizing - -Signed-off-by: Dave Stevenson - -V4L2: Add option to disable enum_framesizes. - -Gstreamer's handling of a driver that advertises -V4L2_FRMSIZE_TYPE_STEPWISE to define the supported -resolutions is broken. See bug -https://bugzilla.gnome.org/show_bug.cgi?id=726521 - -Optional parameter of gst_v4l2src_is_broken added. -If non-zero, the driver claims not to support that -ioctl, and gstreamer should be happy again (it -guesses a set of defaults for itself). - -Signed-off-by: Dave Stevenson - -V4L2: Add support for more image formats - -Adds YVU420 (YV12), YVU420SP (NV21), and BGR888. - -Signed-off-by: Dave Stevenson - -V4L2: Extend range for V4L2_CID_MPEG_VIDEO_H264_I_PERIOD - -Request to extend the range from the fairly arbitrary -1000 frames (33 seconds at 30fps). Extend out to the -max range supported (int32 value). -Also allow 0, which is handled by the codec as only -send an I-frame on the first frame and never again. -There may be an exception if it detects a significant -scene change, but there's no easy way around that. - -Signed-off-by: Dave Stevenson - -bcm2835-camera: stop_streaming now has a void return ---- - arch/arm/configs/bcmrpi_defconfig | 3 + - drivers/media/platform/bcm2835/bcm2835-camera.c | 578 ++++++++++++++---- - drivers/media/platform/bcm2835/bcm2835-camera.h | 23 +- - drivers/media/platform/bcm2835/controls.c | 723 +++++++++++++++++++++-- - drivers/media/platform/bcm2835/mmal-common.h | 1 + - drivers/media/platform/bcm2835/mmal-encodings.h | 34 ++ - drivers/media/platform/bcm2835/mmal-parameters.h | 121 +++- - drivers/media/platform/bcm2835/mmal-vchiq.c | 4 +- - 8 files changed, 1300 insertions(+), 187 deletions(-) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index ef20985..63a6546 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -724,6 +724,9 @@ CONFIG_VIDEO_EM28XX=m - CONFIG_VIDEO_EM28XX_V4L2=m - CONFIG_VIDEO_EM28XX_ALSA=m - CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_V4L_PLATFORM_DRIVERS=y -+CONFIG_VIDEO_BCM2835=y -+CONFIG_VIDEO_BCM2835_MMAL=m - CONFIG_RADIO_SI470X=y - CONFIG_USB_SI470X=m - CONFIG_I2C_SI470X=m -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -index 1c9d9d5..e5a0010 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.c -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -36,7 +36,8 @@ - - #define BM2835_MMAL_VERSION "0.0.2" - #define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" -- -+#define MIN_WIDTH 16 -+#define MIN_HEIGHT 16 - #define MAX_WIDTH 2592 - #define MAX_HEIGHT 1944 - #define MIN_BUFFER_SIZE (80*1024) -@@ -53,13 +54,43 @@ int bcm2835_v4l2_debug; - module_param_named(debug, bcm2835_v4l2_debug, int, 0644); - MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); - -+int max_video_width = MAX_VIDEO_MODE_WIDTH; -+int max_video_height = MAX_VIDEO_MODE_HEIGHT; -+module_param(max_video_width, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max_video_width, "Threshold for video mode"); -+module_param(max_video_height, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max_video_height, "Threshold for video mode"); -+ -+/* Gstreamer bug https://bugzilla.gnome.org/show_bug.cgi?id=726521 -+ * v4l2src does bad (and actually wrong) things when the vidioc_enum_framesizes -+ * function says type V4L2_FRMSIZE_TYPE_STEPWISE, which we do by default. -+ * It's happier if we just don't say anything at all, when it then -+ * sets up a load of defaults that it thinks might work. -+ * If gst_v4l2src_is_broken is non-zero, then we remove the function from -+ * our function table list (actually switch to an alternate set, but same -+ * result). -+ */ -+int gst_v4l2src_is_broken = 0; -+module_param(gst_v4l2src_is_broken, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(gst_v4l2src_is_broken, "If non-zero, enable workaround for Gstreamer"); -+ - static struct bm2835_mmal_dev *gdev; /* global device data */ - -+#define FPS_MIN 1 -+#define FPS_MAX 90 -+ -+/* timeperframe: min/max and default */ -+static const struct v4l2_fract -+ tpf_min = {.numerator = 1, .denominator = FPS_MAX}, -+ tpf_max = {.numerator = 1, .denominator = FPS_MIN}, -+ tpf_default = {.numerator = 1000, .denominator = 30000}; -+ - /* video formats */ - static struct mmal_fmt formats[] = { - { - .name = "4:2:0, packed YUV", - .fourcc = V4L2_PIX_FMT_YUV420, -+ .flags = 0, - .mmal = MMAL_ENCODING_I420, - .depth = 12, - .mmal_component = MMAL_COMPONENT_CAMERA, -@@ -67,13 +98,15 @@ static struct mmal_fmt formats[] = { - { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, -+ .flags = 0, - .mmal = MMAL_ENCODING_YUYV, - .depth = 16, - .mmal_component = MMAL_COMPONENT_CAMERA, - }, - { -- .name = "RGB24 (BE)", -- .fourcc = V4L2_PIX_FMT_BGR24, -+ .name = "RGB24 (LE)", -+ .fourcc = V4L2_PIX_FMT_RGB24, -+ .flags = 0, - .mmal = MMAL_ENCODING_BGR24, - .depth = 24, - .mmal_component = MMAL_COMPONENT_CAMERA, -@@ -81,6 +114,7 @@ static struct mmal_fmt formats[] = { - { - .name = "JPEG", - .fourcc = V4L2_PIX_FMT_JPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, - .mmal = MMAL_ENCODING_JPEG, - .depth = 8, - .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, -@@ -88,10 +122,83 @@ static struct mmal_fmt formats[] = { - { - .name = "H264", - .fourcc = V4L2_PIX_FMT_H264, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, - .mmal = MMAL_ENCODING_H264, - .depth = 8, - .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -- } -+ }, -+ { -+ .name = "MJPEG", -+ .fourcc = V4L2_PIX_FMT_MJPEG, -+ .flags = V4L2_FMT_FLAG_COMPRESSED, -+ .mmal = MMAL_ENCODING_MJPEG, -+ .depth = 8, -+ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, -+ }, -+ { -+ .name = "4:2:2, packed, YVYU", -+ .fourcc = V4L2_PIX_FMT_YVYU, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YVYU, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, VYUY", -+ .fourcc = V4L2_PIX_FMT_VYUY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_VYUY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:2, packed, UYVY", -+ .fourcc = V4L2_PIX_FMT_UYVY, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_UYVY, -+ .depth = 16, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed, NV12", -+ .fourcc = V4L2_PIX_FMT_NV12, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB24 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR24, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_RGB24, -+ .depth = 24, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed YVU", -+ .fourcc = V4L2_PIX_FMT_YVU420, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_YV12, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "4:2:0, packed, NV21", -+ .fourcc = V4L2_PIX_FMT_NV21, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_NV21, -+ .depth = 12, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, -+ { -+ .name = "RGB32 (BE)", -+ .fourcc = V4L2_PIX_FMT_BGR32, -+ .flags = 0, -+ .mmal = MMAL_ENCODING_BGRA, -+ .depth = 32, -+ .mmal_component = MMAL_COMPONENT_CAMERA, -+ }, - }; - - static struct mmal_fmt *get_format(struct v4l2_format *f) -@@ -229,7 +336,8 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, - } - } else { - if (dev->capture.frame_count) { -- if (dev->capture.vc_start_timestamp != -1) { -+ if (dev->capture.vc_start_timestamp != -1 && -+ pts != 0) { - s64 runtime_us = pts - - dev->capture.vc_start_timestamp; - u32 div = 0; -@@ -250,7 +358,7 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, - USEC_PER_SEC; - } - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "Convert start time %d.%06d and %llu" -+ "Convert start time %d.%06d and %llu " - "with offset %llu to %d.%06d\n", - (int)dev->capture.kernel_start_ts. - tv_sec, -@@ -425,7 +533,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) - vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); - if (ret) { - v4l2_err(&dev->v4l2_dev, -- "Failed to enable capture port - error %d\n", ret); -+ "Failed to enable capture port - error %d. " -+ "Disabling camera port again\n", ret); -+ -+ vchiq_mmal_port_disable(dev->instance, -+ dev->capture.camera_port); -+ if (disable_camera(dev) < 0) { -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); -+ return -EINVAL; ++ if (vchiq_close_service_internal( ++ service, 0/*!close_recvd*/) != ++ VCHIQ_SUCCESS) ++ request_poll(state, service, ++ VCHIQ_POLL_REMOVE); ++ } else if (service_flags & ++ (1 << VCHIQ_POLL_TERMINATE)) { ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: ps - terminate %d<->%d", ++ state->id, service->localport, ++ service->remoteport); ++ if (vchiq_close_service_internal( ++ service, 0/*!close_recvd*/) != ++ VCHIQ_SUCCESS) ++ request_poll(state, service, ++ VCHIQ_POLL_TERMINATE); ++ } ++ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY)) ++ notify_bulks(service, ++ &service->bulk_tx, ++ 1/*retry_poll*/); ++ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY)) ++ notify_bulks(service, ++ &service->bulk_rx, ++ 1/*retry_poll*/); ++ unlock_service(service); ++ } + } - return -1; - } - -@@ -439,7 +555,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) - } - - /* abort streaming and wait for last buffer */ --static int stop_streaming(struct vb2_queue *vq) -+static void stop_streaming(struct vb2_queue *vq) - { - int ret; - struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); -@@ -451,8 +567,11 @@ static int stop_streaming(struct vb2_queue *vq) - dev->capture.frame_count = 0; - - /* ensure a format has actually been set */ -- if (dev->capture.port == NULL) -- return -EINVAL; -+ if (dev->capture.port == NULL) { -+ v4l2_err(&dev->v4l2_dev, -+ "no capture port - stream not started?\n"); ++ } ++} ++ ++/* Called by the slot handler or application threads, holding the bulk mutex. */ ++static int ++resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int resolved = 0; ++ int rc; ++ ++ while ((queue->process != queue->local_insert) && ++ (queue->process != queue->remote_insert)) { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: rb:%d %cx - li=%x ri=%x p=%x", ++ state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, ++ queue->process); ++ ++ WARN_ON(!((int)(queue->local_insert - queue->process) > 0)); ++ WARN_ON(!((int)(queue->remote_insert - queue->process) > 0)); ++ ++ rc = mutex_lock_interruptible(&state->bulk_transfer_mutex); ++ if (rc != 0) ++ break; ++ ++ vchiq_transfer_bulk(bulk); ++ mutex_unlock(&state->bulk_transfer_mutex); ++ ++ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { ++ const char *header = (queue == &service->bulk_tx) ? ++ "Send Bulk to" : "Recv Bulk from"; ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data); ++ else ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d," ++ " rx len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS( ++ service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data); ++ } ++ ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ resolved++; ++ } ++ return resolved; ++} ++ ++/* Called with the bulk_mutex held */ ++static void ++abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ int is_tx = (queue == &service->bulk_tx); ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: aob:%d %cx - li=%x ri=%x p=%x", ++ service->state->id, service->localport, is_tx ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++ WARN_ON(!((int)(queue->local_insert - queue->process) >= 0)); ++ WARN_ON(!((int)(queue->remote_insert - queue->process) >= 0)); ++ ++ while ((queue->process != queue->local_insert) || ++ (queue->process != queue->remote_insert)) { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ if (queue->process == queue->remote_insert) { ++ /* fabricate a matching dummy bulk */ ++ bulk->remote_data = NULL; ++ bulk->remote_size = 0; ++ queue->remote_insert++; ++ } ++ ++ if (queue->process != queue->local_insert) { ++ vchiq_complete_bulk(bulk); ++ ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d, " ++ "rx len:%d", ++ is_tx ? "Send Bulk to" : "Recv Bulk from", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size); ++ } else { ++ /* fabricate a matching dummy bulk */ ++ bulk->data = NULL; ++ bulk->size = 0; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : ++ VCHIQ_BULK_RECEIVE; ++ queue->local_insert++; ++ } ++ ++ queue->process++; ++ } ++} ++ ++/* Called from the slot handler thread */ ++static void ++pause_bulks(VCHIQ_STATE_T *state) ++{ ++ if (unlikely(atomic_inc_return(&pause_bulks_count) != 1)) { ++ WARN_ON_ONCE(1); ++ atomic_set(&pause_bulks_count, 1); + return; + } - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); - -@@ -483,12 +602,8 @@ static int stop_streaming(struct vb2_queue *vq) - ret); - } - -- if (disable_camera(dev) < 0) { -- v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); -- return -EINVAL; -- } -- -- return ret; -+ if (disable_camera(dev) < 0) -+ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); - } - - static void bm2835_mmal_lock(struct vb2_queue *vq) -@@ -530,6 +645,7 @@ static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; -+ f->flags = fmt->flags; - - return 0; - } -@@ -647,10 +763,18 @@ static int vidioc_g_fbuf(struct file *file, void *fh, - { - /* The video overlay must stay within the framebuffer and can't be - positioned independently. */ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct vchiq_mmal_port *preview_port = -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW]; - a->flags = V4L2_FBUF_FLAG_OVERLAY; -- -- /* todo: v4l2_framebuffer still needs more info filling in -- * in order to pass the v4l2-compliance test. */ -+ a->fmt.width = preview_port->es.video.width; -+ a->fmt.height = preview_port->es.video.height; -+ a->fmt.pixelformat = V4L2_PIX_FMT_YUV420; -+ a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; -+ a->fmt.sizeimage = (preview_port->es.video.width * -+ preview_port->es.video.height * 3)>>1; -+ a->fmt.colorspace = V4L2_COLORSPACE_JPEG; - - return 0; - } -@@ -717,6 +841,8 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - - strlcpy(f->description, fmt->name, sizeof(f->description)); - f->pixelformat = fmt->fourcc; -+ f->flags = fmt->flags; + - return 0; - } - -@@ -729,20 +855,13 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - f->fmt.pix.height = dev->capture.height; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; -- f->fmt.pix.bytesperline = -- (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; -- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG -- && f->fmt.pix.sizeimage < (100 << 10)) { -- /* Need a minimum size for JPEG to account for EXIF. */ -- f->fmt.pix.sizeimage = (100 << 10); -- } -+ f->fmt.pix.bytesperline = dev->capture.stride; -+ f->fmt.pix.sizeimage = dev->capture.buffersize; - -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || -- dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) -- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -- else -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -@@ -766,21 +885,35 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - } - - f->fmt.pix.field = V4L2_FIELD_NONE; -- /* image must be a multiple of 32 pixels wide and 16 lines high */ -- v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, -- &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); -- f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; -- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; -- if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Clipping/aligning %dx%d format %08X\n", -+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); -+ -+ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 1, -+ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 1, 0); -+ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth)>>3; -+ -+ /* Image buffer has to be padded to allow for alignment, even though -+ * we then remove that padding before delivering the buffer. -+ */ -+ f->fmt.pix.sizeimage = ((f->fmt.pix.height+15)&~15) * -+ (((f->fmt.pix.width+31)&~31) * mfmt->depth) >> 3; -+ -+ if ((mfmt->flags & V4L2_FMT_FLAG_COMPRESSED) && -+ f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) - f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; - -- if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || -- mfmt->fourcc == V4L2_PIX_FMT_UYVY) -- f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; -- else -+ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Now %dx%d format %08X\n", -+ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); -+ - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, - __func__); - return 0; -@@ -818,8 +951,8 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - switch (mfmt->mmal_component) { - case MMAL_COMPONENT_CAMERA: - /* Make a further decision on port based on resolution */ -- if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH -- && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) -+ if (f->fmt.pix.width <= max_video_width -+ && f->fmt.pix.height <= max_video_height) - camera_port = port = - &dev->component[MMAL_COMPONENT_CAMERA]-> - output[MMAL_CAMERA_PORT_VIDEO]; -@@ -861,8 +994,9 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - camera_port->es.video.crop.y = 0; - camera_port->es.video.crop.width = f->fmt.pix.width; - camera_port->es.video.crop.height = f->fmt.pix.height; -- camera_port->es.video.frame_rate.num = 30; -+ camera_port->es.video.frame_rate.num = 0; - camera_port->es.video.frame_rate.den = 1; -+ camera_port->es.video.color_space = MMAL_COLOR_SPACE_JPEG_JFIF; - - ret = vchiq_mmal_port_set_format(dev->instance, camera_port); - -@@ -896,8 +1030,10 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - preview_port->es.video.crop.y = 0; - preview_port->es.video.crop.width = f->fmt.pix.width; - preview_port->es.video.crop.height = f->fmt.pix.height; -- preview_port->es.video.frame_rate.num = 30; -- preview_port->es.video.frame_rate.den = 1; -+ preview_port->es.video.frame_rate.num = -+ dev->capture.timeperframe.denominator; -+ preview_port->es.video.frame_rate.den = -+ dev->capture.timeperframe.numerator; - ret = vchiq_mmal_port_set_format(dev->instance, preview_port); - if (overlay_enabled) { - ret = vchiq_mmal_port_connect_tunnel( -@@ -913,7 +1049,9 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - - if (ret) { - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "%s failed to set format\n", __func__); -+ "%s failed to set format %dx%d %08X\n", __func__, -+ f->fmt.pix.width, f->fmt.pix.height, -+ f->fmt.pix.pixelformat); - /* ensure capture is not going to be tried */ - dev->capture.port = NULL; - } else { -@@ -927,69 +1065,91 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - camera_port->current_buffer.num = - camera_port->recommended_buffer.num; - -- port->format.encoding = mfmt->mmal; -- port->format.encoding_variant = 0; -- /* Set any encoding specific parameters */ -- switch (mfmt->mmal_component) { -- case MMAL_COMPONENT_VIDEO_ENCODE: -- port->format.bitrate = -- dev->capture.encode_bitrate; -- break; -- case MMAL_COMPONENT_IMAGE_ENCODE: -- /* Could set EXIF parameters here */ -- break; -- default: -- break; -- } -- ret = vchiq_mmal_port_set_format(dev->instance, port); -- -+ ret = -+ vchiq_mmal_port_connect_tunnel( -+ dev->instance, -+ camera_port, -+ &encode_component->input[0]); - if (ret) { -- v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "%s failed to set format\n", __func__); -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to create connection\n", -+ __func__); -+ /* ensure capture is not going to be tried */ -+ dev->capture.port = NULL; - } else { -+ port->es.video.width = f->fmt.pix.width; -+ port->es.video.height = f->fmt.pix.height; -+ port->es.video.crop.x = 0; -+ port->es.video.crop.y = 0; -+ port->es.video.crop.width = f->fmt.pix.width; -+ port->es.video.crop.height = f->fmt.pix.height; -+ port->es.video.frame_rate.num = -+ dev->capture.timeperframe.denominator; -+ port->es.video.frame_rate.den = -+ dev->capture.timeperframe.numerator; -+ -+ port->format.encoding = mfmt->mmal; -+ port->format.encoding_variant = 0; -+ /* Set any encoding specific parameters */ -+ switch (mfmt->mmal_component) { -+ case MMAL_COMPONENT_VIDEO_ENCODE: -+ port->format.bitrate = -+ dev->capture.encode_bitrate; -+ break; -+ case MMAL_COMPONENT_IMAGE_ENCODE: -+ /* Could set EXIF parameters here */ -+ break; -+ default: -+ break; -+ } -+ ret = vchiq_mmal_port_set_format(dev->instance, -+ port); -+ if (ret) -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "%s failed to set format %dx%d fmt %08X\n", -+ __func__, -+ f->fmt.pix.width, -+ f->fmt.pix.height, -+ f->fmt.pix.pixelformat -+ ); -+ } -+ -+ if (!ret) { - ret = vchiq_mmal_component_enable( - dev->instance, - encode_component); - if (ret) { - v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "%s Failed to enable encode components\n", -- __func__); -- } else { -- /* configure buffering */ -- port->current_buffer.num = 1; -- port->current_buffer.size = -- f->fmt.pix.sizeimage; -- if (port->format.encoding == -- MMAL_ENCODING_JPEG) { -- v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "JPEG - fiddle buffer size\n"); -- port->current_buffer.size = -- (f->fmt.pix.sizeimage < -- (100 << 10)) -- ? (100 << 10) : f->fmt.pix. -- sizeimage; -- } -+ &dev->v4l2_dev, -+ "%s Failed to enable encode components\n", -+ __func__); -+ } -+ } -+ if (!ret) { -+ /* configure buffering */ -+ port->current_buffer.num = 1; -+ port->current_buffer.size = -+ f->fmt.pix.sizeimage; -+ if (port->format.encoding == -+ MMAL_ENCODING_JPEG) { - v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "vid_cap - current_buffer.size being set to %d\n", -- f->fmt.pix.sizeimage); -- port->current_buffer.alignment = 0; -- ret = -- vchiq_mmal_port_connect_tunnel( -- dev->instance, -- camera_port, -- &encode_component->input[0]); -- if (ret) { -- v4l2_dbg(1, bcm2835_v4l2_debug, -- &dev->v4l2_dev, -- "%s failed to create connection\n", -- __func__); -- /* ensure capture is not going to be tried */ -- dev->capture.port = NULL; -- } -+ &dev->v4l2_dev, -+ "JPG - buf size now %d was %d\n", -+ f->fmt.pix.sizeimage, -+ port->current_buffer.size); -+ port->current_buffer.size = -+ (f->fmt.pix.sizeimage < -+ (100 << 10)) -+ ? (100 << 10) : f->fmt.pix. -+ sizeimage; - } -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "vid_cap - cur_buf.size set to %d\n", -+ f->fmt.pix.sizeimage); -+ port->current_buffer.alignment = 0; - } - } else { - /* configure buffering */ -@@ -1001,13 +1161,20 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, - if (!ret) { - dev->capture.fmt = mfmt; - dev->capture.stride = f->fmt.pix.bytesperline; -- dev->capture.width = port->es.video.crop.width; -- dev->capture.height = port->es.video.crop.height; -+ dev->capture.width = camera_port->es.video.crop.width; -+ dev->capture.height = camera_port->es.video.crop.height; -+ dev->capture.buffersize = port->current_buffer.size; - - /* select port for capture */ - dev->capture.port = port; - dev->capture.camera_port = camera_port; - dev->capture.encode_component = encode_component; -+ v4l2_dbg(1, bcm2835_v4l2_debug, -+ &dev->v4l2_dev, -+ "Set dev->capture.fmt %08X, %dx%d, stride %d, size %d", -+ port->format.encoding, -+ dev->capture.width, dev->capture.height, -+ dev->capture.stride, dev->capture.buffersize); - } - } - -@@ -1048,14 +1215,115 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - } - - ret = mmal_setup_components(dev, f); -- if (ret != 0) -+ if (ret != 0) { - v4l2_err(&dev->v4l2_dev, - "%s: failed to setup mmal components: %d\n", - __func__, ret); -+ ret = -EINVAL; -+ } - - return ret; - } - -+int vidioc_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ static const struct v4l2_frmsize_stepwise sizes = { -+ MIN_WIDTH, MAX_WIDTH, 2, -+ MIN_HEIGHT, MAX_HEIGHT, 2 -+ }; -+ int i; -+ -+ if (fsize->index) -+ return -EINVAL; -+ for (i = 0; i < ARRAY_SIZE(formats); i++) -+ if (formats[i].fourcc == fsize->pixel_format) -+ break; -+ if (i == ARRAY_SIZE(formats)) -+ return -EINVAL; -+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -+ fsize->stepwise = sizes; -+ return 0; ++ /* Block bulk transfers from all services */ ++ mutex_lock(&state->bulk_transfer_mutex); +} + -+/* timeperframe is arbitrary and continous */ -+static int vidioc_enum_frameintervals(struct file *file, void *priv, -+ struct v4l2_frmivalenum *fival) ++/* Called from the slot handler thread */ ++static void ++resume_bulks(VCHIQ_STATE_T *state) +{ + int i; ++ if (unlikely(atomic_dec_return(&pause_bulks_count) != 0)) { ++ WARN_ON_ONCE(1); ++ atomic_set(&pause_bulks_count, 0); ++ return; ++ } + -+ if (fival->index) -+ return -EINVAL; ++ /* Allow bulk transfers from all services */ ++ mutex_unlock(&state->bulk_transfer_mutex); + -+ for (i = 0; i < ARRAY_SIZE(formats); i++) -+ if (formats[i].fourcc == fival->pixel_format) -+ break; -+ if (i == ARRAY_SIZE(formats)) -+ return -EINVAL; ++ if (state->deferred_bulks == 0) ++ return; + -+ /* regarding width & height - we support any within range */ -+ if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH || -+ fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT) -+ return -EINVAL; ++ /* Deal with any bulks which had to be deferred due to being in ++ * paused state. Don't try to match up to number of deferred bulks ++ * in case we've had something come and close the service in the ++ * interim - just process all bulk queues for all services */ ++ vchiq_log_info(vchiq_core_log_level, "%s: processing %d deferred bulks", ++ __func__, state->deferred_bulks); + -+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ int resolved_rx = 0; ++ int resolved_tx = 0; ++ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ continue; + -+ /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ -+ fival->stepwise.min = tpf_min; -+ fival->stepwise.max = tpf_max; -+ fival->stepwise.step = (struct v4l2_fract) {1, 1}; ++ mutex_lock(&service->bulk_mutex); ++ resolved_rx = resolve_bulks(service, &service->bulk_rx); ++ resolved_tx = resolve_bulks(service, &service->bulk_tx); ++ mutex_unlock(&service->bulk_mutex); ++ if (resolved_rx) ++ notify_bulks(service, &service->bulk_rx, 1); ++ if (resolved_tx) ++ notify_bulks(service, &service->bulk_tx, 1); ++ } ++ state->deferred_bulks = 0; ++} ++ ++static int ++parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) ++{ ++ VCHIQ_SERVICE_T *service = NULL; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; ++ ++ msgid = header->msgid; ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); ++ if (size >= sizeof(struct vchiq_open_payload)) { ++ const struct vchiq_open_payload *payload = ++ (struct vchiq_open_payload *)header->data; ++ unsigned int fourcc; ++ ++ fourcc = payload->fourcc; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs OPEN@%x (%d->'%c%c%c%c')", ++ state->id, (unsigned int)header, ++ localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc)); ++ ++ service = get_listening_service(state, fourcc); ++ ++ if (service) { ++ /* A matching service exists */ ++ short version = payload->version; ++ short version_min = payload->version_min; ++ if ((service->version < version_min) || ++ (version < service->version_min)) { ++ /* Version mismatch */ ++ vchiq_loud_error_header(); ++ vchiq_loud_error("%d: service %d (%c%c%c%c) " ++ "version mismatch - local (%d, min %d)" ++ " vs. remote (%d, min %d)", ++ state->id, service->localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ service->version, service->version_min, ++ version, version_min); ++ vchiq_loud_error_footer(); ++ unlock_service(service); ++ service = NULL; ++ goto fail_open; ++ } ++ service->peer_version = version; ++ ++ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { ++ struct vchiq_openack_payload ack_payload = { ++ service->version ++ }; ++ VCHIQ_ELEMENT_T body = { ++ &ack_payload, ++ sizeof(ack_payload) ++ }; ++ ++ if (state->version_common < ++ VCHIQ_VERSION_SYNCHRONOUS_MODE) ++ service->sync = 0; ++ ++ /* Acknowledge the OPEN */ ++ if (service->sync && ++ (state->version_common >= ++ VCHIQ_VERSION_SYNCHRONOUS_MODE)) { ++ if (queue_message_sync(state, NULL, ++ VCHIQ_MAKE_MSG( ++ VCHIQ_MSG_OPENACK, ++ service->localport, ++ remoteport), ++ &body, 1, sizeof(ack_payload), ++ 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ } else { ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG( ++ VCHIQ_MSG_OPENACK, ++ service->localport, ++ remoteport), ++ &body, 1, sizeof(ack_payload), ++ 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ } ++ ++ /* The service is now open */ ++ vchiq_set_service_state(service, ++ service->sync ? VCHIQ_SRVSTATE_OPENSYNC ++ : VCHIQ_SRVSTATE_OPEN); ++ } ++ ++ service->remoteport = remoteport; ++ service->client_id = ((int *)header->data)[1]; ++ if (make_service_callback(service, VCHIQ_SERVICE_OPENED, ++ NULL, NULL) == VCHIQ_RETRY) { ++ /* Bail out if not ready */ ++ service->remoteport = VCHIQ_PORT_FREE; ++ goto bail_not_ready; ++ } ++ ++ /* Success - the message has been dealt with */ ++ unlock_service(service); ++ return 1; ++ } ++ } ++ ++fail_open: ++ /* No available service, or an invalid request - send a CLOSE */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)), ++ NULL, 0, 0, 0) == VCHIQ_RETRY) ++ goto bail_not_ready; ++ ++ return 1; ++ ++bail_not_ready: ++ if (service) ++ unlock_service(service); + + return 0; +} + -+static int vidioc_g_parm(struct file *file, void *priv, -+ struct v4l2_streamparm *parm) ++/* Called by the slot handler thread */ ++static void ++parse_rx_slots(VCHIQ_STATE_T *state) +{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ VCHIQ_SHARED_STATE_T *remote = state->remote; ++ VCHIQ_SERVICE_T *service = NULL; ++ int tx_pos; ++ DEBUG_INITIALISE(state->local) + -+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; ++ tx_pos = remote->tx_pos; + -+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; -+ parm->parm.capture.timeperframe = dev->capture.timeperframe; -+ parm->parm.capture.readbuffers = 1; -+ return 0; -+} ++ while (state->rx_pos != tx_pos) { ++ VCHIQ_HEADER_T *header; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; + -+#define FRACT_CMP(a, OP, b) \ -+ ((u64)(a).numerator * (b).denominator OP \ -+ (u64)(b).numerator * (a).denominator) ++ DEBUG_TRACE(PARSE_LINE); ++ if (!state->rx_data) { ++ int rx_index; ++ WARN_ON(!((state->rx_pos & VCHIQ_SLOT_MASK) == 0)); ++ rx_index = remote->slot_queue[ ++ SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & ++ VCHIQ_SLOT_QUEUE_MASK]; ++ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, ++ rx_index); ++ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index); + -+static int vidioc_s_parm(struct file *file, void *priv, -+ struct v4l2_streamparm *parm) -+{ -+ struct bm2835_mmal_dev *dev = video_drvdata(file); -+ struct v4l2_fract tpf; -+ struct mmal_parameter_rational fps_param; -+ -+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ return -EINVAL; -+ -+ tpf = parm->parm.capture.timeperframe; -+ -+ /* tpf: {*, 0} resets timing; clip to [min, max]*/ -+ tpf = tpf.denominator ? tpf : tpf_default; -+ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; -+ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; -+ -+ dev->capture.timeperframe = tpf; -+ parm->parm.capture.timeperframe = tpf; -+ parm->parm.capture.readbuffers = 1; -+ -+ fps_param.num = 0; /* Select variable fps, and then use -+ * FPS_RANGE to select the actual limits. -+ */ -+ fps_param.den = 1; -+ set_framerate_params(dev); -+ -+ return 0; -+} -+ - static const struct v4l2_ioctl_ops camera0_ioctl_ops = { - /* overlay */ - .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -@@ -1084,6 +1352,51 @@ static const struct v4l2_ioctl_ops camera0_ioctl_ops = { - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_enum_framesizes = vidioc_enum_framesizes, -+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -+ .vidioc_g_parm = vidioc_g_parm, -+ .vidioc_s_parm = vidioc_s_parm, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ -+ .vidioc_log_status = v4l2_ctrl_log_status, -+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, -+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -+}; -+ -+static const struct v4l2_ioctl_ops camera0_ioctl_ops_gstreamer = { -+ /* overlay */ -+ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, -+ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, -+ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, -+ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, -+ .vidioc_overlay = vidioc_overlay, -+ .vidioc_g_fbuf = vidioc_g_fbuf, -+ -+ /* inputs */ -+ .vidioc_enum_input = vidioc_enum_input, -+ .vidioc_g_input = vidioc_g_input, -+ .vidioc_s_input = vidioc_s_input, -+ -+ /* capture */ -+ .vidioc_querycap = vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, -+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, -+ -+ /* buffer management */ -+ .vidioc_reqbufs = vb2_ioctl_reqbufs, -+ .vidioc_create_bufs = vb2_ioctl_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ /* Remove this function ptr to fix gstreamer bug -+ .vidioc_enum_framesizes = vidioc_enum_framesizes, */ -+ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -+ .vidioc_g_parm = vidioc_g_parm, -+ .vidioc_s_parm = vidioc_s_parm, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - -@@ -1122,8 +1435,10 @@ static int set_camera_parameters(struct vchiq_mmal_instance *instance, - .max_stills_h = MAX_HEIGHT, - .stills_yuv422 = 1, - .one_shot_stills = 1, -- .max_preview_video_w = 1920, -- .max_preview_video_h = 1088, -+ .max_preview_video_w = (max_video_width > 1920) ? -+ max_video_width : 1920, -+ .max_preview_video_h = (max_video_height > 1088) ? -+ max_video_height : 1088, - .num_preview_video_frames = 3, - .stills_capture_circular_buffer_height = 0, - .fast_preview_resume = 0, -@@ -1141,6 +1456,7 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - { - int ret; - struct mmal_es_format *format; -+ u32 bool_true = 1; - - ret = vchiq_mmal_init(&dev->instance); - if (ret < 0) -@@ -1176,8 +1492,8 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 1024; - format->es->video.crop.height = 768; -- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ -+ format->es->video.frame_rate.den = 1; - - format = - &dev->component[MMAL_COMPONENT_CAMERA]-> -@@ -1192,8 +1508,14 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 1024; - format->es->video.crop.height = 768; -- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; -- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ -+ format->es->video.frame_rate.den = 1; -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO], -+ MMAL_PARAMETER_NO_IMAGE_PADDING, -+ &bool_true, sizeof(bool_true)); - - format = - &dev->component[MMAL_COMPONENT_CAMERA]-> -@@ -1207,13 +1529,22 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - format->es->video.crop.y = 0; - format->es->video.crop.width = 2592; - format->es->video.crop.height = 1944; -- format->es->video.frame_rate.num = 30; -+ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ - format->es->video.frame_rate.den = 1; - - dev->capture.width = format->es->video.width; - dev->capture.height = format->es->video.height; - dev->capture.fmt = &formats[0]; - dev->capture.encode_component = NULL; -+ dev->capture.timeperframe = tpf_default; -+ dev->capture.enc_profile = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; -+ dev->capture.enc_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; -+ -+ vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE], -+ MMAL_PARAMETER_NO_IMAGE_PADDING, -+ &bool_true, sizeof(bool_true)); - - /* get the preview component ready */ - ret = vchiq_mmal_component_init( -@@ -1260,6 +1591,14 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) - } - - { -+ struct vchiq_mmal_port *encoder_port = -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ encoder_port->format.encoding = MMAL_ENCODING_H264; -+ ret = vchiq_mmal_port_set_format(dev->instance, -+ encoder_port); -+ } -+ -+ { - unsigned int enable = 1; - vchiq_mmal_port_parameter_set( - dev->instance, -@@ -1312,6 +1651,11 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - int ret; - - *vfd = vdev_template; -+ if (gst_v4l2src_is_broken) { -+ v4l2_info(&dev->v4l2_dev, -+ "Work-around for gstreamer issue is active.\n"); -+ vfd->ioctl_ops = &camera0_ioctl_ops_gstreamer; -+ } - - vfd->v4l2_dev = &dev->v4l2_dev; - -@@ -1326,8 +1670,9 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - if (ret < 0) - return ret; - -- v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", -- video_device_node_name(vfd)); -+ v4l2_info(vfd->v4l2_dev, -+ "V4L2 device registered as %s - stills mode > %dx%d\n", -+ video_device_node_name(vfd), max_video_width, max_video_height); - - return 0; - } -@@ -1335,9 +1680,9 @@ static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, - static struct v4l2_format default_v4l2_format = { - .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, - .fmt.pix.width = 1024, -- .fmt.pix.bytesperline = 1024 * 3 / 2, -+ .fmt.pix.bytesperline = 1024, - .fmt.pix.height = 768, -- .fmt.pix.sizeimage = 1<<18, -+ .fmt.pix.sizeimage = 1024*768, - }; - - static int __init bm2835_mmal_init(void) -@@ -1400,6 +1745,9 @@ static int __init bm2835_mmal_init(void) - if (ret < 0) - goto unreg_dev; - -+ /* Really want to call vidioc_s_fmt_vid_cap with the default -+ * format, but currently the APIs don't join up. -+ */ - ret = mmal_setup_components(dev, &default_v4l2_format); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h -index 883eab7..7fe9f65 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.h -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.h -@@ -15,7 +15,7 @@ - * core driver device - */ - --#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ -+#define V4L2_CTRL_COUNT 28 /* number of v4l controls */ - - enum { - MMAL_COMPONENT_CAMERA = 0, -@@ -32,9 +32,6 @@ enum { - MMAL_CAMERA_PORT_COUNT - }; - --#define PREVIEW_FRAME_RATE_NUM 30 --#define PREVIEW_FRAME_RATE_DEN 1 -- - #define PREVIEW_LAYER 2 - - extern int bcm2835_v4l2_debug; -@@ -48,9 +45,19 @@ struct bm2835_mmal_dev { - /* controls */ - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; -+ enum v4l2_scene_mode scene_mode; - struct mmal_colourfx colourfx; - int hflip; - int vflip; -+ int red_gain; -+ int blue_gain; -+ enum mmal_parameter_exposuremode exposure_mode_user; -+ enum v4l2_exposure_auto_type exposure_mode_v4l2_user; -+ /* active exposure mode may differ if selected via a scene mode */ -+ enum mmal_parameter_exposuremode exposure_mode_active; -+ enum mmal_parameter_exposuremeteringmode metering_mode; -+ unsigned int manual_shutter_speed; -+ bool exp_auto_priority; - - /* allocated mmal instance and components */ - struct vchiq_mmal_instance *instance; -@@ -63,12 +70,18 @@ struct bm2835_mmal_dev { - unsigned int width; /* width */ - unsigned int height; /* height */ - unsigned int stride; /* stride */ -+ unsigned int buffersize; /* buffer size with padding */ - struct mmal_fmt *fmt; -+ struct v4l2_fract timeperframe; - - /* H264 encode bitrate */ - int encode_bitrate; - /* H264 bitrate mode. CBR/VBR */ - int encode_bitrate_mode; -+ /* H264 profile */ -+ enum v4l2_mpeg_video_h264_profile enc_profile; -+ /* H264 level */ -+ enum v4l2_mpeg_video_h264_level enc_level; - /* JPEG Q-factor */ - int q_factor; - -@@ -98,7 +111,7 @@ int bm2835_mmal_init_controls( - struct v4l2_ctrl_handler *hdl); - - int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); -- -+int set_framerate_params(struct bm2835_mmal_dev *dev); - - /* Debug helpers */ - -diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c -index d1408e5..3017b94 100644 ---- a/drivers/media/platform/bcm2835/controls.c -+++ b/drivers/media/platform/bcm2835/controls.c -@@ -30,11 +30,23 @@ - #include "mmal-parameters.h" - #include "bcm2835-camera.h" - --/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. -- * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. -+/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -4.0 to +4.0. -+ * MMAL values are in 1/6th increments so the MMAL range is -24 to +24. -+ * V4L2 docs say value "is expressed in terms of EV, drivers should interpret -+ * the values as 0.001 EV units, where the value 1000 stands for +1 EV." -+ * V4L2 is limited to a max of 32 values in a menu, so count in 1/3rds from -+ * -4 to +4 - */ - static const s64 ev_bias_qmenu[] = { -- -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 -+ -4000, -3667, -3333, -+ -3000, -2667, -2333, -+ -2000, -1667, -1333, -+ -1000, -667, -333, -+ 0, 333, 667, -+ 1000, 1333, 1667, -+ 2000, 2333, 2667, -+ 3000, 3333, 3667, -+ 4000 - }; - - /* Supported ISO values -@@ -44,13 +56,19 @@ static const s64 iso_qmenu[] = { - 0, 100, 200, 400, 800, - }; - -+static const s64 mains_freq_qmenu[] = { -+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, -+ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, -+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, -+ V4L2_CID_POWER_LINE_FREQUENCY_AUTO -+}; -+ - /* Supported video encode modes */ - static const s64 bitrate_mode_qmenu[] = { - (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, - (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, - }; - -- - enum bm2835_mmal_ctrl_type { - MMAL_CONTROL_TYPE_STD, - MMAL_CONTROL_TYPE_STD_MENU, -@@ -77,6 +95,7 @@ struct bm2835_mmal_v4l2_ctrl { - const s64 *imenu; /* integer menu array */ - u32 mmal_id; /* mmal parameter id */ - bm2835_mmal_v4l2_ctrl_cb *setter; -+ bool ignore_errors; - }; - - struct v4l2_to_mmal_effects_setting { -@@ -126,6 +145,25 @@ static const struct v4l2_to_mmal_effects_setting - 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } - }; - -+struct v4l2_mmal_scene_config { -+ enum v4l2_scene_mode v4l2_scene; -+ enum mmal_parameter_exposuremode exposure_mode; -+ enum mmal_parameter_exposuremeteringmode metering_mode; -+}; -+ -+static const struct v4l2_mmal_scene_config scene_configs[] = { -+ /* V4L2_SCENE_MODE_NONE automatically added */ -+ { -+ V4L2_SCENE_MODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMODE_NIGHT, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE -+ }, -+ { -+ V4L2_SCENE_MODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMODE_SPORTS, -+ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE -+ }, -+}; - - /* control handlers*/ - -@@ -133,10 +171,7 @@ static int ctrl_set_rational(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -- struct { -- s32 num; /**< Numerator */ -- s32 den; /**< Denominator */ -- } rational_value; -+ struct mmal_parameter_rational rational_value; - struct vchiq_mmal_port *control; - - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -@@ -166,6 +201,41 @@ static int ctrl_set_value(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_value_menu(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *control; -+ -+ if (ctrl->val > mmal_ctrl->max || ctrl->val < mmal_ctrl->min) -+ return 1; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ u32_value = mmal_ctrl->imenu[ctrl->val]; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ s32 s32_value; -+ struct vchiq_mmal_port *control; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ s32_value = (ctrl->val-12)*2; /* Convert from index to 1/6ths */ -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &s32_value, sizeof(s32_value)); -+} -+ - static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -@@ -245,37 +315,97 @@ static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -- u32 u32_value; -+ enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode_user; -+ u32 shutter_speed = 0; - struct vchiq_mmal_port *control; -+ int ret = 0; - - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; - -+ if (mmal_ctrl->mmal_id == MMAL_PARAMETER_SHUTTER_SPEED) { -+ /* V4L2 is in 100usec increments. -+ * MMAL is 1usec. -+ */ -+ dev->manual_shutter_speed = ctrl->val * 100; -+ } else if (mmal_ctrl->mmal_id == MMAL_PARAMETER_EXPOSURE_MODE) { -+ switch (ctrl->val) { -+ case V4L2_EXPOSURE_AUTO: -+ exp_mode = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ break; -+ -+ case V4L2_EXPOSURE_MANUAL: -+ exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF; -+ break; ++ /* Initialise use_count to one, and increment ++ ** release_count at the end of the slot to avoid ++ ** releasing the slot prematurely. */ ++ state->rx_info->use_count = 1; ++ state->rx_info->release_count = 0; + } -+ dev->exposure_mode_user = exp_mode; -+ dev->exposure_mode_v4l2_user = ctrl->val; -+ } else if (mmal_ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { -+ dev->exp_auto_priority = ctrl->val; -+ } + -+ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { -+ if (exp_mode == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; ++ header = (VCHIQ_HEADER_T *)(state->rx_data + ++ (state->rx_pos & VCHIQ_SLOT_MASK)); ++ DEBUG_VALUE(PARSE_HEADER, (int)header); ++ msgid = header->msgid; ++ DEBUG_VALUE(PARSE_MSGID, msgid); ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); + -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exp_mode, -+ sizeof(u32)); -+ dev->exposure_mode_active = exp_mode; -+ } -+ /* exposure_dynamic_framerate (V4L2_CID_EXPOSURE_AUTO_PRIORITY) should -+ * always apply irrespective of scene mode. -+ */ -+ ret += set_framerate_params(dev); ++ if (type != VCHIQ_MSG_DATA) ++ VCHIQ_STATS_INC(state, ctrl_rx_count); + -+ return ret; -+} ++ switch (type) { ++ case VCHIQ_MSG_OPENACK: ++ case VCHIQ_MSG_CLOSE: ++ case VCHIQ_MSG_DATA: ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ service = find_service_by_port(state, localport); ++ if ((!service || ++ ((service->remoteport != remoteport) && ++ (service->remoteport != VCHIQ_PORT_FREE))) && ++ (localport == 0) && ++ (type == VCHIQ_MSG_CLOSE)) { ++ /* This could be a CLOSE from a client which ++ hadn't yet received the OPENACK - look for ++ the connected service */ ++ if (service) ++ unlock_service(service); ++ service = get_connected_service(state, ++ remoteport); ++ if (service) ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) - " ++ "found connected service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ service->localport); ++ } + -+static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ - switch (ctrl->val) { -- case V4L2_EXPOSURE_AUTO: -- u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; -+ case V4L2_EXPOSURE_METERING_AVERAGE: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; - break; - -- case V4L2_EXPOSURE_MANUAL: -- u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; -+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; - break; - -- case V4L2_EXPOSURE_SHUTTER_PRIORITY: -- u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; -+ case V4L2_EXPOSURE_METERING_SPOT: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; - break; - -- case V4L2_EXPOSURE_APERTURE_PRIORITY: -- u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; -+ /* todo matrix weighting not added to Linux API till 3.9 -+ case V4L2_EXPOSURE_METERING_MATRIX: -+ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; - break; -+ */ - - } - -- /* todo: what about the other ten modes there are MMAL parameters for */ -- return vchiq_mmal_port_parameter_set(dev->instance, control, -+ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { -+ struct vchiq_mmal_port *control; -+ u32 u32_value = dev->metering_mode; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, - mmal_ctrl->mmal_id, - &u32_value, sizeof(u32_value)); -+ } else -+ return 0; - } - --static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, -+static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -@@ -285,24 +415,18 @@ static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, - control = &dev->component[MMAL_COMPONENT_CAMERA]->control; - - switch (ctrl->val) { -- case V4L2_EXPOSURE_METERING_AVERAGE: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; -+ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: -+ u32_value = MMAL_PARAM_FLICKERAVOID_OFF; - break; -- -- case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; -+ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: -+ u32_value = MMAL_PARAM_FLICKERAVOID_50HZ; - break; -- -- case V4L2_EXPOSURE_METERING_SPOT: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; -+ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: -+ u32_value = MMAL_PARAM_FLICKERAVOID_60HZ; - break; -- -- /* todo matrix weighting not added to Linux API till 3.9 -- case V4L2_EXPOSURE_METERING_MATRIX: -- u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; -+ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: -+ u32_value = MMAL_PARAM_FLICKERAVOID_AUTO; - break; -- */ -- - } - - return vchiq_mmal_port_parameter_set(dev->instance, control, -@@ -367,6 +491,29 @@ static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_awb_gains(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct vchiq_mmal_port *control; -+ struct mmal_parameter_awbgains gains; -+ -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; -+ -+ if (ctrl->id == V4L2_CID_RED_BALANCE) -+ dev->red_gain = ctrl->val; -+ else if (ctrl->id == V4L2_CID_BLUE_BALANCE) -+ dev->blue_gain = ctrl->val; -+ -+ gains.r_gain.num = dev->red_gain; -+ gains.b_gain.num = dev->blue_gain; -+ gains.r_gain.den = gains.b_gain.den = 1000; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, control, -+ mmal_ctrl->mmal_id, -+ &gains, sizeof(gains)); -+} -+ - static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -@@ -443,8 +590,8 @@ static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, - &dev->colourfx, sizeof(dev->colourfx)); - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -- "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -- mmal_ctrl, ctrl->id, ctrl->val, ret, -+ "%s: After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", -+ __func__, mmal_ctrl, ctrl->id, ctrl->val, ret, - (ret == 0 ? 0 : -EINVAL)); - return (ret == 0 ? 0 : EINVAL); - } -@@ -494,7 +641,7 @@ static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, - return 0; - } - --static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, -+static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl *ctrl, - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) - { -@@ -510,12 +657,247 @@ static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, - &u32_value, sizeof(u32_value)); - } - -+static int ctrl_set_video_encode_param_output(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ u32 u32_value; -+ struct vchiq_mmal_port *vid_enc_ctl; -+ -+ vid_enc_ctl = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; -+ -+ u32_value = ctrl->val; -+ -+ return vchiq_mmal_port_parameter_set(dev->instance, vid_enc_ctl, -+ mmal_ctrl->mmal_id, -+ &u32_value, sizeof(u32_value)); -+} -+ -+static int ctrl_set_video_encode_profile_level(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ struct mmal_parameter_video_profile param; -+ int ret = 0; -+ -+ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) { -+ switch (ctrl->val) { -+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: -+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: -+ dev->capture.enc_profile = ctrl->val; ++ if (!service) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) - " ++ "invalid/closed service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, localport); ++ goto skip_message; ++ } + break; + default: -+ ret = -EINVAL; -+ break; -+ } -+ } else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) { -+ switch (ctrl->val) { -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: -+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: -+ dev->capture.enc_level = ctrl->val; -+ break; -+ default: -+ ret = -EINVAL; -+ break; -+ } -+ } -+ -+ if (!ret) { -+ switch (dev->capture.enc_profile) { -+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: -+ param.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: -+ param.profile = -+ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: -+ param.profile = MMAL_VIDEO_PROFILE_H264_MAIN; -+ break; -+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: -+ param.profile = MMAL_VIDEO_PROFILE_H264_HIGH; -+ break; -+ default: -+ /* Should never get here */ + break; + } + -+ switch (dev->capture.enc_level) { -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_1; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: -+ param.level = MMAL_VIDEO_LEVEL_H264_1b; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_11; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_12; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: -+ param.level = MMAL_VIDEO_LEVEL_H264_13; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_2; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_21; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_22; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_3; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: -+ param.level = MMAL_VIDEO_LEVEL_H264_31; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: -+ param.level = MMAL_VIDEO_LEVEL_H264_32; -+ break; -+ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: -+ param.level = MMAL_VIDEO_LEVEL_H264_4; -+ break; -+ default: -+ /* Should never get here */ -+ break; ++ if (SRVTRACE_ENABLED(service, VCHIQ_LOG_INFO)) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ vchiq_log_info(SRVTRACE_LEVEL(service), ++ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d " ++ "len:%d", ++ msg_type_str(type), type, ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ remoteport, localport, size); ++ if (size > 0) ++ vchiq_log_dump_mem("Rcvd", 0, header->data, ++ min(64, size)); + } + -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0], -+ mmal_ctrl->mmal_id, -+ ¶m, sizeof(param)); -+ } -+ return ret; -+} ++ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) ++ > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "header %x (msgid %x) - size %x too big for " ++ "slot", ++ (unsigned int)header, (unsigned int)msgid, ++ (unsigned int)size); ++ WARN(1, "oversized for slot\n"); ++ } + -+static int ctrl_set_scene_mode(struct bm2835_mmal_dev *dev, -+ struct v4l2_ctrl *ctrl, -+ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) -+{ -+ int ret = 0; -+ int shutter_speed; -+ struct vchiq_mmal_port *control; ++ switch (type) { ++ case VCHIQ_MSG_OPEN: ++ WARN_ON(!(VCHIQ_MSG_DSTPORT(msgid) == 0)); ++ if (!parse_open(state, header)) ++ goto bail_not_ready; ++ break; ++ case VCHIQ_MSG_OPENACK: ++ if (size >= sizeof(struct vchiq_openack_payload)) { ++ const struct vchiq_openack_payload *payload = ++ (struct vchiq_openack_payload *) ++ header->data; ++ service->peer_version = payload->version; ++ } ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs OPENACK@%x,%x (%d->%d) v:%d", ++ state->id, (unsigned int)header, size, ++ remoteport, localport, service->peer_version); ++ if (service->srvstate == ++ VCHIQ_SRVSTATE_OPENING) { ++ service->remoteport = remoteport; ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_OPEN); ++ up(&service->remove_event); ++ } else ++ vchiq_log_error(vchiq_core_log_level, ++ "OPENACK received in state %s", ++ srvstate_names[service->srvstate]); ++ break; ++ case VCHIQ_MSG_CLOSE: ++ WARN_ON(size != 0); /* There should be no data */ + -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "scene mode selected %d, was %d\n", ctrl->val, -+ dev->scene_mode); -+ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs CLOSE@%x (%d->%d)", ++ state->id, (unsigned int)header, ++ remoteport, localport); + -+ if (ctrl->val == dev->scene_mode) -+ return 0; ++ mark_service_closing_internal(service, 1); + -+ if (ctrl->val == V4L2_SCENE_MODE_NONE) { -+ /* Restore all user selections */ -+ dev->scene_mode = V4L2_SCENE_MODE_NONE; ++ if (vchiq_close_service_internal(service, ++ 1/*close_recvd*/) == VCHIQ_RETRY) ++ goto bail_not_ready; + -+ if (dev->exposure_mode_user == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; -+ else -+ shutter_speed = 0; ++ vchiq_log_info(vchiq_core_log_level, ++ "Close Service %c%c%c%c s:%u d:%d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->localport, ++ service->remoteport); ++ break; ++ case VCHIQ_MSG_DATA: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs DATA@%x,%x (%d->%d)", ++ state->id, (unsigned int)header, size, ++ remoteport, localport); + -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", -+ __func__, shutter_speed, dev->exposure_mode_user, -+ dev->metering_mode); -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &dev->exposure_mode_user, -+ sizeof(u32)); -+ dev->exposure_mode_active = dev->exposure_mode_user; -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &dev->metering_mode, -+ sizeof(u32)); -+ ret += set_framerate_params(dev); -+ } else { -+ /* Set up scene mode */ -+ int i; -+ const struct v4l2_mmal_scene_config *scene = NULL; -+ int shutter_speed; -+ enum mmal_parameter_exposuremode exposure_mode; -+ enum mmal_parameter_exposuremeteringmode metering_mode; ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) { ++ header->msgid = msgid | VCHIQ_MSGID_CLAIMED; ++ claim_slot(state->rx_info); ++ DEBUG_TRACE(PARSE_LINE); ++ if (make_service_callback(service, ++ VCHIQ_MESSAGE_AVAILABLE, header, ++ NULL) == VCHIQ_RETRY) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, ++ size); ++ } else { ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ break; ++ case VCHIQ_MSG_CONNECT: ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs CONNECT@%x", ++ state->id, (unsigned int)header); ++ state->version_common = ((VCHIQ_SLOT_ZERO_T *) ++ state->slot_data)->version; ++ up(&state->connect); ++ break; ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: { ++ VCHIQ_BULK_QUEUE_T *queue; ++ WARN_ON(!state->is_master); ++ queue = (type == VCHIQ_MSG_BULK_RX) ? ++ &service->bulk_tx : &service->bulk_rx; ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) { ++ VCHIQ_BULK_T *bulk; ++ int resolved = 0; + -+ for (i = 0; i < ARRAY_SIZE(scene_configs); i++) { -+ if (scene_configs[i].v4l2_scene == -+ ctrl->val) { -+ scene = &scene_configs[i]; ++ DEBUG_TRACE(PARSE_LINE); ++ if (mutex_lock_interruptible( ++ &service->bulk_mutex) != 0) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ ++ WARN_ON(!(queue->remote_insert < queue->remove + ++ VCHIQ_NUM_SERVICE_BULKS)); ++ bulk = &queue->bulks[ ++ BULK_INDEX(queue->remote_insert)]; ++ bulk->remote_data = ++ (void *)((int *)header->data)[0]; ++ bulk->remote_size = ((int *)header->data)[1]; ++ wmb(); ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->remote_size, ++ (unsigned int)bulk->remote_data); ++ ++ queue->remote_insert++; ++ ++ if (atomic_read(&pause_bulks_count)) { ++ state->deferred_bulks++; ++ vchiq_log_info(vchiq_core_log_level, ++ "%s: deferring bulk (%d)", ++ __func__, ++ state->deferred_bulks); ++ if (state->conn_state != ++ VCHIQ_CONNSTATE_PAUSE_SENT) ++ vchiq_log_error( ++ vchiq_core_log_level, ++ "%s: bulks paused in " ++ "unexpected state %s", ++ __func__, ++ conn_state_names[ ++ state->conn_state]); ++ } else if (state->conn_state == ++ VCHIQ_CONNSTATE_CONNECTED) { ++ DEBUG_TRACE(PARSE_LINE); ++ resolved = resolve_bulks(service, ++ queue); ++ } ++ ++ mutex_unlock(&service->bulk_mutex); ++ if (resolved) ++ notify_bulks(service, queue, ++ 1/*retry_poll*/); ++ } ++ } break; ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ WARN_ON(state->is_master); ++ if ((service->remoteport == remoteport) ++ && (service->srvstate != ++ VCHIQ_SRVSTATE_FREE)) { ++ VCHIQ_BULK_QUEUE_T *queue; ++ VCHIQ_BULK_T *bulk; ++ ++ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ? ++ &service->bulk_rx : &service->bulk_tx; ++ ++ DEBUG_TRACE(PARSE_LINE); ++ if (mutex_lock_interruptible( ++ &service->bulk_mutex) != 0) { ++ DEBUG_TRACE(PARSE_LINE); ++ goto bail_not_ready; ++ } ++ if ((int)(queue->remote_insert - ++ queue->local_insert) >= 0) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) " ++ "unexpected (ri=%d,li=%d)", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ queue->remote_insert, ++ queue->local_insert); ++ mutex_unlock(&service->bulk_mutex); ++ break; ++ } ++ ++ BUG_ON(queue->process == queue->local_insert); ++ BUG_ON(queue->process != queue->remote_insert); ++ ++ bulk = &queue->bulks[ ++ BULK_INDEX(queue->remote_insert)]; ++ bulk->actual = *(int *)header->data; ++ queue->remote_insert++; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->actual, (unsigned int)bulk->data); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs:%d %cx li=%x ri=%x p=%x", ++ state->id, localport, ++ (type == VCHIQ_MSG_BULK_RX_DONE) ? ++ 'r' : 't', ++ queue->local_insert, ++ queue->remote_insert, queue->process); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ WARN_ON(queue->process == queue->local_insert); ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ mutex_unlock(&service->bulk_mutex); ++ DEBUG_TRACE(PARSE_LINE); ++ notify_bulks(service, queue, 1/*retry_poll*/); ++ DEBUG_TRACE(PARSE_LINE); ++ } ++ break; ++ case VCHIQ_MSG_PADDING: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs PADDING@%x,%x", ++ state->id, (unsigned int)header, size); ++ break; ++ case VCHIQ_MSG_PAUSE: ++ /* If initiated, signal the application thread */ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs PAUSE@%x,%x", ++ state->id, (unsigned int)header, size); ++ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: PAUSE received in state PAUSED", ++ state->id); + break; + } -+ } -+ if (i >= ARRAY_SIZE(scene_configs)) -+ return -EINVAL; -+ -+ /* Set all the values */ -+ dev->scene_mode = ctrl->val; -+ -+ if (scene->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF) -+ shutter_speed = dev->manual_shutter_speed; -+ else -+ shutter_speed = 0; -+ exposure_mode = scene->exposure_mode; -+ metering_mode = scene->metering_mode; -+ -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", -+ __func__, shutter_speed, exposure_mode, metering_mode); -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &shutter_speed, -+ sizeof(shutter_speed)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exposure_mode, -+ sizeof(u32)); -+ dev->exposure_mode_active = exposure_mode; -+ ret += vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &exposure_mode, -+ sizeof(u32)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, control, -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &metering_mode, -+ sizeof(u32)); -+ ret += set_framerate_params(dev); -+ } -+ if (ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "%s: Setting scene to %d, ret=%d\n", -+ __func__, ctrl->val, ret); -+ ret = -EINVAL; -+ } -+ return 0; -+} -+ - static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) - { - struct bm2835_mmal_dev *dev = - container_of(ctrl->handler, struct bm2835_mmal_dev, - ctrl_handler); - const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; -+ int ret; - - if ((mmal_ctrl == NULL) || - (mmal_ctrl->id != ctrl->id) || -@@ -524,7 +906,13 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) - return -EINVAL; - } - -- return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+ ret = mmal_ctrl->setter(dev, ctrl, mmal_ctrl); -+ if (ret) -+ pr_warn("ctrl id:%d/MMAL param %08X- returned ret %d\n", -+ ctrl->id, mmal_ctrl->mmal_id, ret); -+ if (mmal_ctrl->ignore_errors) -+ ret = 0; -+ return ret; - } - - static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { -@@ -537,40 +925,54 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { - { - V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_SATURATION, &ctrl_set_rational -+ MMAL_PARAMETER_SATURATION, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational -+ MMAL_PARAMETER_SHARPNESS, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, - -100, 100, 0, 1, NULL, -- MMAL_PARAMETER_CONTRAST, &ctrl_set_rational -+ MMAL_PARAMETER_CONTRAST, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, - 0, 100, 50, 1, NULL, -- MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational -+ MMAL_PARAMETER_BRIGHTNESS, -+ &ctrl_set_rational, -+ false - }, - { - V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, - 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, -- MMAL_PARAMETER_ISO, &ctrl_set_value -+ MMAL_PARAMETER_ISO, -+ &ctrl_set_value_menu, -+ false - }, - { - V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value -+ MMAL_PARAMETER_VIDEO_STABILISATION, -+ &ctrl_set_value, -+ false - }, - /* { - 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL -- }, --*/ { -+ }, */ -+ { - V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, - ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, -- MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure -+ MMAL_PARAMETER_EXPOSURE_MODE, -+ &ctrl_set_exposure, -+ false - }, - /* todo this needs mixing in with set exposure - { -@@ -578,83 +980,258 @@ static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { - }, - */ - { -+ V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, -+ /* Units of 100usecs */ -+ 1, 1*1000*10, 100*10, 1, NULL, -+ MMAL_PARAMETER_SHUTTER_SPEED, -+ &ctrl_set_exposure, -+ false -+ }, -+ { - V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, - 0, ARRAY_SIZE(ev_bias_qmenu) - 1, - (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, -- MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value -+ MMAL_PARAMETER_EXPOSURE_COMP, -+ &ctrl_set_value_ev, -+ false -+ }, -+ { -+ V4L2_CID_EXPOSURE_AUTO_PRIORITY, MMAL_CONTROL_TYPE_STD, -+ 0, 1, -+ 0, 1, NULL, -+ 0, /* Dummy MMAL ID as it gets mapped into FPS range*/ -+ &ctrl_set_exposure, -+ false - }, - { - V4L2_CID_EXPOSURE_METERING, - MMAL_CONTROL_TYPE_STD_MENU, - ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, -- MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode -+ MMAL_PARAMETER_EXP_METERING_MODE, -+ &ctrl_set_metering_mode, -+ false - }, - { - V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, - MMAL_CONTROL_TYPE_STD_MENU, -- ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -- MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode -+ ~0x3ff, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, -+ MMAL_PARAMETER_AWB_MODE, -+ &ctrl_set_awb_mode, -+ false -+ }, -+ { -+ V4L2_CID_RED_BALANCE, MMAL_CONTROL_TYPE_STD, -+ 1, 7999, 1000, 1, NULL, -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ &ctrl_set_awb_gains, -+ false -+ }, -+ { -+ V4L2_CID_BLUE_BALANCE, MMAL_CONTROL_TYPE_STD, -+ 1, 7999, 1000, 1, NULL, -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, -+ &ctrl_set_awb_gains, -+ false - }, - { - V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, - 0, 15, V4L2_COLORFX_NONE, 0, NULL, -- MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect -+ MMAL_PARAMETER_IMAGE_EFFECT, -+ &ctrl_set_image_effect, -+ false - }, - { - V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, - 0, 0xffff, 0x8080, 1, NULL, -- MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx -+ MMAL_PARAMETER_COLOUR_EFFECT, -+ &ctrl_set_colfx, -+ false - }, - { - V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, - 0, 360, 0, 90, NULL, -- MMAL_PARAMETER_ROTATION, &ctrl_set_rotate -+ MMAL_PARAMETER_ROTATION, -+ &ctrl_set_rotate, -+ false - }, - { - V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ MMAL_PARAMETER_MIRROR, -+ &ctrl_set_flip, -+ false - }, - { - V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, - 0, 1, 0, 1, NULL, -- MMAL_PARAMETER_MIRROR, &ctrl_set_flip -+ MMAL_PARAMETER_MIRROR, -+ &ctrl_set_flip, -+ false - }, - { - V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, - 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, - 0, 0, bitrate_mode_qmenu, -- MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode -+ MMAL_PARAMETER_RATECONTROL, -+ &ctrl_set_bitrate_mode, -+ false - }, - { - V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, - 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, -- MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate -+ MMAL_PARAMETER_VIDEO_BIT_RATE, -+ &ctrl_set_bitrate, -+ false - }, - { - V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, -- 0, 100, -+ 1, 100, - 30, 1, NULL, -- MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor -+ MMAL_PARAMETER_JPEG_Q_FACTOR, -+ &ctrl_set_image_encode_output, -+ false -+ }, -+ { -+ V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, -+ 0, ARRAY_SIZE(mains_freq_qmenu) - 1, -+ 1, 1, NULL, -+ MMAL_PARAMETER_FLICKER_AVOID, -+ &ctrl_set_flicker_avoidance, -+ false -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, MMAL_CONTROL_TYPE_STD, -+ 0, 1, -+ 0, 1, NULL, -+ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, -+ &ctrl_set_video_encode_param_output, -+ true /* Errors ignored as requires latest firmware to work */ -+ }, -+ { -+ V4L2_CID_MPEG_VIDEO_H264_PROFILE, -+ MMAL_CONTROL_TYPE_STD_MENU, -+ ~((1<ctrls[c]) && (v4l2_ctrls[c].setter)) { - ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], - &v4l2_ctrls[c]); -- if (ret) -+ if (!v4l2_ctrls[c].ignore_errors && ret) { -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Failed when setting default values for ctrl %d\n", -+ c); - break; ++ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) { ++ /* Send a PAUSE in response */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK) ++ == VCHIQ_RETRY) ++ goto bail_not_ready; ++ if (state->is_master) ++ pause_bulks(state); + } - } - } - return ret; - } - -+int set_framerate_params(struct bm2835_mmal_dev *dev) -+{ -+ struct mmal_parameter_fps_range fps_range; -+ int ret; ++ /* At this point slot_mutex is held */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); ++ vchiq_platform_paused(state); ++ break; ++ case VCHIQ_MSG_RESUME: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: prs RESUME@%x,%x", ++ state->id, (unsigned int)header, size); ++ /* Release the slot mutex */ ++ mutex_unlock(&state->slot_mutex); ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ break; + -+ if ((dev->exposure_mode_active != MMAL_PARAM_EXPOSUREMODE_OFF) && -+ (dev->exp_auto_priority)) { -+ /* Variable FPS. Define min FPS as 1fps. -+ * Max as max defined FPS. -+ */ -+ fps_range.fps_low.num = 1; -+ fps_range.fps_low.den = 1; -+ fps_range.fps_high.num = dev->capture.timeperframe.denominator; -+ fps_range.fps_high.den = dev->capture.timeperframe.numerator; -+ } else { -+ /* Fixed FPS - set min and max to be the same */ -+ fps_range.fps_low.num = fps_range.fps_high.num = -+ dev->capture.timeperframe.denominator; -+ fps_range.fps_low.den = fps_range.fps_high.den = -+ dev->capture.timeperframe.numerator; ++ case VCHIQ_MSG_REMOTE_USE: ++ vchiq_on_remote_use(state); ++ break; ++ case VCHIQ_MSG_REMOTE_RELEASE: ++ vchiq_on_remote_release(state); ++ break; ++ case VCHIQ_MSG_REMOTE_USE_ACTIVE: ++ vchiq_on_remote_use_active(state); ++ break; ++ ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: prs invalid msgid %x@%x,%x", ++ state->id, msgid, (unsigned int)header, size); ++ WARN(1, "invalid message\n"); ++ break; ++ } ++ ++skip_message: ++ if (service) { ++ unlock_service(service); ++ service = NULL; ++ } ++ ++ state->rx_pos += calc_stride(size); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ /* Perform some housekeeping when the end of the slot is ++ ** reached. */ ++ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) { ++ /* Remove the extra reference count. */ ++ release_slot(state, state->rx_info, NULL, NULL); ++ state->rx_data = NULL; ++ } + } + -+ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Set fps range to %d/%d to %d/%d\n", -+ fps_range.fps_low.num, -+ fps_range.fps_low.den, -+ fps_range.fps_high.num, -+ fps_range.fps_high.den -+ ); -+ -+ ret = vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_PREVIEW], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_VIDEO], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ ret += vchiq_mmal_port_parameter_set(dev->instance, -+ &dev->component[MMAL_COMPONENT_CAMERA]-> -+ output[MMAL_CAMERA_PORT_CAPTURE], -+ MMAL_PARAMETER_FPS_RANGE, -+ &fps_range, sizeof(fps_range)); -+ if (ret) -+ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, -+ "Failed to set fps ret %d\n", -+ ret); -+ -+ return ret; -+ ++bail_not_ready: ++ if (service) ++ unlock_service(service); +} + - int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, - struct v4l2_ctrl_handler *hdl) - { -@@ -674,10 +1251,30 @@ int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, - break; - - case MMAL_CONTROL_TYPE_STD_MENU: -+ { -+ int mask = ctrl->min; ++/* Called by the slot handler thread */ ++static int ++slot_handler_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ DEBUG_INITIALISE(local) + -+ if (ctrl->id == V4L2_CID_SCENE_MODE) { -+ /* Special handling to work out the mask -+ * value based on the scene_configs array -+ * at runtime. Reduces the chance of -+ * mismatches. -+ */ -+ int i; -+ mask = 1<trigger); ++ ++ rmb(); ++ ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ if (state->poll_needed) { ++ /* Check if we need to suspend - may change our ++ * conn_state */ ++ vchiq_platform_check_suspend(state); ++ ++ state->poll_needed = 0; ++ ++ /* Handle service polling and other rare conditions here ++ ** out of the mainline code */ ++ switch (state->conn_state) { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Poll the services as requested */ ++ poll_services(state); ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSING: ++ if (state->is_master) ++ pause_bulks(state); ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), ++ NULL, 0, 0, ++ QMFLAGS_NO_MUTEX_UNLOCK) ++ != VCHIQ_RETRY) { ++ vchiq_set_conn_state(state, ++ VCHIQ_CONNSTATE_PAUSE_SENT); ++ } else { ++ if (state->is_master) ++ resume_bulks(state); ++ /* Retry later */ ++ state->poll_needed = 1; + } -+ mask = ~mask; ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSED: ++ vchiq_platform_resume(state); ++ break; ++ ++ case VCHIQ_CONNSTATE_RESUMING: ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK) ++ != VCHIQ_RETRY) { ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, ++ VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ } else { ++ /* This should really be impossible, ++ ** since the PAUSE should have flushed ++ ** through outstanding messages. */ ++ vchiq_log_error(vchiq_core_log_level, ++ "Failed to send RESUME " ++ "message"); ++ BUG(); ++ } ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSE_TIMEOUT: ++ case VCHIQ_CONNSTATE_RESUME_TIMEOUT: ++ vchiq_platform_handle_timeout(state); ++ break; ++ default: ++ break; + } + - dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, - &bm2835_mmal_ctrl_ops, ctrl->id, -- ctrl->max, ctrl->min, ctrl->def); -+ ctrl->max, mask, ctrl->def); - break; ++ + } - - case MMAL_CONTROL_TYPE_INT_MENU: - dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, -diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h -index 84abbb3..35698c8 100644 ---- a/drivers/media/platform/bcm2835/mmal-common.h -+++ b/drivers/media/platform/bcm2835/mmal-common.h -@@ -26,6 +26,7 @@ - struct mmal_fmt { - char *name; - u32 fourcc; /* v4l2 format id */ -+ int flags; /* v4l2 flags field */ - u32 mmal; - int depth; - u32 mmal_component; /* MMAL component index to be used to encode */ -diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h -index 856e80e..024d620 100644 ---- a/drivers/media/platform/bcm2835/mmal-encodings.h -+++ b/drivers/media/platform/bcm2835/mmal-encodings.h -@@ -12,6 +12,8 @@ - * Simon Mellor - * Luke Diamand - */ -+#ifndef MMAL_ENCODINGS_H -+#define MMAL_ENCODINGS_H - - #define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') - #define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') -@@ -27,6 +29,7 @@ - #define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') - #define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') - #define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') -+#define MMAL_ENCODING_MJPEG MMAL_FOURCC('M', 'J', 'P', 'G') - - #define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') - #define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') -@@ -91,3 +94,34 @@ - #define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') - /** Implicitly delineated NAL units without emulation prevention */ - #define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') + -+ -+/** \defgroup MmalColorSpace List of pre-defined video color spaces -+ * This defines a list of common color spaces. This list isn't exhaustive and -+ * is only provided as a convenience to avoid clients having to use FourCC -+ * codes directly. However components are allowed to define and use their own -+ * FourCC codes. -+ */ -+/* @{ */ -+ -+/** Unknown color space */ -+#define MMAL_COLOR_SPACE_UNKNOWN 0 -+/** ITU-R BT.601-5 [SDTV] */ -+#define MMAL_COLOR_SPACE_ITUR_BT601 MMAL_FOURCC('Y', '6', '0', '1') -+/** ITU-R BT.709-3 [HDTV] */ -+#define MMAL_COLOR_SPACE_ITUR_BT709 MMAL_FOURCC('Y', '7', '0', '9') -+/** JPEG JFIF */ -+#define MMAL_COLOR_SPACE_JPEG_JFIF MMAL_FOURCC('Y', 'J', 'F', 'I') -+/** Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */ -+#define MMAL_COLOR_SPACE_FCC MMAL_FOURCC('Y', 'F', 'C', 'C') -+/** Society of Motion Picture and Television Engineers 240M (1999) */ -+#define MMAL_COLOR_SPACE_SMPTE240M MMAL_FOURCC('Y', '2', '4', '0') -+/** ITU-R BT.470-2 System M */ -+#define MMAL_COLOR_SPACE_BT470_2_M MMAL_FOURCC('Y', '_', '_', 'M') -+/** ITU-R BT.470-2 System BG */ -+#define MMAL_COLOR_SPACE_BT470_2_BG MMAL_FOURCC('Y', '_', 'B', 'G') -+/** JPEG JFIF, but with 16..255 luma */ -+#define MMAL_COLOR_SPACE_JFIF_Y16_255 MMAL_FOURCC('Y', 'Y', '1', '6') -+/* @} MmalColorSpace List */ -+ -+#endif /* MMAL_ENCODINGS_H */ -diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h -index c611b58..aa0fd18 100644 ---- a/drivers/media/platform/bcm2835/mmal-parameters.h -+++ b/drivers/media/platform/bcm2835/mmal-parameters.h -@@ -57,7 +57,8 @@ enum mmal_parameter_common_type { - MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ - MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ -- MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ -+ MMAL_PARAMETER_SYSTEM_TIME, /**< MMAL_PARAMETER_UINT64_T */ -+ MMAL_PARAMETER_NO_IMAGE_PADDING /**< MMAL_PARAMETER_BOOLEAN_T */ - }; - - /* camera parameters */ -@@ -161,6 +162,13 @@ enum mmal_parameter_camera_type { - MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_SHUTTER_SPEED, /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ -+ MMAL_PARAMETER_CUSTOM_AWB_GAINS, /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ -+}; -+ -+struct mmal_parameter_rational { -+ s32 num; /**< Numerator */ -+ s32 den; /**< Denominator */ - }; - - enum mmal_parameter_camera_config_timestamp_mode { -@@ -176,6 +184,14 @@ enum mmal_parameter_camera_config_timestamp_mode { - */ - }; - -+struct mmal_parameter_fps_range { -+ /**< Low end of the permitted framerate range */ -+ struct mmal_parameter_rational fps_low; -+ /**< High end of the permitted framerate range */ -+ struct mmal_parameter_rational fps_high; -+}; -+ -+ - /* camera configuration parameter */ - struct mmal_parameter_camera_config { - /* Parameters for setting up the image pools */ -@@ -270,6 +286,19 @@ enum mmal_parameter_imagefx { - MMAL_PARAM_IMAGEFX_CARTOON, - }; - -+enum MMAL_PARAM_FLICKERAVOID_T { -+ MMAL_PARAM_FLICKERAVOID_OFF, -+ MMAL_PARAM_FLICKERAVOID_AUTO, -+ MMAL_PARAM_FLICKERAVOID_50HZ, -+ MMAL_PARAM_FLICKERAVOID_60HZ, -+ MMAL_PARAM_FLICKERAVOID_MAX = 0x7FFFFFFF -+}; -+ -+struct mmal_parameter_awbgains { -+ struct mmal_parameter_rational r_gain; /**< Red gain */ -+ struct mmal_parameter_rational b_gain; /**< Blue gain */ -+}; -+ - /** Manner of video rate control */ - enum mmal_parameter_rate_control_mode { - MMAL_VIDEO_RATECONTROL_DEFAULT, -@@ -279,6 +308,85 @@ enum mmal_parameter_rate_control_mode { - MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES - }; - -+enum mmal_video_profile { -+ MMAL_VIDEO_PROFILE_H263_BASELINE, -+ MMAL_VIDEO_PROFILE_H263_H320CODING, -+ MMAL_VIDEO_PROFILE_H263_BACKWARDCOMPATIBLE, -+ MMAL_VIDEO_PROFILE_H263_ISWV2, -+ MMAL_VIDEO_PROFILE_H263_ISWV3, -+ MMAL_VIDEO_PROFILE_H263_HIGHCOMPRESSION, -+ MMAL_VIDEO_PROFILE_H263_INTERNET, -+ MMAL_VIDEO_PROFILE_H263_INTERLACE, -+ MMAL_VIDEO_PROFILE_H263_HIGHLATENCY, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLESCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_CORE, -+ MMAL_VIDEO_PROFILE_MP4V_MAIN, -+ MMAL_VIDEO_PROFILE_MP4V_NBIT, -+ MMAL_VIDEO_PROFILE_MP4V_SCALABLETEXTURE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFACE, -+ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFBA, -+ MMAL_VIDEO_PROFILE_MP4V_BASICANIMATED, -+ MMAL_VIDEO_PROFILE_MP4V_HYBRID, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDREALTIME, -+ MMAL_VIDEO_PROFILE_MP4V_CORESCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCODING, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCORE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSCALABLE, -+ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSIMPLE, -+ MMAL_VIDEO_PROFILE_H264_BASELINE, -+ MMAL_VIDEO_PROFILE_H264_MAIN, -+ MMAL_VIDEO_PROFILE_H264_EXTENDED, -+ MMAL_VIDEO_PROFILE_H264_HIGH, -+ MMAL_VIDEO_PROFILE_H264_HIGH10, -+ MMAL_VIDEO_PROFILE_H264_HIGH422, -+ MMAL_VIDEO_PROFILE_H264_HIGH444, -+ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, -+ MMAL_VIDEO_PROFILE_DUMMY = 0x7FFFFFFF -+}; -+ -+enum mmal_video_level { -+ MMAL_VIDEO_LEVEL_H263_10, -+ MMAL_VIDEO_LEVEL_H263_20, -+ MMAL_VIDEO_LEVEL_H263_30, -+ MMAL_VIDEO_LEVEL_H263_40, -+ MMAL_VIDEO_LEVEL_H263_45, -+ MMAL_VIDEO_LEVEL_H263_50, -+ MMAL_VIDEO_LEVEL_H263_60, -+ MMAL_VIDEO_LEVEL_H263_70, -+ MMAL_VIDEO_LEVEL_MP4V_0, -+ MMAL_VIDEO_LEVEL_MP4V_0b, -+ MMAL_VIDEO_LEVEL_MP4V_1, -+ MMAL_VIDEO_LEVEL_MP4V_2, -+ MMAL_VIDEO_LEVEL_MP4V_3, -+ MMAL_VIDEO_LEVEL_MP4V_4, -+ MMAL_VIDEO_LEVEL_MP4V_4a, -+ MMAL_VIDEO_LEVEL_MP4V_5, -+ MMAL_VIDEO_LEVEL_MP4V_6, -+ MMAL_VIDEO_LEVEL_H264_1, -+ MMAL_VIDEO_LEVEL_H264_1b, -+ MMAL_VIDEO_LEVEL_H264_11, -+ MMAL_VIDEO_LEVEL_H264_12, -+ MMAL_VIDEO_LEVEL_H264_13, -+ MMAL_VIDEO_LEVEL_H264_2, -+ MMAL_VIDEO_LEVEL_H264_21, -+ MMAL_VIDEO_LEVEL_H264_22, -+ MMAL_VIDEO_LEVEL_H264_3, -+ MMAL_VIDEO_LEVEL_H264_31, -+ MMAL_VIDEO_LEVEL_H264_32, -+ MMAL_VIDEO_LEVEL_H264_4, -+ MMAL_VIDEO_LEVEL_H264_41, -+ MMAL_VIDEO_LEVEL_H264_42, -+ MMAL_VIDEO_LEVEL_H264_5, -+ MMAL_VIDEO_LEVEL_H264_51, -+ MMAL_VIDEO_LEVEL_DUMMY = 0x7FFFFFFF -+}; -+ -+struct mmal_parameter_video_profile { -+ enum mmal_video_profile profile; -+ enum mmal_video_level level; -+}; -+ - /* video parameters */ - - enum mmal_parameter_video_type { -@@ -407,7 +515,16 @@ enum mmal_parameter_video_type { - MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, - - /** @ref MMAL_PARAMETER_BYTES_T */ -- MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 -+ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_VCL_HRD_PARAMETERS, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, -+ -+ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ -+ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER - }; - - /** Valid mirror modes */ -diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c -index a06fb44..76f249e 100644 ---- a/drivers/media/platform/bcm2835/mmal-vchiq.c -+++ b/drivers/media/platform/bcm2835/mmal-vchiq.c -@@ -742,7 +742,7 @@ static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, - return ret; - } - -- ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); -+ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, 3*HZ); - if (ret <= 0) { - pr_err("error %d waiting for sync completion\n", ret); - if (ret == 0) -@@ -1326,7 +1326,7 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, - memcpy(value, &rmsg->u.port_parameter_get_reply.value, - rmsg->u.port_parameter_get_reply.size); - -- pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, -+ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, - ret, port->component->handle, port->handle, parameter_id); - - release_msg: - -From 62a271e1a6bbd67590b5f7a03cc0f23ec00c3e1c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:22:53 +0100 -Subject: [PATCH 030/172] dmaengine: Add support for BCM2708 - -Add support for DMA controller of BCM2708 as used in the Raspberry Pi. -Currently it only supports cyclic DMA. - -Signed-off-by: Florian Meier - -dmaengine: expand functionality by supporting scatter/gather transfers sdhci-bcm2708 and dma.c: fix for LITE channels - -DMA: fix cyclic LITE length overflow bug ---- - arch/arm/mach-bcm2708/dma.c | 2 + - arch/arm/mach-bcm2708/include/mach/dma.h | 6 +- - drivers/dma/Kconfig | 6 + - drivers/dma/Makefile | 1 + - drivers/dma/bcm2708-dmaengine.c | 1052 ++++++++++++++++++++++++++++++ - 5 files changed, 1066 insertions(+), 1 deletion(-) - create mode 100644 drivers/dma/bcm2708-dmaengine.c - -diff --git a/arch/arm/mach-bcm2708/dma.c b/arch/arm/mach-bcm2708/dma.c -index 1da2413..a5e58d1 100644 ---- a/arch/arm/mach-bcm2708/dma.c -+++ b/arch/arm/mach-bcm2708/dma.c -@@ -156,6 +156,8 @@ static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, - dmaman->chan_available = chans_available; - dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ - dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ - } - - static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, -diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h -index a4aac4c..d03e7b5 100644 ---- a/arch/arm/mach-bcm2708/include/mach/dma.h -+++ b/arch/arm/mach-bcm2708/include/mach/dma.h -@@ -77,9 +77,13 @@ extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); - those with higher priority smaller ordinal numbers */ - #define BCM_DMA_FEATURE_FAST_ORD 0 - #define BCM_DMA_FEATURE_BULK_ORD 1 -+#define BCM_DMA_FEATURE_NORMAL_ORD 2 -+#define BCM_DMA_FEATURE_LITE_ORD 3 - #define BCM_DMA_FEATURE_FAST (1< -+ * Gellert Weisz -+ * Copyright 2013-2014 -+ * -+ * Based on -+ * OMAP DMAengine support by Russell King -+ * -+ * BCM2708 DMA Driver -+ * Copyright (C) 2010 Broadcom -+ * -+ * Raspberry Pi PCM I2S ALSA Driver -+ * Copyright (c) by Phil Poole 2013 -+ * -+ * MARVELL MMP Peripheral DMA Driver -+ * Copyright 2012 Marvell International Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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 -+#include -+#include -+#include -+#include -+ -+#ifndef CONFIG_OF -+ -+/* dma manager */ -+#include -+ -+//#define DMA_COMPLETE DMA_SUCCESS -+ -+#endif -+ -+#include -+#include -+ -+#include "virt-dma.h" -+ -+ -+struct bcm2835_dmadev { -+ struct dma_device ddev; -+ spinlock_t lock; -+ void __iomem *base; -+ struct device_dma_parameters dma_parms; -+}; -+ -+struct bcm2835_dma_cb { -+ uint32_t info; -+ uint32_t src; -+ uint32_t dst; -+ uint32_t length; -+ uint32_t stride; -+ uint32_t next; -+ uint32_t pad[2]; -+}; -+ -+struct bcm2835_chan { -+ struct virt_dma_chan vc; -+ struct list_head node; -+ -+ struct dma_slave_config cfg; -+ bool cyclic; -+ -+ int ch; -+ struct bcm2835_desc *desc; -+ -+ void __iomem *chan_base; -+ int irq_number; -+ -+ unsigned int dreq; -+}; -+ -+struct bcm2835_desc { -+ struct virt_dma_desc vd; -+ enum dma_transfer_direction dir; -+ -+ unsigned int control_block_size; -+ struct bcm2835_dma_cb *control_block_base; -+ dma_addr_t control_block_base_phys; -+ -+ unsigned int frames; -+ size_t size; -+}; -+ -+#define BCM2835_DMA_CS 0x00 -+#define BCM2835_DMA_ADDR 0x04 -+#define BCM2835_DMA_SOURCE_AD 0x0c -+#define BCM2835_DMA_DEST_AD 0x10 -+#define BCM2835_DMA_NEXTCB 0x1C -+ -+/* DMA CS Control and Status bits */ -+#define BCM2835_DMA_ACTIVE BIT(0) -+#define BCM2835_DMA_INT BIT(2) -+#define BCM2835_DMA_ISPAUSED BIT(4) /* Pause requested or not active */ -+#define BCM2835_DMA_ISHELD BIT(5) /* Is held by DREQ flow control */ -+#define BCM2835_DMA_ERR BIT(8) -+#define BCM2835_DMA_ABORT BIT(30) /* Stop current CB, go to next, WO */ -+#define BCM2835_DMA_RESET BIT(31) /* WO, self clearing */ -+ -+#define BCM2835_DMA_INT_EN BIT(0) -+#define BCM2835_DMA_WAIT_RESP BIT(3) -+#define BCM2835_DMA_D_INC BIT(4) -+#define BCM2835_DMA_D_WIDTH BIT(5) -+#define BCM2835_DMA_D_DREQ BIT(6) -+#define BCM2835_DMA_S_INC BIT(8) -+#define BCM2835_DMA_S_WIDTH BIT(9) -+#define BCM2835_DMA_S_DREQ BIT(10) -+ -+#define BCM2835_DMA_PER_MAP(x) ((x) << 16) -+#define BCM2835_DMA_WAITS(x) (((x)&0x1f) << 21) -+ -+#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ -+ -+#define BCM2835_DMA_DATA_TYPE_S8 1 -+#define BCM2835_DMA_DATA_TYPE_S16 2 -+#define BCM2835_DMA_DATA_TYPE_S32 4 -+#define BCM2835_DMA_DATA_TYPE_S128 16 -+ -+#define BCM2835_DMA_BULK_MASK BIT(0) -+#define BCM2835_DMA_FIQ_MASK (BIT(2) | BIT(3)) -+ -+ -+/* Valid only for channels 0 - 14, 15 has its own base address */ -+#define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */ -+#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n)) -+ -+#define MAX_LITE_TRANSFER 32768 -+#define MAX_NORMAL_TRANSFER 1073741824 -+ -+static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d) -+{ -+ return container_of(d, struct bcm2835_dmadev, ddev); -+} -+ -+static inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c) -+{ -+ return container_of(c, struct bcm2835_chan, vc.chan); -+} -+ -+static inline struct bcm2835_desc *to_bcm2835_dma_desc( -+ struct dma_async_tx_descriptor *t) -+{ -+ return container_of(t, struct bcm2835_desc, vd.tx); -+} -+ -+static void dma_dumpregs(struct bcm2835_chan *c) -+{ -+ pr_debug("-------------DMA DUMPREGS-------------\n"); -+ pr_debug("CS= %u\n", -+ readl(c->chan_base + BCM2835_DMA_CS)); -+ pr_debug("ADDR= %u\n", -+ readl(c->chan_base + BCM2835_DMA_ADDR)); -+ pr_debug("SOURCE_ADDR= %u\n", -+ readl(c->chan_base + BCM2835_DMA_SOURCE_AD)); -+ pr_debug("DEST_AD= %u\n", -+ readl(c->chan_base + BCM2835_DMA_DEST_AD)); -+ pr_debug("NEXTCB= %u\n", -+ readl(c->chan_base + BCM2835_DMA_NEXTCB)); -+ pr_debug("--------------------------------------\n"); -+} -+ -+static void bcm2835_dma_desc_free(struct virt_dma_desc *vd) -+{ -+ struct bcm2835_desc *desc = container_of(vd, struct bcm2835_desc, vd); -+ dma_free_coherent(desc->vd.tx.chan->device->dev, -+ desc->control_block_size, -+ desc->control_block_base, -+ desc->control_block_base_phys); -+ kfree(desc); -+} -+ -+static int bcm2835_dma_abort(void __iomem *chan_base) -+{ -+ unsigned long cs; -+ long int timeout = 10000; -+ -+ cs = readl(chan_base + BCM2835_DMA_CS); -+ if (!(cs & BCM2835_DMA_ACTIVE)) -+ return 0; -+ -+ /* Write 0 to the active bit - Pause the DMA */ -+ writel(0, chan_base + BCM2835_DMA_CS); -+ -+ /* Wait for any current AXI transfer to complete */ -+ while ((cs & BCM2835_DMA_ISPAUSED) && --timeout) { -+ cpu_relax(); -+ cs = readl(chan_base + BCM2835_DMA_CS); ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ parse_rx_slots(state); + } ++ return 0; ++} + -+ /* We'll un-pause when we set of our next DMA */ -+ if (!timeout) -+ return -ETIMEDOUT; + -+ if (!(cs & BCM2835_DMA_ACTIVE)) -+ return 0; ++/* Called by the recycle thread */ ++static int ++recycle_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; + -+ /* Terminate the control block chain */ -+ writel(0, chan_base + BCM2835_DMA_NEXTCB); ++ while (1) { ++ remote_event_wait(&local->recycle); + -+ /* Abort the whole DMA */ -+ writel(BCM2835_DMA_ABORT | BCM2835_DMA_ACTIVE, -+ chan_base + BCM2835_DMA_CS); ++ process_free_queue(state); ++ } ++ return 0; ++} ++ ++ ++/* Called by the sync thread */ ++static int ++sync_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ state->remote->slot_sync); ++ ++ while (1) { ++ VCHIQ_SERVICE_T *service; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; ++ ++ remote_event_wait(&local->sync_trigger); ++ ++ rmb(); ++ ++ msgid = header->msgid; ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); ++ ++ service = find_service_by_port(state, localport); ++ ++ if (!service) { ++ vchiq_log_error(vchiq_sync_log_level, ++ "%d: sf %s@%x (%d->%d) - " ++ "invalid/closed service %d", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, localport); ++ release_message_sync(state, header); ++ continue; ++ } ++ ++ if (vchiq_sync_log_level >= VCHIQ_LOG_TRACE) { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?', '?', '?', '?'); ++ vchiq_log_trace(vchiq_sync_log_level, ++ "Rcvd Msg %s from %c%c%c%c s:%d d:%d len:%d", ++ msg_type_str(type), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ remoteport, localport, size); ++ if (size > 0) ++ vchiq_log_dump_mem("Rcvd", 0, header->data, ++ min(64, size)); ++ } ++ ++ switch (type) { ++ case VCHIQ_MSG_OPENACK: ++ if (size >= sizeof(struct vchiq_openack_payload)) { ++ const struct vchiq_openack_payload *payload = ++ (struct vchiq_openack_payload *) ++ header->data; ++ service->peer_version = payload->version; ++ } ++ vchiq_log_info(vchiq_sync_log_level, ++ "%d: sf OPENACK@%x,%x (%d->%d) v:%d", ++ state->id, (unsigned int)header, size, ++ remoteport, localport, service->peer_version); ++ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { ++ service->remoteport = remoteport; ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_OPENSYNC); ++ service->sync = 1; ++ up(&service->remove_event); ++ } ++ release_message_sync(state, header); ++ break; ++ ++ case VCHIQ_MSG_DATA: ++ vchiq_log_trace(vchiq_sync_log_level, ++ "%d: sf DATA@%x,%x (%d->%d)", ++ state->id, (unsigned int)header, size, ++ remoteport, localport); ++ ++ if ((service->remoteport == remoteport) && ++ (service->srvstate == ++ VCHIQ_SRVSTATE_OPENSYNC)) { ++ if (make_service_callback(service, ++ VCHIQ_MESSAGE_AVAILABLE, header, ++ NULL) == VCHIQ_RETRY) ++ vchiq_log_error(vchiq_sync_log_level, ++ "synchronous callback to " ++ "service %d returns " ++ "VCHIQ_RETRY", ++ localport); ++ } ++ break; ++ ++ default: ++ vchiq_log_error(vchiq_sync_log_level, ++ "%d: sf unexpected msgid %x@%x,%x", ++ state->id, msgid, (unsigned int)header, size); ++ release_message_sync(state, header); ++ break; ++ } ++ ++ unlock_service(service); ++ } + + return 0; +} + + -+static void bcm2835_dma_start_desc(struct bcm2835_chan *c) ++static void ++init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) +{ -+ struct virt_dma_desc *vd = vchan_next_desc(&c->vc); -+ struct bcm2835_desc *d; ++ queue->local_insert = 0; ++ queue->remote_insert = 0; ++ queue->process = 0; ++ queue->remote_notify = 0; ++ queue->remove = 0; ++} ++ ++ ++inline const char * ++get_conn_state_name(VCHIQ_CONNSTATE_T conn_state) ++{ ++ return conn_state_names[conn_state]; ++} ++ ++ ++VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size) ++{ ++ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK; ++ VCHIQ_SLOT_ZERO_T *slot_zero = ++ (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align); ++ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE; ++ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS; ++ ++ /* Ensure there is enough memory to run an absolutely minimum system */ ++ num_slots -= first_data_slot; ++ ++ if (num_slots < 4) { ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_init_slots - insufficient memory %x bytes", ++ mem_size); ++ return NULL; ++ } ++ ++ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T)); ++ ++ slot_zero->magic = VCHIQ_MAGIC; ++ slot_zero->version = VCHIQ_VERSION; ++ slot_zero->version_min = VCHIQ_VERSION_MIN; ++ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T); ++ slot_zero->slot_size = VCHIQ_SLOT_SIZE; ++ slot_zero->max_slots = VCHIQ_MAX_SLOTS; ++ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE; ++ ++ slot_zero->master.slot_sync = first_data_slot; ++ slot_zero->master.slot_first = first_data_slot + 1; ++ slot_zero->master.slot_last = first_data_slot + (num_slots/2) - 1; ++ slot_zero->slave.slot_sync = first_data_slot + (num_slots/2); ++ slot_zero->slave.slot_first = first_data_slot + (num_slots/2) + 1; ++ slot_zero->slave.slot_last = first_data_slot + num_slots - 1; ++ ++ return slot_zero; ++} ++ ++VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, ++ int is_master) ++{ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_STATUS_T status; ++ char threadname[10]; ++ static int id; ++ int i; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%s: slot_zero = 0x%08lx, is_master = %d", ++ __func__, (unsigned long)slot_zero, is_master); ++ ++ /* Check the input configuration */ ++ ++ if (slot_zero->magic != VCHIQ_MAGIC) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Invalid VCHIQ magic value found."); ++ vchiq_loud_error("slot_zero=%x: magic=%x (expected %x)", ++ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->version < VCHIQ_VERSION_MIN) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Incompatible VCHIQ versions found."); ++ vchiq_loud_error("slot_zero=%x: VideoCore version=%d " ++ "(minimum %d)", ++ (unsigned int)slot_zero, slot_zero->version, ++ VCHIQ_VERSION_MIN); ++ vchiq_loud_error("Restart with a newer VideoCore image."); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (VCHIQ_VERSION < slot_zero->version_min) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("Incompatible VCHIQ versions found."); ++ vchiq_loud_error("slot_zero=%x: version=%d (VideoCore " ++ "minimum %d)", ++ (unsigned int)slot_zero, VCHIQ_VERSION, ++ slot_zero->version_min); ++ vchiq_loud_error("Restart with a newer kernel."); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if ((slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) || ++ (slot_zero->slot_size != VCHIQ_SLOT_SIZE) || ++ (slot_zero->max_slots != VCHIQ_MAX_SLOTS) || ++ (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE)) { ++ vchiq_loud_error_header(); ++ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) ++ vchiq_loud_error("slot_zero=%x: slot_zero_size=%x " ++ "(expected %x)", ++ (unsigned int)slot_zero, ++ slot_zero->slot_zero_size, ++ sizeof(VCHIQ_SLOT_ZERO_T)); ++ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE) ++ vchiq_loud_error("slot_zero=%x: slot_size=%d " ++ "(expected %d", ++ (unsigned int)slot_zero, slot_zero->slot_size, ++ VCHIQ_SLOT_SIZE); ++ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS) ++ vchiq_loud_error("slot_zero=%x: max_slots=%d " ++ "(expected %d)", ++ (unsigned int)slot_zero, slot_zero->max_slots, ++ VCHIQ_MAX_SLOTS); ++ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE) ++ vchiq_loud_error("slot_zero=%x: max_slots_per_side=%d " ++ "(expected %d)", ++ (unsigned int)slot_zero, ++ slot_zero->max_slots_per_side, ++ VCHIQ_MAX_SLOTS_PER_SIDE); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ if (VCHIQ_VERSION < slot_zero->version) ++ slot_zero->version = VCHIQ_VERSION; ++ ++ if (is_master) { ++ local = &slot_zero->master; ++ remote = &slot_zero->slave; ++ } else { ++ local = &slot_zero->slave; ++ remote = &slot_zero->master; ++ } ++ ++ if (local->initialised) { ++ vchiq_loud_error_header(); ++ if (remote->initialised) ++ vchiq_loud_error("local state has already been " ++ "initialised"); ++ else ++ vchiq_loud_error("master/slave mismatch - two %ss", ++ is_master ? "master" : "slave"); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ ++ memset(state, 0, sizeof(VCHIQ_STATE_T)); ++ ++ state->id = id++; ++ state->is_master = is_master; ++ ++ /* ++ initialize shared state pointers ++ */ ++ ++ state->local = local; ++ state->remote = remote; ++ state->slot_data = (VCHIQ_SLOT_T *)slot_zero; ++ ++ /* ++ initialize events and mutexes ++ */ ++ ++ sema_init(&state->connect, 0); ++ mutex_init(&state->mutex); ++ sema_init(&state->trigger_event, 0); ++ sema_init(&state->recycle_event, 0); ++ sema_init(&state->sync_trigger_event, 0); ++ sema_init(&state->sync_release_event, 0); ++ ++ mutex_init(&state->slot_mutex); ++ mutex_init(&state->recycle_mutex); ++ mutex_init(&state->sync_mutex); ++ mutex_init(&state->bulk_transfer_mutex); ++ ++ sema_init(&state->slot_available_event, 0); ++ sema_init(&state->slot_remove_event, 0); ++ sema_init(&state->data_quota_event, 0); ++ ++ state->slot_queue_available = 0; ++ ++ for (i = 0; i < VCHIQ_MAX_SERVICES; i++) { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[i]; ++ sema_init(&service_quota->quota_event, 0); ++ } ++ ++ for (i = local->slot_first; i <= local->slot_last; i++) { ++ local->slot_queue[state->slot_queue_available++] = i; ++ up(&state->slot_available_event); ++ } ++ ++ state->default_slot_quota = state->slot_queue_available/2; ++ state->default_message_quota = ++ min((unsigned short)(state->default_slot_quota * 256), ++ (unsigned short)~0); ++ ++ state->previous_data_index = -1; ++ state->data_use_count = 0; ++ state->data_quota = state->slot_queue_available - 1; ++ ++ local->trigger.event = &state->trigger_event; ++ remote_event_create(&local->trigger); ++ local->tx_pos = 0; ++ ++ local->recycle.event = &state->recycle_event; ++ remote_event_create(&local->recycle); ++ local->slot_queue_recycle = state->slot_queue_available; ++ ++ local->sync_trigger.event = &state->sync_trigger_event; ++ remote_event_create(&local->sync_trigger); ++ ++ local->sync_release.event = &state->sync_release_event; ++ remote_event_create(&local->sync_release); ++ ++ /* At start-of-day, the slot is empty and available */ ++ ((VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, local->slot_sync))->msgid ++ = VCHIQ_MSGID_PADDING; ++ remote_event_signal_local(&local->sync_release); ++ ++ local->debug[DEBUG_ENTRIES] = DEBUG_MAX; ++ ++ status = vchiq_platform_init_state(state); ++ ++ /* ++ bring up slot handler thread ++ */ ++ snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); ++ state->slot_handler_thread = kthread_create(&slot_handler_func, ++ (void *)state, ++ threadname); ++ ++ if (state->slot_handler_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->slot_handler_thread, -19); ++ wake_up_process(state->slot_handler_thread); ++ ++ snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); ++ state->recycle_thread = kthread_create(&recycle_func, ++ (void *)state, ++ threadname); ++ if (state->recycle_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->recycle_thread, -19); ++ wake_up_process(state->recycle_thread); ++ ++ snprintf(threadname, sizeof(threadname), "VCHIQs-%d", state->id); ++ state->sync_thread = kthread_create(&sync_func, ++ (void *)state, ++ threadname); ++ if (state->sync_thread == NULL) { ++ vchiq_loud_error_header(); ++ vchiq_loud_error("couldn't create thread %s", threadname); ++ vchiq_loud_error_footer(); ++ return VCHIQ_ERROR; ++ } ++ set_user_nice(state->sync_thread, -20); ++ wake_up_process(state->sync_thread); ++ ++ BUG_ON(state->id >= VCHIQ_MAX_STATES); ++ vchiq_states[state->id] = state; ++ ++ /* Indicate readiness to the other side */ ++ local->initialised = 1; ++ ++ return status; ++} ++ ++/* Called from application thread when a client or server service is created. */ ++VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term) ++{ ++ VCHIQ_SERVICE_T *service; ++ ++ service = kmalloc(sizeof(VCHIQ_SERVICE_T), GFP_KERNEL); ++ if (service) { ++ service->base.fourcc = params->fourcc; ++ service->base.callback = params->callback; ++ service->base.userdata = params->userdata; ++ service->handle = VCHIQ_SERVICE_HANDLE_INVALID; ++ service->ref_count = 1; ++ service->srvstate = VCHIQ_SRVSTATE_FREE; ++ service->userdata_term = userdata_term; ++ service->localport = VCHIQ_PORT_FREE; ++ service->remoteport = VCHIQ_PORT_FREE; ++ ++ service->public_fourcc = (srvstate == VCHIQ_SRVSTATE_OPENING) ? ++ VCHIQ_FOURCC_INVALID : params->fourcc; ++ service->client_id = 0; ++ service->auto_close = 1; ++ service->sync = 0; ++ service->closing = 0; ++ service->trace = 0; ++ atomic_set(&service->poll_flags, 0); ++ service->version = params->version; ++ service->version_min = params->version_min; ++ service->state = state; ++ service->instance = instance; ++ service->service_use_count = 0; ++ init_bulk_queue(&service->bulk_tx); ++ init_bulk_queue(&service->bulk_rx); ++ sema_init(&service->remove_event, 0); ++ sema_init(&service->bulk_remove_event, 0); ++ mutex_init(&service->bulk_mutex); ++ memset(&service->stats, 0, sizeof(service->stats)); ++ } else { ++ vchiq_log_error(vchiq_core_log_level, ++ "Out of memory"); ++ } ++ ++ if (service) { ++ VCHIQ_SERVICE_T **pservice = NULL; ++ int i; ++ ++ /* Although it is perfectly possible to use service_spinlock ++ ** to protect the creation of services, it is overkill as it ++ ** disables interrupts while the array is searched. ++ ** The only danger is of another thread trying to create a ++ ** service - service deletion is safe. ++ ** Therefore it is preferable to use state->mutex which, ++ ** although slower to claim, doesn't block interrupts while ++ ** it is held. ++ */ ++ ++ mutex_lock(&state->mutex); ++ ++ /* Prepare to use a previously unused service */ ++ if (state->unused_service < VCHIQ_MAX_SERVICES) ++ pservice = &state->services[state->unused_service]; ++ ++ if (srvstate == VCHIQ_SRVSTATE_OPENING) { ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) { ++ pservice = &state->services[i]; ++ break; ++ } ++ } ++ } else { ++ for (i = (state->unused_service - 1); i >= 0; i--) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) ++ pservice = &state->services[i]; ++ else if ((srv->public_fourcc == params->fourcc) ++ && ((srv->instance != instance) || ++ (srv->base.callback != ++ params->callback))) { ++ /* There is another server using this ++ ** fourcc which doesn't match. */ ++ pservice = NULL; ++ break; ++ } ++ } ++ } ++ ++ if (pservice) { ++ service->localport = (pservice - state->services); ++ if (!handle_seq) ++ handle_seq = VCHIQ_MAX_STATES * ++ VCHIQ_MAX_SERVICES; ++ service->handle = handle_seq | ++ (state->id * VCHIQ_MAX_SERVICES) | ++ service->localport; ++ handle_seq += VCHIQ_MAX_STATES * VCHIQ_MAX_SERVICES; ++ *pservice = service; ++ if (pservice == &state->services[state->unused_service]) ++ state->unused_service++; ++ } ++ ++ mutex_unlock(&state->mutex); ++ ++ if (!pservice) { ++ kfree(service); ++ service = NULL; ++ } ++ } ++ ++ if (service) { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[service->localport]; ++ service_quota->slot_quota = state->default_slot_quota; ++ service_quota->message_quota = state->default_message_quota; ++ if (service_quota->slot_use_count == 0) ++ service_quota->previous_tx_index = ++ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) ++ - 1; ++ ++ /* Bring this service online */ ++ vchiq_set_service_state(service, srvstate); ++ ++ vchiq_log_info(vchiq_core_msg_log_level, ++ "%s Service %c%c%c%c SrcPort:%d", ++ (srvstate == VCHIQ_SRVSTATE_OPENING) ++ ? "Open" : "Add", ++ VCHIQ_FOURCC_AS_4CHARS(params->fourcc), ++ service->localport); ++ } ++ ++ /* Don't unlock the service - leave it with a ref_count of 1. */ ++ ++ return service; ++} ++ ++VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) ++{ ++ struct vchiq_open_payload payload = { ++ service->base.fourcc, ++ client_id, ++ service->version, ++ service->version_min ++ }; ++ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) }; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ service->client_id = client_id; ++ vchiq_use_service_internal(service); ++ status = queue_message(service->state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), ++ &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING); ++ if (status == VCHIQ_SUCCESS) { ++ /* Wait for the ACK/NAK */ ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ vchiq_release_service_internal(service); ++ } else if ((service->srvstate != VCHIQ_SRVSTATE_OPEN) && ++ (service->srvstate != VCHIQ_SRVSTATE_OPENSYNC)) { ++ if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: osi - srvstate = %s (ref %d)", ++ service->state->id, ++ srvstate_names[service->srvstate], ++ service->ref_count); ++ status = VCHIQ_ERROR; ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ vchiq_release_service_internal(service); ++ } ++ } ++ return status; ++} ++ ++static void ++release_service_messages(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int slot_last = state->remote->slot_last; ++ int i; ++ ++ /* Release any claimed messages aimed at this service */ ++ ++ if (service->sync) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, ++ state->remote->slot_sync); ++ if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport) ++ release_message_sync(state, header); + -+ if (!vd) { -+ c->desc = NULL; + return; + } + -+ list_del(&vd->node); ++ for (i = state->remote->slot_first; i <= slot_last; i++) { ++ VCHIQ_SLOT_INFO_T *slot_info = ++ SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info->release_count != slot_info->use_count) { ++ char *data = ++ (char *)SLOT_DATA_FROM_INDEX(state, i); ++ unsigned int pos, end; + -+ c->desc = d = to_bcm2835_dma_desc(&vd->tx); ++ end = VCHIQ_SLOT_SIZE; ++ if (data == state->rx_data) ++ /* This buffer is still being read from - stop ++ ** at the current read position */ ++ end = state->rx_pos & VCHIQ_SLOT_MASK; + -+ writel(d->control_block_base_phys, c->chan_base + BCM2835_DMA_ADDR); -+ writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS); -+ -+} -+ -+static irqreturn_t bcm2835_dma_callback(int irq, void *data) -+{ -+ struct bcm2835_chan *c = data; -+ struct bcm2835_desc *d; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ -+ /* Acknowledge interrupt */ -+ writel(BCM2835_DMA_INT, c->chan_base + BCM2835_DMA_CS); -+ -+ d = c->desc; -+ -+ if (d) { -+ if (c->cyclic) { -+ vchan_cyclic_callback(&d->vd); -+ -+ /* Keep the DMA engine running */ -+ writel(BCM2835_DMA_ACTIVE, -+ c->chan_base + BCM2835_DMA_CS); -+ -+ } else { -+ vchan_cookie_complete(&c->desc->vd); -+ bcm2835_dma_start_desc(c); -+ } -+ } -+ -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ -+ return IRQ_HANDLED; -+} -+ -+static int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ int ret; -+ -+ dev_dbg(c->vc.chan.device->dev, -+ "Allocating DMA channel %d\n", c->ch); -+ -+ ret = request_irq(c->irq_number, -+ bcm2835_dma_callback, 0, "DMA IRQ", c); -+ -+ return ret; -+} -+ -+static void bcm2835_dma_free_chan_resources(struct dma_chan *chan) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ -+ vchan_free_chan_resources(&c->vc); -+ free_irq(c->irq_number, c); -+ -+ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); -+} -+ -+static size_t bcm2835_dma_desc_size(struct bcm2835_desc *d) -+{ -+ return d->size; -+} -+ -+static size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr) -+{ -+ unsigned int i; -+ size_t size; -+ -+ for (size = i = 0; i < d->frames; i++) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[i]; -+ size_t this_size = control_block->length; -+ dma_addr_t dma; -+ -+ if (d->dir == DMA_DEV_TO_MEM) -+ dma = control_block->dst; -+ else -+ dma = control_block->src; -+ -+ if (size) -+ size += this_size; -+ else if (addr >= dma && addr < dma + this_size) -+ size += dma + this_size - addr; -+ } -+ -+ return size; -+} -+ -+static enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan, -+ dma_cookie_t cookie, struct dma_tx_state *txstate) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ struct bcm2835_desc *d; -+ struct virt_dma_desc *vd; -+ enum dma_status ret; -+ unsigned long flags; -+ dma_addr_t pos; -+ -+ ret = dma_cookie_status(chan, cookie, txstate); -+ if (ret == DMA_COMPLETE || !txstate) -+ return ret; -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ vd = vchan_find_desc(&c->vc, cookie); -+ if (vd) { -+ txstate->residue = -+ bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx)); -+ } else if (c->desc && c->desc->vd.tx.cookie == cookie) { -+ d = c->desc; -+ -+ if (d->dir == DMA_MEM_TO_DEV) -+ pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD); -+ else if (d->dir == DMA_DEV_TO_MEM) -+ pos = readl(c->chan_base + BCM2835_DMA_DEST_AD); -+ else + pos = 0; + -+ txstate->residue = bcm2835_dma_desc_size_pos(d, pos); -+ } else { -+ txstate->residue = 0; ++ while (pos < end) { ++ VCHIQ_HEADER_T *header = ++ (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ int port = VCHIQ_MSG_DSTPORT(msgid); ++ if ((port == service->localport) && ++ (msgid & VCHIQ_MSGID_CLAIMED)) { ++ vchiq_log_info(vchiq_core_log_level, ++ " fsi - hdr %x", ++ (unsigned int)header); ++ release_slot(state, slot_info, header, ++ NULL); ++ } ++ pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) { ++ vchiq_log_error(vchiq_core_log_level, ++ "fsi - pos %x: header %x, " ++ "msgid %x, header->msgid %x, " ++ "header->size %x", ++ pos, (unsigned int)header, ++ msgid, header->msgid, ++ header->size); ++ WARN(1, "invalid slot position\n"); ++ } ++ } ++ } + } -+ -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ -+ return ret; +} + -+static void bcm2835_dma_issue_pending(struct dma_chan *chan) ++static int ++do_abort_bulks(VCHIQ_SERVICE_T *service) +{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ unsigned long flags; ++ VCHIQ_STATUS_T status; + -+ spin_lock_irqsave(&c->vc.lock, flags); -+ if (vchan_issue_pending(&c->vc) && !c->desc) -+ bcm2835_dma_start_desc(c); ++ /* Abort any outstanding bulk transfers */ ++ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) ++ return 0; ++ abort_outstanding_bulks(service, &service->bulk_tx); ++ abort_outstanding_bulks(service, &service->bulk_rx); ++ mutex_unlock(&service->bulk_mutex); + -+ spin_unlock_irqrestore(&c->vc.lock, flags); ++ status = notify_bulks(service, &service->bulk_tx, 0/*!retry_poll*/); ++ if (status == VCHIQ_SUCCESS) ++ status = notify_bulks(service, &service->bulk_rx, ++ 0/*!retry_poll*/); ++ return (status == VCHIQ_SUCCESS); +} + -+static struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic( -+ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, -+ size_t period_len, enum dma_transfer_direction direction, -+ unsigned long flags) ++static VCHIQ_STATUS_T ++close_service_complete(VCHIQ_SERVICE_T *service, int failstate) +{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ enum dma_slave_buswidth dev_width; -+ struct bcm2835_desc *d; -+ dma_addr_t dev_addr; -+ unsigned int es, sync_type; -+ unsigned int frame, max_size; ++ VCHIQ_STATUS_T status; ++ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); ++ int newstate; + -+ /* Grab configuration */ -+ if (!is_slave_direction(direction)) { -+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); -+ return NULL; -+ } -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ dev_addr = c->cfg.src_addr; -+ dev_width = c->cfg.src_addr_width; -+ sync_type = BCM2835_DMA_S_DREQ; -+ } else { -+ dev_addr = c->cfg.dst_addr; -+ dev_width = c->cfg.dst_addr_width; -+ sync_type = BCM2835_DMA_D_DREQ; -+ } -+ -+ /* Bus width translates to the element size (ES) */ -+ switch (dev_width) { -+ case DMA_SLAVE_BUSWIDTH_4_BYTES: -+ es = BCM2835_DMA_DATA_TYPE_S32; ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPEN: ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ case VCHIQ_SRVSTATE_CLOSERECVD: ++ if (is_server) { ++ if (service->auto_close) { ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; ++ newstate = VCHIQ_SRVSTATE_LISTENING; ++ } else ++ newstate = VCHIQ_SRVSTATE_CLOSEWAIT; ++ } else ++ newstate = VCHIQ_SRVSTATE_CLOSED; ++ vchiq_set_service_state(service, newstate); ++ break; ++ case VCHIQ_SRVSTATE_LISTENING: + break; + default: -+ return NULL; ++ vchiq_log_error(vchiq_core_log_level, ++ "close_service_complete(%x) called in state %s", ++ service->handle, srvstate_names[service->srvstate]); ++ WARN(1, "close_service_complete in unexpected state\n"); ++ return VCHIQ_ERROR; + } + -+ /* Now allocate and setup the descriptor. */ -+ d = kzalloc(sizeof(*d), GFP_NOWAIT); -+ if (!d) -+ return NULL; ++ status = make_service_callback(service, ++ VCHIQ_SERVICE_CLOSED, NULL, NULL); + -+ d->dir = direction; ++ if (status != VCHIQ_RETRY) { ++ int uc = service->service_use_count; ++ int i; ++ /* Complete the close process */ ++ for (i = 0; i < uc; i++) ++ /* cater for cases where close is forced and the ++ ** client may not close all it's handles */ ++ vchiq_release_service_internal(service); + -+ if (c->ch >= 8) /* we have a LITE channel */ -+ max_size = MAX_LITE_TRANSFER; -+ else -+ max_size = MAX_NORMAL_TRANSFER; -+ period_len = min(period_len, max_size); ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; + -+ d->frames = (buf_len-1) / period_len + 1; ++ if (service->srvstate == VCHIQ_SRVSTATE_CLOSED) ++ vchiq_free_service_internal(service); ++ else if (service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) { ++ if (is_server) ++ service->closing = 0; + -+ /* Allocate memory for control blocks */ -+ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); -+ d->control_block_base = dma_zalloc_coherent(chan->device->dev, -+ d->control_block_size, &d->control_block_base_phys, -+ GFP_NOWAIT); ++ up(&service->remove_event); ++ } ++ } else ++ vchiq_set_service_state(service, failstate); + -+ if (!d->control_block_base) { -+ kfree(d); -+ return NULL; -+ } ++ return status; ++} + -+ /* -+ * Iterate over all frames, create a control block -+ * for each frame and link them together. -+ */ -+ for (frame = 0; frame < d->frames; frame++) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[frame]; ++/* Called by the slot handler */ ++VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ int is_server = (service->public_fourcc != VCHIQ_FOURCC_INVALID); + -+ /* Setup adresses */ -+ if (d->dir == DMA_DEV_TO_MEM) { -+ control_block->info = BCM2835_DMA_D_INC; -+ control_block->src = dev_addr; -+ control_block->dst = buf_addr + frame * period_len; ++ vchiq_log_info(vchiq_core_log_level, "%d: csi:%d,%d (%s)", ++ service->state->id, service->localport, close_recvd, ++ srvstate_names[service->srvstate]); ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_CLOSED: ++ case VCHIQ_SRVSTATE_HIDDEN: ++ case VCHIQ_SRVSTATE_LISTENING: ++ case VCHIQ_SRVSTATE_CLOSEWAIT: ++ if (close_recvd) ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_close_service_internal(1) called " ++ "in state %s", ++ srvstate_names[service->srvstate]); ++ else if (is_server) { ++ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) { ++ status = VCHIQ_ERROR; ++ } else { ++ service->client_id = 0; ++ service->remoteport = VCHIQ_PORT_FREE; ++ if (service->srvstate == ++ VCHIQ_SRVSTATE_CLOSEWAIT) ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ } ++ up(&service->remove_event); ++ } else ++ vchiq_free_service_internal(service); ++ break; ++ case VCHIQ_SRVSTATE_OPENING: ++ if (close_recvd) { ++ /* The open was rejected - tell the user */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_CLOSEWAIT); ++ up(&service->remove_event); + } else { -+ control_block->info = BCM2835_DMA_S_INC; -+ control_block->src = buf_addr + frame * period_len; -+ control_block->dst = dev_addr; ++ /* Shutdown mid-open - let the other side know */ ++ status = queue_message(state, service, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, 0); + } -+ -+ /* Enable interrupt */ -+ control_block->info |= BCM2835_DMA_INT_EN; -+ -+ /* Setup synchronization */ -+ if (sync_type != 0) -+ control_block->info |= sync_type; -+ -+ /* Setup DREQ channel */ -+ if (c->cfg.slave_id != 0) -+ control_block->info |= -+ BCM2835_DMA_PER_MAP(c->cfg.slave_id); -+ -+ /* Length of a frame */ -+ if (frame != d->frames-1) -+ control_block->length = period_len; -+ else -+ control_block->length = buf_len - (d->frames - 1) * period_len; -+ -+ d->size += control_block->length; -+ -+ /* -+ * Next block is the next frame. -+ * This function is called on cyclic DMA transfers. -+ * Therefore, wrap around at number of frames. -+ */ -+ control_block->next = d->control_block_base_phys + -+ sizeof(struct bcm2835_dma_cb) -+ * ((frame + 1) % d->frames); -+ } -+ -+ c->cyclic = true; -+ -+ return vchan_tx_prep(&c->vc, &d->vd, flags); -+} -+ -+ -+static struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg( -+ struct dma_chan *chan, struct scatterlist *sgl, -+ unsigned int sg_len, enum dma_transfer_direction direction, -+ unsigned long flags, void *context) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ enum dma_slave_buswidth dev_width; -+ struct bcm2835_desc *d; -+ dma_addr_t dev_addr; -+ struct scatterlist *sgent; -+ unsigned int es, sync_type; -+ unsigned int i, j, splitct, max_size; -+ -+ if (!is_slave_direction(direction)) { -+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); -+ return NULL; -+ } -+ -+ if (direction == DMA_DEV_TO_MEM) { -+ dev_addr = c->cfg.src_addr; -+ dev_width = c->cfg.src_addr_width; -+ sync_type = BCM2835_DMA_S_DREQ; -+ } else { -+ dev_addr = c->cfg.dst_addr; -+ dev_width = c->cfg.dst_addr_width; -+ sync_type = BCM2835_DMA_D_DREQ; -+ } -+ -+ /* Bus width translates to the element size (ES) */ -+ switch (dev_width) { -+ case DMA_SLAVE_BUSWIDTH_4_BYTES: -+ es = BCM2835_DMA_DATA_TYPE_S32; + break; -+ default: -+ return NULL; -+ } + -+ /* Now allocate and setup the descriptor. */ -+ d = kzalloc(sizeof(*d), GFP_NOWAIT); -+ if (!d) -+ return NULL; ++ case VCHIQ_SRVSTATE_OPENSYNC: ++ mutex_lock(&state->sync_mutex); ++ /* Drop through */ + -+ d->dir = direction; -+ -+ if (c->ch >= 8) /* we have a LITE channel */ -+ max_size = MAX_LITE_TRANSFER; -+ else -+ max_size = MAX_NORMAL_TRANSFER; -+ -+ /* We store the length of the SG list in d->frames -+ taking care to account for splitting up transfers -+ too large for a LITE channel */ -+ -+ d->frames = 0; -+ for_each_sg(sgl, sgent, sg_len, i) { -+ uint32_t len = sg_dma_len(sgent); -+ d->frames += 1 + len / max_size; -+ } -+ -+ /* Allocate memory for control blocks */ -+ d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb); -+ d->control_block_base = dma_zalloc_coherent(chan->device->dev, -+ d->control_block_size, &d->control_block_base_phys, -+ GFP_NOWAIT); -+ -+ if (!d->control_block_base) { -+ kfree(d); -+ return NULL; -+ } -+ -+ /* -+ * Iterate over all SG entries, create a control block -+ * for each frame and link them together. -+ */ -+ -+ /* we count the number of times an SG entry had to be splitct -+ as a result of using a LITE channel */ -+ splitct = 0; -+ -+ for_each_sg(sgl, sgent, sg_len, i) { -+ dma_addr_t addr = sg_dma_address(sgent); -+ uint32_t len = sg_dma_len(sgent); -+ -+ for (j = 0; j < len; j += max_size) { -+ struct bcm2835_dma_cb *control_block = -+ &d->control_block_base[i+splitct]; -+ -+ /* Setup adresses */ -+ if (d->dir == DMA_DEV_TO_MEM) { -+ control_block->info = BCM2835_DMA_D_INC | -+ BCM2835_DMA_D_WIDTH | BCM2835_DMA_S_DREQ; -+ control_block->src = dev_addr; -+ control_block->dst = addr + (dma_addr_t)j; -+ } else { -+ control_block->info = BCM2835_DMA_S_INC | -+ BCM2835_DMA_S_WIDTH | BCM2835_DMA_D_DREQ; -+ control_block->src = addr + (dma_addr_t)j; -+ control_block->dst = dev_addr; -+ } -+ -+ /* Common part */ -+ control_block->info |= BCM2835_DMA_WAITS(SDHCI_BCM_DMA_WAITS); -+ control_block->info |= BCM2835_DMA_WAIT_RESP; -+ -+ /* Enable */ -+ if (i == sg_len-1 && len-j <= max_size) -+ control_block->info |= BCM2835_DMA_INT_EN; -+ -+ /* Setup synchronization */ -+ if (sync_type != 0) -+ control_block->info |= sync_type; -+ -+ /* Setup DREQ channel */ -+ c->dreq = c->cfg.slave_id; /* DREQ loaded from config */ -+ -+ if (c->dreq != 0) -+ control_block->info |= -+ BCM2835_DMA_PER_MAP(c->dreq); -+ -+ /* Length of a frame */ -+ control_block->length = min(len-j, max_size); -+ d->size += control_block->length; -+ -+ /* -+ * Next block is the next frame. -+ */ -+ if (i < sg_len-1 || len-j > max_size) { -+ /* next block is the next frame. */ -+ control_block->next = d->control_block_base_phys + -+ sizeof(struct bcm2835_dma_cb) * (i + splitct + 1); -+ } else { -+ /* next block is empty. */ -+ control_block->next = 0; -+ } -+ -+ if (len-j > max_size) -+ splitct++; ++ case VCHIQ_SRVSTATE_OPEN: ++ if (state->is_master || close_recvd) { ++ if (!do_abort_bulks(service)) ++ status = VCHIQ_RETRY; + } -+ } + -+ c->cyclic = false; ++ release_service_messages(service); + -+ return vchan_tx_prep(&c->vc, &d->vd, flags); -+} ++ if (status == VCHIQ_SUCCESS) ++ status = queue_message(state, service, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK); + -+static int bcm2835_dma_slave_config(struct bcm2835_chan *c, -+ struct dma_slave_config *cfg) -+{ -+ if ((cfg->direction == DMA_DEV_TO_MEM && -+ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || -+ (cfg->direction == DMA_MEM_TO_DEV && -+ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || -+ !is_slave_direction(cfg->direction)) { -+ return -EINVAL; -+ } -+ -+ c->cfg = *cfg; -+ -+ return 0; -+} -+ -+static int bcm2835_dma_terminate_all(struct bcm2835_chan *c) -+{ -+ struct bcm2835_dmadev *d = to_bcm2835_dma_dev(c->vc.chan.device); -+ unsigned long flags; -+ int timeout = 10000; -+ LIST_HEAD(head); -+ -+ spin_lock_irqsave(&c->vc.lock, flags); -+ -+ /* Prevent this channel being scheduled */ -+ spin_lock(&d->lock); -+ list_del_init(&c->node); -+ spin_unlock(&d->lock); -+ -+ /* -+ * Stop DMA activity: we assume the callback will not be called -+ * after bcm_dma_abort() returns (even if it does, it will see -+ * c->desc is NULL and exit.) -+ */ -+ if (c->desc) { -+ c->desc = NULL; -+ bcm2835_dma_abort(c->chan_base); -+ -+ /* Wait for stopping */ -+ while (--timeout) { -+ if (!(readl(c->chan_base + BCM2835_DMA_CS) & -+ BCM2835_DMA_ACTIVE)) ++ if (status == VCHIQ_SUCCESS) { ++ if (!close_recvd) { ++ /* Change the state while the mutex is ++ still held */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_CLOSESENT); ++ mutex_unlock(&state->slot_mutex); ++ if (service->sync) ++ mutex_unlock(&state->sync_mutex); + break; ++ } ++ } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { ++ mutex_unlock(&state->sync_mutex); ++ break; ++ } else ++ break; + -+ cpu_relax(); ++ /* Change the state while the mutex is still held */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD); ++ mutex_unlock(&state->slot_mutex); ++ if (service->sync) ++ mutex_unlock(&state->sync_mutex); ++ ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; ++ ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ if (!close_recvd) ++ /* This happens when a process is killed mid-close */ ++ break; ++ ++ if (!state->is_master) { ++ if (!do_abort_bulks(service)) { ++ status = VCHIQ_RETRY; ++ break; ++ } + } + -+ if (!timeout) -+ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); -+ } ++ if (status == VCHIQ_SUCCESS) ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; + -+ vchan_get_all_descriptors(&c->vc, &head); -+ spin_unlock_irqrestore(&c->vc.lock, flags); -+ vchan_dma_desc_free_list(&c->vc, &head); -+ -+ return 0; -+} -+ -+static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, -+ unsigned long arg) -+{ -+ struct bcm2835_chan *c = to_bcm2835_dma_chan(chan); -+ -+ switch (cmd) { -+ case DMA_SLAVE_CONFIG: -+ return bcm2835_dma_slave_config(c, -+ (struct dma_slave_config *)arg); -+ -+ case DMA_TERMINATE_ALL: -+ return bcm2835_dma_terminate_all(c); ++ case VCHIQ_SRVSTATE_CLOSERECVD: ++ if (!close_recvd && is_server) ++ /* Force into LISTENING mode */ ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ status = close_service_complete(service, ++ VCHIQ_SRVSTATE_CLOSERECVD); ++ break; + + default: -+ return -ENXIO; ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_close_service_internal(%d) called in state %s", ++ close_recvd, srvstate_names[service->srvstate]); ++ break; ++ } ++ ++ return status; ++} ++ ++/* Called from the application process upon process death */ ++void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ ++ vchiq_log_info(vchiq_core_log_level, "%d: tsi - (%d<->%d)", ++ state->id, service->localport, service->remoteport); ++ ++ mark_service_closing(service); ++ ++ /* Mark the service for removal by the slot handler */ ++ request_poll(state, service, VCHIQ_POLL_REMOVE); ++} ++ ++/* Called from the slot handler */ ++void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ ++ vchiq_log_info(vchiq_core_log_level, "%d: fsi - (%d)", ++ state->id, service->localport); ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPENING: ++ case VCHIQ_SRVSTATE_CLOSED: ++ case VCHIQ_SRVSTATE_HIDDEN: ++ case VCHIQ_SRVSTATE_LISTENING: ++ case VCHIQ_SRVSTATE_CLOSEWAIT: ++ break; ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "%d: fsi - (%d) in state %s", ++ state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ return; ++ } ++ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE); ++ ++ up(&service->remove_event); ++ ++ /* Release the initial lock */ ++ unlock_service(service); ++} ++ ++VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ /* Find all services registered to this client and enable them. */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ unlock_service(service); ++ } ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, ++ 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY) ++ return VCHIQ_RETRY; ++ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); ++ } ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_CONNECTING) { ++ if (down_interruptible(&state->connect) != 0) ++ return VCHIQ_RETRY; ++ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ up(&state->connect); ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T *service; ++ int i; ++ ++ /* Find all services registered to this client and enable them. */ ++ i = 0; ++ while ((service = next_service_by_instance(state, instance, ++ &i)) != NULL) { ++ (void)vchiq_remove_service(service->handle); ++ unlock_service(service); ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ switch (state->conn_state) { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Request a pause */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING); ++ request_poll(state, NULL, 0); ++ break; ++ default: ++ vchiq_log_error(vchiq_core_log_level, ++ "vchiq_pause_internal in state %s\n", ++ conn_state_names[state->conn_state]); ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ break; ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) { ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING); ++ request_poll(state, NULL, 0); ++ } else { ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: close_service:%d", ++ service->state->id, service->localport); ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ (service->srvstate == VCHIQ_SRVSTATE_HIDDEN)) { ++ unlock_service(service); ++ return VCHIQ_ERROR; ++ } ++ ++ mark_service_closing(service); ++ ++ if (current == service->state->slot_handler_thread) { ++ status = vchiq_close_service_internal(service, ++ 0/*!close_recvd*/); ++ BUG_ON(status == VCHIQ_RETRY); ++ } else { ++ /* Mark the service for termination by the slot handler */ ++ request_poll(service->state, service, VCHIQ_POLL_TERMINATE); ++ } ++ ++ while (1) { ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ break; ++ } ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) ++ break; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: close_service:%d - waiting in state %s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && ++ (service->srvstate != VCHIQ_SRVSTATE_FREE) && ++ (service->srvstate != VCHIQ_SRVSTATE_LISTENING)) ++ status = VCHIQ_ERROR; ++ ++ unlock_service(service); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: remove_service:%d", ++ service->state->id, service->localport); ++ ++ if (service->srvstate == VCHIQ_SRVSTATE_FREE) { ++ unlock_service(service); ++ return VCHIQ_ERROR; ++ } ++ ++ mark_service_closing(service); ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || ++ (current == service->state->slot_handler_thread)) { ++ /* Make it look like a client, because it must be removed and ++ not left in the LISTENING state. */ ++ service->public_fourcc = VCHIQ_FOURCC_INVALID; ++ ++ status = vchiq_close_service_internal(service, ++ 0/*!close_recvd*/); ++ BUG_ON(status == VCHIQ_RETRY); ++ } else { ++ /* Mark the service for removal by the slot handler */ ++ request_poll(service->state, service, VCHIQ_POLL_REMOVE); ++ } ++ while (1) { ++ if (down_interruptible(&service->remove_event) != 0) { ++ status = VCHIQ_RETRY; ++ break; ++ } ++ ++ if ((service->srvstate == VCHIQ_SRVSTATE_FREE) || ++ (service->srvstate == VCHIQ_SRVSTATE_OPEN)) ++ break; ++ ++ vchiq_log_warning(vchiq_core_log_level, ++ "%d: remove_service:%d - waiting in state %s", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ } ++ ++ if ((status == VCHIQ_SUCCESS) && ++ (service->srvstate != VCHIQ_SRVSTATE_FREE)) ++ status = VCHIQ_ERROR; ++ ++ unlock_service(service); ++ ++ return status; ++} ++ ++ ++/* This function may be called by kernel threads or user threads. ++ * User threads may receive VCHIQ_RETRY to indicate that a signal has been ++ * received and the call should be retried after being returned to user ++ * context. ++ * When called in blocking mode, the userdata field points to a bulk_waiter ++ * structure. ++ */ ++VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_BULK_QUEUE_T *queue; ++ VCHIQ_BULK_T *bulk; ++ VCHIQ_STATE_T *state; ++ struct bulk_waiter *bulk_waiter = NULL; ++ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; ++ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (!service || ++ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || ++ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) ++ goto error_exit; ++ ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ bulk_waiter = (struct bulk_waiter *)userdata; ++ sema_init(&bulk_waiter->event, 0); ++ bulk_waiter->actual = 0; ++ bulk_waiter->bulk = NULL; ++ break; ++ case VCHIQ_BULK_MODE_WAITING: ++ bulk_waiter = (struct bulk_waiter *)userdata; ++ bulk = bulk_waiter->bulk; ++ goto waiting; ++ default: ++ goto error_exit; ++ } ++ ++ state = service->state; ++ ++ queue = (dir == VCHIQ_BULK_TRANSMIT) ? ++ &service->bulk_tx : &service->bulk_rx; ++ ++ if (mutex_lock_interruptible(&service->bulk_mutex) != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ ++ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); ++ do { ++ mutex_unlock(&service->bulk_mutex); ++ if (down_interruptible(&service->bulk_remove_event) ++ != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ if (mutex_lock_interruptible(&service->bulk_mutex) ++ != 0) { ++ status = VCHIQ_RETRY; ++ goto error_exit; ++ } ++ } while (queue->local_insert == queue->remove + ++ VCHIQ_NUM_SERVICE_BULKS); ++ } ++ ++ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; ++ ++ bulk->mode = mode; ++ bulk->dir = dir; ++ bulk->userdata = userdata; ++ bulk->size = size; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ ++ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != ++ VCHIQ_SUCCESS) ++ goto unlock_error_exit; ++ ++ wmb(); ++ ++ vchiq_log_info(vchiq_core_log_level, ++ "%d: bt (%d->%d) %cx %x@%x %x", ++ state->id, ++ service->localport, service->remoteport, dir_char, ++ size, (unsigned int)bulk->data, (unsigned int)userdata); ++ ++ /* The slot mutex must be held when the service is being closed, so ++ claim it here to ensure that isn't happening */ ++ if (mutex_lock_interruptible(&state->slot_mutex) != 0) { ++ status = VCHIQ_RETRY; ++ goto cancel_bulk_error_exit; ++ } ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) ++ goto unlock_both_error_exit; ++ ++ if (state->is_master) { ++ queue->local_insert++; ++ if (resolve_bulks(service, queue)) ++ request_poll(state, service, ++ (dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); ++ } else { ++ int payload[2] = { (int)bulk->data, bulk->size }; ++ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) }; ++ ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(dir_msgtype, ++ service->localport, service->remoteport), ++ &element, 1, sizeof(payload), ++ QMFLAGS_IS_BLOCKING | ++ QMFLAGS_NO_MUTEX_LOCK | ++ QMFLAGS_NO_MUTEX_UNLOCK); ++ if (status != VCHIQ_SUCCESS) { ++ goto unlock_both_error_exit; ++ } ++ queue->local_insert++; ++ } ++ ++ mutex_unlock(&state->slot_mutex); ++ mutex_unlock(&service->bulk_mutex); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%d: bt:%d %cx li=%x ri=%x p=%x", ++ state->id, ++ service->localport, dir_char, ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++waiting: ++ unlock_service(service); ++ ++ status = VCHIQ_SUCCESS; ++ ++ if (bulk_waiter) { ++ bulk_waiter->bulk = bulk; ++ if (down_interruptible(&bulk_waiter->event) != 0) ++ status = VCHIQ_RETRY; ++ else if (bulk_waiter->actual == VCHIQ_BULK_ACTUAL_ABORTED) ++ status = VCHIQ_ERROR; ++ } ++ ++ return status; ++ ++unlock_both_error_exit: ++ mutex_unlock(&state->slot_mutex); ++cancel_bulk_error_exit: ++ vchiq_complete_bulk(bulk); ++unlock_error_exit: ++ mutex_unlock(&service->bulk_mutex); ++ ++error_exit: ++ if (service) ++ unlock_service(service); ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, ++ const VCHIQ_ELEMENT_T *elements, unsigned int count) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ unsigned int size = 0; ++ unsigned int i; ++ ++ if (!service || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) ++ goto error_exit; ++ ++ for (i = 0; i < (unsigned int)count; i++) { ++ if (elements[i].size) { ++ if (elements[i].data == NULL) { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ goto error_exit; ++ } ++ size += elements[i].size; ++ } ++ } ++ ++ if (size > VCHIQ_MAX_MSG_SIZE) { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ goto error_exit; ++ } ++ ++ switch (service->srvstate) { ++ case VCHIQ_SRVSTATE_OPEN: ++ status = queue_message(service->state, service, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, ++ service->localport, ++ service->remoteport), ++ elements, count, size, 1); ++ break; ++ case VCHIQ_SRVSTATE_OPENSYNC: ++ status = queue_message_sync(service->state, service, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, ++ service->localport, ++ service->remoteport), ++ elements, count, size, 1); ++ break; ++ default: ++ status = VCHIQ_ERROR; ++ break; ++ } ++ ++error_exit: ++ if (service) ++ unlock_service(service); ++ ++ return status; ++} ++ ++void ++vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_STATE_T *state; ++ int slot_index; ++ ++ if (!service) ++ return; ++ ++ state = service->state; ++ remote = state->remote; ++ ++ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header); ++ ++ if ((slot_index >= remote->slot_first) && ++ (slot_index <= remote->slot_last)) { ++ int msgid = header->msgid; ++ if (msgid & VCHIQ_MSGID_CLAIMED) { ++ VCHIQ_SLOT_INFO_T *slot_info = ++ SLOT_INFO_FROM_INDEX(state, slot_index); ++ ++ release_slot(state, slot_info, header, service); ++ } ++ } else if (slot_index == remote->slot_sync) ++ release_message_sync(state, header); ++ ++ unlock_service(service); ++} ++ ++static void ++release_message_sync(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) ++{ ++ header->msgid = VCHIQ_MSGID_PADDING; ++ wmb(); ++ remote_event_signal(&state->remote->sync_release); ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, short *peer_version) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ ++ if (!service || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS) || ++ !peer_version) ++ goto exit; ++ *peer_version = service->peer_version; ++ status = VCHIQ_SUCCESS; ++ ++exit: ++ if (service) ++ unlock_service(service); ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, VCHIQ_CONFIG_T *pconfig) ++{ ++ VCHIQ_CONFIG_T config; ++ ++ (void)instance; ++ ++ config.max_msg_size = VCHIQ_MAX_MSG_SIZE; ++ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE; ++ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS; ++ config.max_services = VCHIQ_MAX_SERVICES; ++ config.version = VCHIQ_VERSION; ++ config.version_min = VCHIQ_VERSION_MIN; ++ ++ if (config_size > sizeof(VCHIQ_CONFIG_T)) ++ return VCHIQ_ERROR; ++ ++ memcpy(pconfig, &config, ++ min(config_size, (int)(sizeof(VCHIQ_CONFIG_T)))); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHIQ_SERVICE_OPTION_T option, int value) ++{ ++ VCHIQ_SERVICE_T *service = find_service_by_handle(handle); ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (service) { ++ switch (option) { ++ case VCHIQ_SERVICE_OPTION_AUTOCLOSE: ++ service->auto_close = value; ++ status = VCHIQ_SUCCESS; ++ break; ++ ++ case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[ ++ service->localport]; ++ if (value == 0) ++ value = service->state->default_slot_quota; ++ if ((value >= service_quota->slot_use_count) && ++ (value < (unsigned short)~0)) { ++ service_quota->slot_quota = value; ++ if ((value >= service_quota->slot_use_count) && ++ (service_quota->message_quota >= ++ service_quota->message_use_count)) { ++ /* Signal the service that it may have ++ ** dropped below its quota */ ++ up(&service_quota->quota_event); ++ } ++ status = VCHIQ_SUCCESS; ++ } ++ } break; ++ ++ case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[ ++ service->localport]; ++ if (value == 0) ++ value = service->state->default_message_quota; ++ if ((value >= service_quota->message_use_count) && ++ (value < (unsigned short)~0)) { ++ service_quota->message_quota = value; ++ if ((value >= ++ service_quota->message_use_count) && ++ (service_quota->slot_quota >= ++ service_quota->slot_use_count)) ++ /* Signal the service that it may have ++ ** dropped below its quota */ ++ up(&service_quota->quota_event); ++ status = VCHIQ_SUCCESS; ++ } ++ } break; ++ ++ case VCHIQ_SERVICE_OPTION_SYNCHRONOUS: ++ if ((service->srvstate == VCHIQ_SRVSTATE_HIDDEN) || ++ (service->srvstate == ++ VCHIQ_SRVSTATE_LISTENING)) { ++ service->sync = value; ++ status = VCHIQ_SUCCESS; ++ } ++ break; ++ ++ case VCHIQ_SERVICE_OPTION_TRACE: ++ service->trace = value; ++ status = VCHIQ_SUCCESS; ++ break; ++ ++ default: ++ break; ++ } ++ unlock_service(service); ++ } ++ ++ return status; ++} ++ ++void ++vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, ++ VCHIQ_SHARED_STATE_T *shared, const char *label) ++{ ++ static const char *const debug_names[] = { ++ "", ++ "SLOT_HANDLER_COUNT", ++ "SLOT_HANDLER_LINE", ++ "PARSE_LINE", ++ "PARSE_HEADER", ++ "PARSE_MSGID", ++ "AWAIT_COMPLETION_LINE", ++ "DEQUEUE_MESSAGE_LINE", ++ "SERVICE_CALLBACK_LINE", ++ "MSG_QUEUE_FULL_COUNT", ++ "COMPLETION_QUEUE_FULL_COUNT" ++ }; ++ int i; ++ ++ char buf[80]; ++ int len; ++ len = snprintf(buf, sizeof(buf), ++ " %s: slots %d-%d tx_pos=%x recycle=%x", ++ label, shared->slot_first, shared->slot_last, ++ shared->tx_pos, shared->slot_queue_recycle); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Slots claimed:"); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ for (i = shared->slot_first; i <= shared->slot_last; i++) { ++ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info.use_count != slot_info.release_count) { ++ len = snprintf(buf, sizeof(buf), ++ " %d: %d/%d", i, slot_info.use_count, ++ slot_info.release_count); ++ vchiq_dump(dump_context, buf, len + 1); ++ } ++ } ++ ++ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) { ++ len = snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", ++ debug_names[i], shared->debug[i], shared->debug[i]); ++ vchiq_dump(dump_context, buf, len + 1); + } +} + -+#ifdef CONFIG_OF -+static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) ++void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state) +{ -+ struct bcm2835_chan *c; ++ char buf[80]; ++ int len; ++ int i; + -+ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); -+ if (!c) -+ return -ENOMEM; ++ len = snprintf(buf, sizeof(buf), "State %d: %s", state->id, ++ conn_state_names[state->conn_state]); ++ vchiq_dump(dump_context, buf, len + 1); + -+ c->vc.desc_free = bcm2835_dma_desc_free; -+ vchan_init(&c->vc, &d->ddev); -+ INIT_LIST_HEAD(&c->node); ++ len = snprintf(buf, sizeof(buf), ++ " tx_pos=%x(@%x), rx_pos=%x(@%x)", ++ state->local->tx_pos, ++ (uint32_t)state->tx_data + ++ (state->local_tx_pos & VCHIQ_SLOT_MASK), ++ state->rx_pos, ++ (uint32_t)state->rx_data + ++ (state->rx_pos & VCHIQ_SLOT_MASK)); ++ vchiq_dump(dump_context, buf, len + 1); + -+ d->ddev.chancnt++; ++ len = snprintf(buf, sizeof(buf), ++ " Version: %d (min %d)", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN); ++ vchiq_dump(dump_context, buf, len + 1); + -+ c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id); -+ c->ch = chan_id; -+ c->irq_number = irq; ++ if (VCHIQ_ENABLE_STATS) { ++ len = snprintf(buf, sizeof(buf), ++ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, " ++ "error_count=%d", ++ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, ++ state->stats.error_count); ++ vchiq_dump(dump_context, buf, len + 1); ++ } + -+ return 0; ++ len = snprintf(buf, sizeof(buf), ++ " Slots: %d available (%d data), %d recyclable, %d stalls " ++ "(%d data)", ++ ((state->slot_queue_available * VCHIQ_SLOT_SIZE) - ++ state->local_tx_pos) / VCHIQ_SLOT_SIZE, ++ state->data_quota - state->data_use_count, ++ state->local->slot_queue_recycle - state->slot_queue_available, ++ state->stats.slot_stalls, state->stats.data_stalls); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ vchiq_dump_platform_state(dump_context); ++ ++ vchiq_dump_shared_state(dump_context, state, state->local, "Local"); ++ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote"); ++ ++ vchiq_dump_platform_instances(dump_context); ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = find_service_by_port(state, i); ++ ++ if (service) { ++ vchiq_dump_service_state(dump_context, service); ++ unlock_service(service); ++ } ++ } +} ++ ++void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ char buf[80]; ++ int len; ++ ++ len = snprintf(buf, sizeof(buf), "Service %d: %s (ref %u)", ++ service->localport, srvstate_names[service->srvstate], ++ service->ref_count - 1); /*Don't include the lock just taken*/ ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_FREE) { ++ char remoteport[30]; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[service->localport]; ++ int fourcc = service->base.fourcc; ++ int tx_pending, rx_pending; ++ if (service->remoteport != VCHIQ_PORT_FREE) { ++ int len2 = snprintf(remoteport, sizeof(remoteport), ++ "%d", service->remoteport); ++ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) ++ snprintf(remoteport + len2, ++ sizeof(remoteport) - len2, ++ " (client %x)", service->client_id); ++ } else ++ strcpy(remoteport, "n/a"); ++ ++ len += snprintf(buf + len, sizeof(buf) - len, ++ " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)", ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ remoteport, ++ service_quota->message_use_count, ++ service_quota->message_quota, ++ service_quota->slot_use_count, ++ service_quota->slot_quota); ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ tx_pending = service->bulk_tx.local_insert - ++ service->bulk_tx.remote_insert; ++ ++ rx_pending = service->bulk_rx.local_insert - ++ service->bulk_rx.remote_insert; ++ ++ len = snprintf(buf, sizeof(buf), ++ " Bulk: tx_pending=%d (size %d)," ++ " rx_pending=%d (size %d)", ++ tx_pending, ++ tx_pending ? service->bulk_tx.bulks[ ++ BULK_INDEX(service->bulk_tx.remove)].size : 0, ++ rx_pending, ++ rx_pending ? service->bulk_rx.bulks[ ++ BULK_INDEX(service->bulk_rx.remove)].size : 0); ++ ++ if (VCHIQ_ENABLE_STATS) { ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Ctrl: tx_count=%d, tx_bytes=%llu, " ++ "rx_count=%d, rx_bytes=%llu", ++ service->stats.ctrl_tx_count, ++ service->stats.ctrl_tx_bytes, ++ service->stats.ctrl_rx_count, ++ service->stats.ctrl_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " Bulk: tx_count=%d, tx_bytes=%llu, " ++ "rx_count=%d, rx_bytes=%llu", ++ service->stats.bulk_tx_count, ++ service->stats.bulk_tx_bytes, ++ service->stats.bulk_rx_count, ++ service->stats.bulk_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = snprintf(buf, sizeof(buf), ++ " %d quota stalls, %d slot stalls, " ++ "%d bulk stalls, %d aborted, %d errors", ++ service->stats.quota_stalls, ++ service->stats.slot_stalls, ++ service->stats.bulk_stalls, ++ service->stats.bulk_aborted_count, ++ service->stats.error_count); ++ } ++ } ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_FREE) ++ vchiq_dump_platform_service_state(dump_context, service); ++} ++ ++ ++void ++vchiq_loud_error_header(void) ++{ ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, "====="); ++} ++ ++void ++vchiq_loud_error_footer(void) ++{ ++ vchiq_log_error(vchiq_core_log_level, "====="); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++ vchiq_log_error(vchiq_core_log_level, ++ "============================================================" ++ "================"); ++} ++ ++ ++VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if (state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), ++ NULL, 0, 0, 0); ++ return status; ++} ++ ++void vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, ++ size_t numBytes) ++{ ++ const uint8_t *mem = (const uint8_t *)voidMem; ++ size_t offset; ++ char lineBuf[100]; ++ char *s; ++ ++ while (numBytes > 0) { ++ s = lineBuf; ++ ++ for (offset = 0; offset < 16; offset++) { ++ if (offset < numBytes) ++ s += snprintf(s, 4, "%02x ", mem[offset]); ++ else ++ s += snprintf(s, 4, " "); ++ } ++ ++ for (offset = 0; offset < 16; offset++) { ++ if (offset < numBytes) { ++ uint8_t ch = mem[offset]; ++ ++ if ((ch < ' ') || (ch > '~')) ++ ch = '.'; ++ *s++ = (char)ch; ++ } ++ } ++ *s++ = '\0'; ++ ++ if ((label != NULL) && (*label != '\0')) ++ vchiq_log_trace(VCHIQ_LOG_TRACE, ++ "%s: %08x: %s", label, addr, lineBuf); ++ else ++ vchiq_log_trace(VCHIQ_LOG_TRACE, ++ "%08x: %s", addr, lineBuf); ++ ++ addr += 16; ++ mem += 16; ++ if (numBytes > 16) ++ numBytes -= 16; ++ else ++ numBytes = 0; ++ } ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +new file mode 100644 +index 0000000..9be484c +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +@@ -0,0 +1,712 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_CORE_H ++#define VCHIQ_CORE_H ++ ++#include ++#include ++#include ++ ++#include "vchiq_cfg.h" ++ ++#include "vchiq.h" ++ ++/* Run time control of log level, based on KERN_XXX level. */ ++#define VCHIQ_LOG_DEFAULT 4 ++#define VCHIQ_LOG_ERROR 3 ++#define VCHIQ_LOG_WARNING 4 ++#define VCHIQ_LOG_INFO 6 ++#define VCHIQ_LOG_TRACE 7 ++ ++#define VCHIQ_LOG_PREFIX KERN_INFO "vchiq: " ++ ++#ifndef vchiq_log_error ++#define vchiq_log_error(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_ERROR) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_warning ++#define vchiq_log_warning(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_WARNING) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_info ++#define vchiq_log_info(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_INFO) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) ++#endif ++#ifndef vchiq_log_trace ++#define vchiq_log_trace(cat, fmt, ...) \ ++ do { if (cat >= VCHIQ_LOG_TRACE) \ ++ printk(VCHIQ_LOG_PREFIX fmt "\n", ##__VA_ARGS__); } while (0) +#endif + -+static int bcm2708_dma_chan_init(struct bcm2835_dmadev *d, -+ void __iomem *chan_base, int chan_id, int irq) -+{ -+ struct bcm2835_chan *c; ++#define vchiq_loud_error(...) \ ++ vchiq_log_error(vchiq_core_log_level, "===== " __VA_ARGS__) + -+ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); -+ if (!c) -+ return -ENOMEM; ++#ifndef vchiq_static_assert ++#define vchiq_static_assert(cond) __attribute__((unused)) \ ++ extern int vchiq_static_assert[(cond) ? 1 : -1] ++#endif + -+ c->vc.desc_free = bcm2835_dma_desc_free; -+ vchan_init(&c->vc, &d->ddev); -+ INIT_LIST_HEAD(&c->node); ++#define IS_POW2(x) (x && ((x & (x - 1)) == 0)) + -+ d->ddev.chancnt++; ++/* Ensure that the slot size and maximum number of slots are powers of 2 */ ++vchiq_static_assert(IS_POW2(VCHIQ_SLOT_SIZE)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS)); ++vchiq_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); + -+ c->chan_base = chan_base; -+ c->ch = chan_id; -+ c->irq_number = irq; ++#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) ++#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) ++#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \ ++ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) + -+ return 0; -+} ++#define VCHIQ_MSG_PADDING 0 /* - */ ++#define VCHIQ_MSG_CONNECT 1 /* - */ ++#define VCHIQ_MSG_OPEN 2 /* + (srcport, -), fourcc, client_id */ ++#define VCHIQ_MSG_OPENACK 3 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_CLOSE 4 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_DATA 5 /* + (srcport, dstport) */ ++#define VCHIQ_MSG_BULK_RX 6 /* + (srcport, dstport), data, size */ ++#define VCHIQ_MSG_BULK_TX 7 /* + (srcport, dstport), data, size */ ++#define VCHIQ_MSG_BULK_RX_DONE 8 /* + (srcport, dstport), actual */ ++#define VCHIQ_MSG_BULK_TX_DONE 9 /* + (srcport, dstport), actual */ ++#define VCHIQ_MSG_PAUSE 10 /* - */ ++#define VCHIQ_MSG_RESUME 11 /* - */ ++#define VCHIQ_MSG_REMOTE_USE 12 /* - */ ++#define VCHIQ_MSG_REMOTE_RELEASE 13 /* - */ ++#define VCHIQ_MSG_REMOTE_USE_ACTIVE 14 /* - */ + ++#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) ++#define VCHIQ_PORT_FREE 0x1000 ++#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE) ++#define VCHIQ_MAKE_MSG(type, srcport, dstport) \ ++ ((type<<24) | (srcport<<12) | (dstport<<0)) ++#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24) ++#define VCHIQ_MSG_SRCPORT(msgid) \ ++ (unsigned short)(((unsigned int)msgid >> 12) & 0xfff) ++#define VCHIQ_MSG_DSTPORT(msgid) \ ++ ((unsigned short)msgid & 0xfff) + -+static void bcm2835_dma_free(struct bcm2835_dmadev *od) -+{ -+ struct bcm2835_chan *c, *next; ++#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \ ++ ((fourcc) >> 24) & 0xff, \ ++ ((fourcc) >> 16) & 0xff, \ ++ ((fourcc) >> 8) & 0xff, \ ++ (fourcc) & 0xff + -+ list_for_each_entry_safe(c, next, &od->ddev.channels, -+ vc.chan.device_node) { -+ list_del(&c->vc.chan.device_node); -+ tasklet_kill(&c->vc.task); -+ } -+} ++/* Ensure the fields are wide enough */ ++vchiq_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0, 0, VCHIQ_PORT_MAX)) ++ == 0); ++vchiq_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0, VCHIQ_PORT_MAX, 0)) == 0); ++vchiq_static_assert((unsigned int)VCHIQ_PORT_MAX < ++ (unsigned int)VCHIQ_PORT_FREE); + -+static const struct of_device_id bcm2835_dma_of_match[] = { -+ { .compatible = "brcm,bcm2835-dma", }, -+ {}, ++#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING, 0, 0) ++#define VCHIQ_MSGID_CLAIMED 0x40000000 ++ ++#define VCHIQ_FOURCC_INVALID 0x00000000 ++#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID) ++ ++#define VCHIQ_BULK_ACTUAL_ABORTED -1 ++ ++typedef uint32_t BITSET_T; ++ ++vchiq_static_assert((sizeof(BITSET_T) * 8) == 32); ++ ++#define BITSET_SIZE(b) ((b + 31) >> 5) ++#define BITSET_WORD(b) (b >> 5) ++#define BITSET_BIT(b) (1 << (b & 31)) ++#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs)) ++#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b)) ++#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b)) ++#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b)) ++ ++#if VCHIQ_ENABLE_STATS ++#define VCHIQ_STATS_INC(state, stat) (state->stats. stat++) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat++) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) \ ++ (service->stats. stat += addend) ++#else ++#define VCHIQ_STATS_INC(state, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0) ++#endif ++ ++enum { ++ DEBUG_ENTRIES, ++#if VCHIQ_ENABLE_DEBUG ++ DEBUG_SLOT_HANDLER_COUNT, ++ DEBUG_SLOT_HANDLER_LINE, ++ DEBUG_PARSE_LINE, ++ DEBUG_PARSE_HEADER, ++ DEBUG_PARSE_MSGID, ++ DEBUG_AWAIT_COMPLETION_LINE, ++ DEBUG_DEQUEUE_MESSAGE_LINE, ++ DEBUG_SERVICE_CALLBACK_LINE, ++ DEBUG_MSG_QUEUE_FULL_COUNT, ++ DEBUG_COMPLETION_QUEUE_FULL_COUNT, ++#endif ++ DEBUG_MAX +}; -+MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); + -+#ifdef CONFIG_OF -+static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, -+ struct of_dma *ofdma) ++#if VCHIQ_ENABLE_DEBUG ++ ++#define DEBUG_INITIALISE(local) int *debug_ptr = (local)->debug; ++#define DEBUG_TRACE(d) \ ++ do { debug_ptr[DEBUG_ ## d] = __LINE__; dsb(); } while (0) ++#define DEBUG_VALUE(d, v) \ ++ do { debug_ptr[DEBUG_ ## d] = (v); dsb(); } while (0) ++#define DEBUG_COUNT(d) \ ++ do { debug_ptr[DEBUG_ ## d]++; dsb(); } while (0) ++ ++#else /* VCHIQ_ENABLE_DEBUG */ ++ ++#define DEBUG_INITIALISE(local) ++#define DEBUG_TRACE(d) ++#define DEBUG_VALUE(d, v) ++#define DEBUG_COUNT(d) ++ ++#endif /* VCHIQ_ENABLE_DEBUG */ ++ ++typedef enum { ++ VCHIQ_CONNSTATE_DISCONNECTED, ++ VCHIQ_CONNSTATE_CONNECTING, ++ VCHIQ_CONNSTATE_CONNECTED, ++ VCHIQ_CONNSTATE_PAUSING, ++ VCHIQ_CONNSTATE_PAUSE_SENT, ++ VCHIQ_CONNSTATE_PAUSED, ++ VCHIQ_CONNSTATE_RESUMING, ++ VCHIQ_CONNSTATE_PAUSE_TIMEOUT, ++ VCHIQ_CONNSTATE_RESUME_TIMEOUT ++} VCHIQ_CONNSTATE_T; ++ ++enum { ++ VCHIQ_SRVSTATE_FREE, ++ VCHIQ_SRVSTATE_HIDDEN, ++ VCHIQ_SRVSTATE_LISTENING, ++ VCHIQ_SRVSTATE_OPENING, ++ VCHIQ_SRVSTATE_OPEN, ++ VCHIQ_SRVSTATE_OPENSYNC, ++ VCHIQ_SRVSTATE_CLOSESENT, ++ VCHIQ_SRVSTATE_CLOSERECVD, ++ VCHIQ_SRVSTATE_CLOSEWAIT, ++ VCHIQ_SRVSTATE_CLOSED ++}; ++ ++enum { ++ VCHIQ_POLL_TERMINATE, ++ VCHIQ_POLL_REMOVE, ++ VCHIQ_POLL_TXNOTIFY, ++ VCHIQ_POLL_RXNOTIFY, ++ VCHIQ_POLL_COUNT ++}; ++ ++typedef enum { ++ VCHIQ_BULK_TRANSMIT, ++ VCHIQ_BULK_RECEIVE ++} VCHIQ_BULK_DIR_T; ++ ++typedef void (*VCHIQ_USERDATA_TERM_T)(void *userdata); ++ ++typedef struct vchiq_bulk_struct { ++ short mode; ++ short dir; ++ void *userdata; ++ VCHI_MEM_HANDLE_T handle; ++ void *data; ++ int size; ++ void *remote_data; ++ int remote_size; ++ int actual; ++} VCHIQ_BULK_T; ++ ++typedef struct vchiq_bulk_queue_struct { ++ int local_insert; /* Where to insert the next local bulk */ ++ int remote_insert; /* Where to insert the next remote bulk (master) */ ++ int process; /* Bulk to transfer next */ ++ int remote_notify; /* Bulk to notify the remote client of next (mstr) */ ++ int remove; /* Bulk to notify the local client of, and remove, ++ ** next */ ++ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS]; ++} VCHIQ_BULK_QUEUE_T; ++ ++typedef struct remote_event_struct { ++ int armed; ++ int fired; ++ struct semaphore *event; ++} REMOTE_EVENT_T; ++ ++typedef struct opaque_platform_state_t *VCHIQ_PLATFORM_STATE_T; ++ ++typedef struct vchiq_state_struct VCHIQ_STATE_T; ++ ++typedef struct vchiq_slot_struct { ++ char data[VCHIQ_SLOT_SIZE]; ++} VCHIQ_SLOT_T; ++ ++typedef struct vchiq_slot_info_struct { ++ /* Use two counters rather than one to avoid the need for a mutex. */ ++ short use_count; ++ short release_count; ++} VCHIQ_SLOT_INFO_T; ++ ++typedef struct vchiq_service_struct { ++ VCHIQ_SERVICE_BASE_T base; ++ VCHIQ_SERVICE_HANDLE_T handle; ++ unsigned int ref_count; ++ int srvstate; ++ VCHIQ_USERDATA_TERM_T userdata_term; ++ unsigned int localport; ++ unsigned int remoteport; ++ int public_fourcc; ++ int client_id; ++ char auto_close; ++ char sync; ++ char closing; ++ char trace; ++ atomic_t poll_flags; ++ short version; ++ short version_min; ++ short peer_version; ++ ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance; ++ ++ int service_use_count; ++ ++ VCHIQ_BULK_QUEUE_T bulk_tx; ++ VCHIQ_BULK_QUEUE_T bulk_rx; ++ ++ struct semaphore remove_event; ++ struct semaphore bulk_remove_event; ++ struct mutex bulk_mutex; ++ ++ struct service_stats_struct { ++ int quota_stalls; ++ int slot_stalls; ++ int bulk_stalls; ++ int error_count; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int bulk_tx_count; ++ int bulk_rx_count; ++ int bulk_aborted_count; ++ uint64_t ctrl_tx_bytes; ++ uint64_t ctrl_rx_bytes; ++ uint64_t bulk_tx_bytes; ++ uint64_t bulk_rx_bytes; ++ } stats; ++} VCHIQ_SERVICE_T; ++ ++/* The quota information is outside VCHIQ_SERVICE_T so that it can be ++ statically allocated, since for accounting reasons a service's slot ++ usage is carried over between users of the same port number. ++ */ ++typedef struct vchiq_service_quota_struct { ++ unsigned short slot_quota; ++ unsigned short slot_use_count; ++ unsigned short message_quota; ++ unsigned short message_use_count; ++ struct semaphore quota_event; ++ int previous_tx_index; ++} VCHIQ_SERVICE_QUOTA_T; ++ ++typedef struct vchiq_shared_state_struct { ++ ++ /* A non-zero value here indicates that the content is valid. */ ++ int initialised; ++ ++ /* The first and last (inclusive) slots allocated to the owner. */ ++ int slot_first; ++ int slot_last; ++ ++ /* The slot allocated to synchronous messages from the owner. */ ++ int slot_sync; ++ ++ /* Signalling this event indicates that owner's slot handler thread ++ ** should run. */ ++ REMOTE_EVENT_T trigger; ++ ++ /* Indicates the byte position within the stream where the next message ++ ** will be written. The least significant bits are an index into the ++ ** slot. The next bits are the index of the slot in slot_queue. */ ++ int tx_pos; ++ ++ /* This event should be signalled when a slot is recycled. */ ++ REMOTE_EVENT_T recycle; ++ ++ /* The slot_queue index where the next recycled slot will be written. */ ++ int slot_queue_recycle; ++ ++ /* This event should be signalled when a synchronous message is sent. */ ++ REMOTE_EVENT_T sync_trigger; ++ ++ /* This event should be signalled when a synchronous message has been ++ ** released. */ ++ REMOTE_EVENT_T sync_release; ++ ++ /* A circular buffer of slot indexes. */ ++ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE]; ++ ++ /* Debugging state */ ++ int debug[DEBUG_MAX]; ++} VCHIQ_SHARED_STATE_T; ++ ++typedef struct vchiq_slot_zero_struct { ++ int magic; ++ short version; ++ short version_min; ++ int slot_zero_size; ++ int slot_size; ++ int max_slots; ++ int max_slots_per_side; ++ int platform_data[2]; ++ VCHIQ_SHARED_STATE_T master; ++ VCHIQ_SHARED_STATE_T slave; ++ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS]; ++} VCHIQ_SLOT_ZERO_T; ++ ++struct vchiq_state_struct { ++ int id; ++ int initialised; ++ VCHIQ_CONNSTATE_T conn_state; ++ int is_master; ++ short version_common; ++ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_SLOT_T *slot_data; ++ ++ unsigned short default_slot_quota; ++ unsigned short default_message_quota; ++ ++ /* Event indicating connect message received */ ++ struct semaphore connect; ++ ++ /* Mutex protecting services */ ++ struct mutex mutex; ++ VCHIQ_INSTANCE_T *instance; ++ ++ /* Processes incoming messages */ ++ struct task_struct *slot_handler_thread; ++ ++ /* Processes recycled slots */ ++ struct task_struct *recycle_thread; ++ ++ /* Processes synchronous messages */ ++ struct task_struct *sync_thread; ++ ++ /* Local implementation of the trigger remote event */ ++ struct semaphore trigger_event; ++ ++ /* Local implementation of the recycle remote event */ ++ struct semaphore recycle_event; ++ ++ /* Local implementation of the sync trigger remote event */ ++ struct semaphore sync_trigger_event; ++ ++ /* Local implementation of the sync release remote event */ ++ struct semaphore sync_release_event; ++ ++ char *tx_data; ++ char *rx_data; ++ VCHIQ_SLOT_INFO_T *rx_info; ++ ++ struct mutex slot_mutex; ++ ++ struct mutex recycle_mutex; ++ ++ struct mutex sync_mutex; ++ ++ struct mutex bulk_transfer_mutex; ++ ++ /* Indicates the byte position within the stream from where the next ++ ** message will be read. The least significant bits are an index into ++ ** the slot.The next bits are the index of the slot in ++ ** remote->slot_queue. */ ++ int rx_pos; ++ ++ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read ++ from remote->tx_pos. */ ++ int local_tx_pos; ++ ++ /* The slot_queue index of the slot to become available next. */ ++ int slot_queue_available; ++ ++ /* A flag to indicate if any poll has been requested */ ++ int poll_needed; ++ ++ /* Ths index of the previous slot used for data messages. */ ++ int previous_data_index; ++ ++ /* The number of slots occupied by data messages. */ ++ unsigned short data_use_count; ++ ++ /* The maximum number of slots to be occupied by data messages. */ ++ unsigned short data_quota; ++ ++ /* An array of bit sets indicating which services must be polled. */ ++ atomic_t poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ ++ /* The number of the first unused service */ ++ int unused_service; ++ ++ /* Signalled when a free slot becomes available. */ ++ struct semaphore slot_available_event; ++ ++ struct semaphore slot_remove_event; ++ ++ /* Signalled when a free data slot becomes available. */ ++ struct semaphore data_quota_event; ++ ++ /* Incremented when there are bulk transfers which cannot be processed ++ * whilst paused and must be processed on resume */ ++ int deferred_bulks; ++ ++ struct state_stats_struct { ++ int slot_stalls; ++ int data_stalls; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int error_count; ++ } stats; ++ ++ VCHIQ_SERVICE_T * services[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; ++ ++ VCHIQ_PLATFORM_STATE_T platform_state; ++}; ++ ++struct bulk_waiter { ++ VCHIQ_BULK_T *bulk; ++ struct semaphore event; ++ int actual; ++}; ++ ++extern spinlock_t bulk_waiter_spinlock; ++ ++extern int vchiq_core_log_level; ++extern int vchiq_core_msg_log_level; ++extern int vchiq_sync_log_level; ++ ++extern VCHIQ_STATE_T *vchiq_states[VCHIQ_MAX_STATES]; ++ ++extern const char * ++get_conn_state_name(VCHIQ_CONNSTATE_T conn_state); ++ ++extern VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size); ++ ++extern VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, ++ int is_master); ++ ++extern VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance, VCHIQ_USERDATA_TERM_T userdata_term); ++ ++extern VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id); ++ ++extern VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd); ++ ++extern void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state); ++ ++extern void ++remote_event_pollall(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir); ++ ++extern void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_loud_error_header(void); ++ ++extern void ++vchiq_loud_error_footer(void); ++ ++extern void ++request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type); ++ ++static inline VCHIQ_SERVICE_T * ++handle_to_service(VCHIQ_SERVICE_HANDLE_T handle) +{ -+ struct bcm2835_dmadev *d = ofdma->of_dma_data; -+ struct dma_chan *chan; -+ -+ chan = dma_get_any_slave_channel(&d->ddev); -+ if (!chan) ++ VCHIQ_STATE_T *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & ++ (VCHIQ_MAX_STATES - 1)]; ++ if (!state) + return NULL; + -+ /* Set DREQ from param */ -+ to_bcm2835_dma_chan(chan)->dreq = spec->args[0]; -+ -+ return chan; -+} -+#endif -+ -+static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, -+ struct dma_slave_caps *caps) -+{ -+ caps->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); -+ caps->dstn_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); -+ caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); -+ caps->cmd_pause = false; -+ caps->cmd_terminate = true; -+ -+ return 0; ++ return state->services[handle & (VCHIQ_MAX_SERVICES - 1)]; +} + -+static int bcm2835_dma_probe(struct platform_device *pdev) -+{ -+ struct bcm2835_dmadev *od; -+#ifdef CONFIG_OF -+ struct resource *res; -+ void __iomem *base; -+ uint32_t chans_available; -+#endif -+ int rc; -+ int i; -+ int irq; ++extern VCHIQ_SERVICE_T * ++find_service_by_handle(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++find_service_by_port(VCHIQ_STATE_T *state, int localport); ++ ++extern VCHIQ_SERVICE_T * ++find_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++find_closed_service_for_instance(VCHIQ_INSTANCE_T instance, ++ VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_SERVICE_T * ++next_service_by_instance(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance, ++ int *pidx); ++ ++extern void ++lock_service(VCHIQ_SERVICE_T *service); ++ ++extern void ++unlock_service(VCHIQ_SERVICE_T *service); ++ ++/* The following functions are called from vchiq_core, and external ++** implementations must be provided. */ ++ ++extern VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir); ++ ++extern void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk); ++ ++extern void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk); ++ ++extern VCHIQ_STATUS_T ++vchiq_copy_from_user(void *dst, const void *src, int size); ++ ++extern void ++remote_event_signal(REMOTE_EVENT_T *event); ++ ++void ++vchiq_platform_check_suspend(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_paused(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_resumed(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump(void *dump_context, const char *str, int len); ++ ++extern void ++vchiq_dump_platform_state(void *dump_context); ++ ++extern void ++vchiq_dump_platform_instances(void *dump_context); ++ ++extern void ++vchiq_dump_platform_service_state(void *dump_context, ++ VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_on_remote_use(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_on_remote_release(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_init_state(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_on_remote_use_active(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_release(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use_active(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, ++ VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate); ++ ++extern void ++vchiq_platform_handle_timeout(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate); + + -+ if (!pdev->dev.dma_mask) -+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; -+ -+ /* If CONFIG_OF is selected, device tree is used */ -+ /* hence the difference between probing */ -+ -+#ifndef CONFIG_OF -+ -+ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); -+ if (rc) -+ return rc; -+ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); -+ -+ -+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); -+ if (!od) -+ return -ENOMEM; -+ -+ pdev->dev.dma_parms = &od->dma_parms; -+ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); -+ -+ -+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); -+ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); -+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); -+ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; -+ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; -+ od->ddev.device_tx_status = bcm2835_dma_tx_status; -+ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; -+ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; -+ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; -+ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; -+ od->ddev.device_control = bcm2835_dma_control; -+ od->ddev.dev = &pdev->dev; -+ INIT_LIST_HEAD(&od->ddev.channels); -+ spin_lock_init(&od->lock); -+ -+ platform_set_drvdata(pdev, od); -+ -+ for (i = 0; i < 5; i++) { -+ void __iomem *chan_base; -+ int chan_id; -+ -+ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_LITE, -+ &chan_base, -+ &irq); -+ -+ if (chan_id < 0) -+ break; -+ -+ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); -+ if (rc) -+ goto err_no_dma; -+ } -+#else -+ rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); -+ if (rc) -+ return rc; -+ -+ -+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); -+ if (!od) -+ return -ENOMEM; -+ -+ pdev->dev.dma_parms = &od->dma_parms; -+ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); -+ -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ base = devm_ioremap_resource(&pdev->dev, res); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); -+ -+ od->base = base; -+ -+ -+ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); -+ dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask); -+ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); -+ od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources; -+ od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources; -+ od->ddev.device_tx_status = bcm2835_dma_tx_status; -+ od->ddev.device_issue_pending = bcm2835_dma_issue_pending; -+ od->ddev.device_slave_caps = bcm2835_dma_device_slave_caps; -+ od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic; -+ od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg; -+ od->ddev.device_control = bcm2835_dma_control; -+ od->ddev.dev = &pdev->dev; -+ INIT_LIST_HEAD(&od->ddev.channels); -+ spin_lock_init(&od->lock); -+ -+ platform_set_drvdata(pdev, od); -+ -+ -+ /* Request DMA channel mask from device tree */ -+ if (of_property_read_u32(pdev->dev.of_node, -+ "brcm,dma-channel-mask", -+ &chans_available)) { -+ dev_err(&pdev->dev, "Failed to get channel mask\n"); -+ rc = -EINVAL; -+ goto err_no_dma; -+ } -+ -+ -+ /* -+ * Do not use the FIQ and BULK channels, -+ * because they are used by the GPU. -+ */ -+ chans_available &= ~(BCM2835_DMA_FIQ_MASK | BCM2835_DMA_BULK_MASK); -+ -+ -+ for (i = 0; i < pdev->num_resources; i++) { -+ irq = platform_get_irq(pdev, i); -+ if (irq < 0) -+ break; -+ -+ if (chans_available & (1 << i)) { -+ rc = bcm2835_dma_chan_init(od, i, irq); -+ if (rc) -+ goto err_no_dma; -+ } -+ } -+ -+ dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i); -+ -+ /* Device-tree DMA controller registration */ -+ rc = of_dma_controller_register(pdev->dev.of_node, -+ bcm2835_dma_xlate, od); -+ if (rc) { -+ dev_err(&pdev->dev, "Failed to register DMA controller\n"); -+ goto err_no_dma; -+ } -+#endif -+ -+ rc = dma_async_device_register(&od->ddev); -+ if (rc) { -+ dev_err(&pdev->dev, -+ "Failed to register slave DMA engine device: %d\n", rc); -+ goto err_no_dma; -+ } -+ -+ dev_info(&pdev->dev, "Load BCM2835 DMA engine driver\n"); -+ -+ return 0; -+ -+err_no_dma: -+ bcm2835_dma_free(od); -+ return rc; -+} -+ -+static int bcm2835_dma_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_dmadev *od = platform_get_drvdata(pdev); -+ -+ dma_async_device_unregister(&od->ddev); -+ bcm2835_dma_free(od); -+ -+ return 0; -+} -+ -+#ifndef CONFIG_OF -+ -+ -+static struct platform_driver bcm2835_dma_driver = { -+ .probe = bcm2835_dma_probe, -+ .remove = bcm2835_dma_remove, -+ .driver = { -+ .name = "bcm2708-dmaengine", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static struct platform_device *pdev; -+ -+static const struct platform_device_info bcm2835_dma_dev_info = { -+ .name = "bcm2708-dmaengine", -+ .id = -1, -+}; -+ -+static int bcm2835_dma_init(void) -+{ -+ int rc = platform_driver_register(&bcm2835_dma_driver); -+ -+ if (rc == 0) { -+ pdev = platform_device_register_full(&bcm2835_dma_dev_info); -+ if (IS_ERR(pdev)) { -+ platform_driver_unregister(&bcm2835_dma_driver); -+ rc = PTR_ERR(pdev); -+ } -+ } -+ -+ return rc; -+} -+module_init(bcm2835_dma_init); /* preferable to subsys_initcall */ -+ -+static void __exit bcm2835_dma_exit(void) -+{ -+ platform_device_unregister(pdev); -+ platform_driver_unregister(&bcm2835_dma_driver); -+} -+module_exit(bcm2835_dma_exit); -+ -+#else -+ -+static struct platform_driver bcm2835_dma_driver = { -+ .probe = bcm2835_dma_probe, -+ .remove = bcm2835_dma_remove, -+ .driver = { -+ .name = "bcm2835-dma", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(bcm2835_dma_of_match), -+ }, -+}; -+ -+module_platform_driver(bcm2835_dma_driver); ++extern void ++vchiq_log_dump_mem(const char *label, uint32_t addr, const void *voidMem, ++ size_t numBytes); + +#endif -+ -+MODULE_ALIAS("platform:bcm2835-dma"); -+MODULE_DESCRIPTION("BCM2835 DMA engine driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_AUTHOR("Gellert Weisz "); -+MODULE_LICENSE("GPL v2"); - -From 1cd0b002163ef24b26370db2efdc928aab23357b Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:33:38 +0100 -Subject: [PATCH 031/172] ASoC: Add support for BCM2708 - -This driver adds support for digital audio (I2S) -for the BCM2708 SoC that is used by the -Raspberry Pi. External audio codecs can be -connected to the Raspberry Pi via P5 header. - -It relies on cyclic DMA engine support for BCM2708. - -Signed-off-by: Florian Meier - -ASoC: BCM2708: Add 24 bit support - -This adds 24 bit support to the I2S driver of the BCM2708. -Besides enabling the 24 bit flags, it includes two bug fixes: - -MMAP is not supported. Claiming this leads to strange issues -when the format of driver and file do not match. - -The datasheet states that the width extension bit should be set -for widths greater than 24, but greater or equal would be correct. -This follows from the definition of the width field. - -Signed-off-by: Florian Meier - -bcm2708-i2s: Update bclk_ratio to more correct values - -Move GPIO setup to hw_params. - -This is used to stop the I2S driver from breaking -the GPIO setup for other uses of the PCM interface - -Configure GPIOs for I2S based on revision/card settings - -With RPi model B+, assignment of the I2S GPIO pins has changed. -This patch uses the board revision to auto-detect the GPIOs used -for I2S. It also allows sound card drivers to set the GPIOs that -should be used. This is especially important with the Compute -Module. - -bcm2708-i2s: Avoid leak from iomap when accessing gpio - -bcm2708: Eliminate i2s debugfs directory error - -Qualify the two regmap ranges uses by bcm2708-i2s ('-i2s' and '-clk') -to avoid the name clash when registering debugfs entries. ---- - sound/soc/bcm/Kconfig | 11 + - sound/soc/bcm/Makefile | 4 + - sound/soc/bcm/bcm2708-i2s.c | 998 ++++++++++++++++++++++++++++++++++++++++++++ - sound/soc/bcm/bcm2708-i2s.h | 35 ++ - 4 files changed, 1048 insertions(+) - create mode 100644 sound/soc/bcm/bcm2708-i2s.c - create mode 100644 sound/soc/bcm/bcm2708-i2s.h - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 6a834e1..7e5b945 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -7,3 +7,14 @@ config SND_BCM2835_SOC_I2S - Say Y or M if you want to add support for codecs attached to - the BCM2835 I2S interface. You will also need - to select the audio interfaces to support below. -+ -+config SND_BCM2708_SOC_I2S -+ tristate "SoC Audio support for the Broadcom BCM2708 I2S module" -+ depends on MACH_BCM2708 -+ select REGMAP_MMIO -+ select SND_SOC_DMAENGINE_PCM -+ select SND_SOC_GENERIC_DMAENGINE_PCM -+ help -+ Say Y or M if you want to add support for codecs attached to -+ the BCM2708 I2S interface. You will also need -+ to select the audio interfaces to support below. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index bc816b7..f8bbe1f 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o - - obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o - -+# BCM2708 Platform Support -+snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o -+ -+obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o -diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c new file mode 100644 -index 0000000..0b5322a +index 0000000..7e03213 --- /dev/null -+++ b/sound/soc/bcm/bcm2708-i2s.c -@@ -0,0 +1,998 @@ -+/* -+ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.c +@@ -0,0 +1,383 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. + * -+ * Author: Florian Meier -+ * Copyright 2013 ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. + * -+ * Based on -+ * Raspberry Pi PCM I2S ALSA Driver -+ * Copyright (c) by Phil Poole 2013 ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. + * -+ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor -+ * Vladimir Barinov, -+ * Copyright (C) 2007 MontaVista Software, Inc., -+ * -+ * OMAP ALSA SoC DAI driver using McBSP port -+ * Copyright (C) 2008 Nokia Corporation -+ * Contact: Jarkko Nikula -+ * Peter Ujfalusi -+ * -+ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver -+ * Author: Timur Tabi -+ * Copyright 2007-2010 Freescale Semiconductor, Inc. -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -+ * General Public License for more details. ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + -+#include "bcm2708-i2s.h" + -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include ++#include ++#include "vchiq_core.h" ++#include "vchiq_arm.h" ++#include "vchiq_debugfs.h" + -+#include -+#include -+#include -+#include -+#include -+#include ++#ifdef CONFIG_DEBUG_FS + -+#include ++/**************************************************************************** ++* ++* log category entries ++* ++***************************************************************************/ ++#define DEBUGFS_WRITE_BUF_SIZE 256 + -+/* Clock registers */ -+#define BCM2708_CLK_PCMCTL_REG 0x00 -+#define BCM2708_CLK_PCMDIV_REG 0x04 ++#define VCHIQ_LOG_ERROR_STR "error" ++#define VCHIQ_LOG_WARNING_STR "warning" ++#define VCHIQ_LOG_INFO_STR "info" ++#define VCHIQ_LOG_TRACE_STR "trace" + -+/* Clock register settings */ -+#define BCM2708_CLK_PASSWD (0x5a000000) -+#define BCM2708_CLK_PASSWD_MASK (0xff000000) -+#define BCM2708_CLK_MASH(v) ((v) << 9) -+#define BCM2708_CLK_FLIP BIT(8) -+#define BCM2708_CLK_BUSY BIT(7) -+#define BCM2708_CLK_KILL BIT(5) -+#define BCM2708_CLK_ENAB BIT(4) -+#define BCM2708_CLK_SRC(v) (v) + -+#define BCM2708_CLK_SHIFT (12) -+#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) -+#define BCM2708_CLK_DIVF(v) (v) -+#define BCM2708_CLK_DIVF_MASK (0xFFF) ++/* Top-level debug info */ ++struct vchiq_debugfs_info { ++ /* Global 'vchiq' debugfs entry used by all instances */ ++ struct dentry *vchiq_cfg_dir; + -+enum { -+ BCM2708_CLK_MASH_0 = 0, -+ BCM2708_CLK_MASH_1, -+ BCM2708_CLK_MASH_2, -+ BCM2708_CLK_MASH_3, ++ /* one entry per client process */ ++ struct dentry *clients; ++ ++ /* log categories */ ++ struct dentry *log_categories; +}; + -+enum { -+ BCM2708_CLK_SRC_GND = 0, -+ BCM2708_CLK_SRC_OSC, -+ BCM2708_CLK_SRC_DBG0, -+ BCM2708_CLK_SRC_DBG1, -+ BCM2708_CLK_SRC_PLLA, -+ BCM2708_CLK_SRC_PLLC, -+ BCM2708_CLK_SRC_PLLD, -+ BCM2708_CLK_SRC_HDMI, ++static struct vchiq_debugfs_info debugfs_info; ++ ++/* Log category debugfs entries */ ++struct vchiq_debugfs_log_entry { ++ const char *name; ++ int *plevel; ++ struct dentry *dir; +}; + -+/* Most clocks are not useable (freq = 0) */ -+static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { -+ [BCM2708_CLK_SRC_GND] = 0, -+ [BCM2708_CLK_SRC_OSC] = 19200000, -+ [BCM2708_CLK_SRC_DBG0] = 0, -+ [BCM2708_CLK_SRC_DBG1] = 0, -+ [BCM2708_CLK_SRC_PLLA] = 0, -+ [BCM2708_CLK_SRC_PLLC] = 0, -+ [BCM2708_CLK_SRC_PLLD] = 500000000, -+ [BCM2708_CLK_SRC_HDMI] = 0, ++static struct vchiq_debugfs_log_entry vchiq_debugfs_log_entries[] = { ++ { "core", &vchiq_core_log_level }, ++ { "msg", &vchiq_core_msg_log_level }, ++ { "sync", &vchiq_sync_log_level }, ++ { "susp", &vchiq_susp_log_level }, ++ { "arm", &vchiq_arm_log_level }, +}; -+ -+/* I2S registers */ -+#define BCM2708_I2S_CS_A_REG 0x00 -+#define BCM2708_I2S_FIFO_A_REG 0x04 -+#define BCM2708_I2S_MODE_A_REG 0x08 -+#define BCM2708_I2S_RXC_A_REG 0x0c -+#define BCM2708_I2S_TXC_A_REG 0x10 -+#define BCM2708_I2S_DREQ_A_REG 0x14 -+#define BCM2708_I2S_INTEN_A_REG 0x18 -+#define BCM2708_I2S_INTSTC_A_REG 0x1c -+#define BCM2708_I2S_GRAY_REG 0x20 -+ -+/* I2S register settings */ -+#define BCM2708_I2S_STBY BIT(25) -+#define BCM2708_I2S_SYNC BIT(24) -+#define BCM2708_I2S_RXSEX BIT(23) -+#define BCM2708_I2S_RXF BIT(22) -+#define BCM2708_I2S_TXE BIT(21) -+#define BCM2708_I2S_RXD BIT(20) -+#define BCM2708_I2S_TXD BIT(19) -+#define BCM2708_I2S_RXR BIT(18) -+#define BCM2708_I2S_TXW BIT(17) -+#define BCM2708_I2S_CS_RXERR BIT(16) -+#define BCM2708_I2S_CS_TXERR BIT(15) -+#define BCM2708_I2S_RXSYNC BIT(14) -+#define BCM2708_I2S_TXSYNC BIT(13) -+#define BCM2708_I2S_DMAEN BIT(9) -+#define BCM2708_I2S_RXTHR(v) ((v) << 7) -+#define BCM2708_I2S_TXTHR(v) ((v) << 5) -+#define BCM2708_I2S_RXCLR BIT(4) -+#define BCM2708_I2S_TXCLR BIT(3) -+#define BCM2708_I2S_TXON BIT(2) -+#define BCM2708_I2S_RXON BIT(1) -+#define BCM2708_I2S_EN (1) -+ -+#define BCM2708_I2S_CLKDIS BIT(28) -+#define BCM2708_I2S_PDMN BIT(27) -+#define BCM2708_I2S_PDME BIT(26) -+#define BCM2708_I2S_FRXP BIT(25) -+#define BCM2708_I2S_FTXP BIT(24) -+#define BCM2708_I2S_CLKM BIT(23) -+#define BCM2708_I2S_CLKI BIT(22) -+#define BCM2708_I2S_FSM BIT(21) -+#define BCM2708_I2S_FSI BIT(20) -+#define BCM2708_I2S_FLEN(v) ((v) << 10) -+#define BCM2708_I2S_FSLEN(v) (v) -+ -+#define BCM2708_I2S_CHWEX BIT(15) -+#define BCM2708_I2S_CHEN BIT(14) -+#define BCM2708_I2S_CHPOS(v) ((v) << 4) -+#define BCM2708_I2S_CHWID(v) (v) -+#define BCM2708_I2S_CH1(v) ((v) << 16) -+#define BCM2708_I2S_CH2(v) (v) -+ -+#define BCM2708_I2S_TX_PANIC(v) ((v) << 24) -+#define BCM2708_I2S_RX_PANIC(v) ((v) << 16) -+#define BCM2708_I2S_TX(v) ((v) << 8) -+#define BCM2708_I2S_RX(v) (v) -+ -+#define BCM2708_I2S_INT_RXERR BIT(3) -+#define BCM2708_I2S_INT_TXERR BIT(2) -+#define BCM2708_I2S_INT_RXR BIT(1) -+#define BCM2708_I2S_INT_TXW BIT(0) -+ -+/* I2S DMA interface */ -+#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004 -+#define BCM2708_DMA_DREQ_PCM_TX 2 -+#define BCM2708_DMA_DREQ_PCM_RX 3 -+ -+/* I2S pin configuration */ -+static int bcm2708_i2s_gpio=BCM2708_I2S_GPIO_AUTO; -+ -+/* General device struct */ -+struct bcm2708_i2s_dev { -+ struct device *dev; -+ struct snd_dmaengine_dai_dma_data dma_data[2]; -+ unsigned int fmt; -+ unsigned int bclk_ratio; -+ -+ struct regmap *i2s_regmap; -+ struct regmap *clk_regmap; -+}; -+ -+void bcm2708_i2s_set_gpio(int gpio) { -+ bcm2708_i2s_gpio=gpio; -+} -+EXPORT_SYMBOL(bcm2708_i2s_set_gpio); ++static int n_log_entries = ++ sizeof(vchiq_debugfs_log_entries)/sizeof(vchiq_debugfs_log_entries[0]); + + -+static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev) ++static struct dentry *vchiq_clients_top(void); ++static struct dentry *vchiq_debugfs_top(void); ++ ++static int debugfs_log_show(struct seq_file *f, void *offset) +{ -+ /* Start the clock if in master mode */ -+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ int *levp = f->private; ++ char *log_value = NULL; + -+ switch (master) { -+ case SND_SOC_DAIFMT_CBS_CFS: -+ case SND_SOC_DAIFMT_CBS_CFM: -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ switch (*levp) { ++ case VCHIQ_LOG_ERROR: ++ log_value = VCHIQ_LOG_ERROR_STR; ++ break; ++ case VCHIQ_LOG_WARNING: ++ log_value = VCHIQ_LOG_WARNING_STR; ++ break; ++ case VCHIQ_LOG_INFO: ++ log_value = VCHIQ_LOG_INFO_STR; ++ break; ++ case VCHIQ_LOG_TRACE: ++ log_value = VCHIQ_LOG_TRACE_STR; + break; + default: + break; + } -+} + -+static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev) -+{ -+ uint32_t clkreg; -+ int timeout = 1000; -+ -+ /* Stop clock */ -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD); -+ -+ /* Wait for the BUSY flag going down */ -+ while (--timeout) { -+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); -+ if (!(clkreg & BCM2708_CLK_BUSY)) -+ break; -+ } -+ -+ if (!timeout) { -+ /* KILL the clock */ -+ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK, -+ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); -+ } -+} -+ -+static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev, -+ bool tx, bool rx) -+{ -+ int timeout = 1000; -+ uint32_t syncval; -+ uint32_t csreg; -+ uint32_t i2s_active_state; -+ uint32_t clkreg; -+ uint32_t clk_active_state; -+ uint32_t off; -+ uint32_t clr; -+ -+ off = tx ? BCM2708_I2S_TXON : 0; -+ off |= rx ? BCM2708_I2S_RXON : 0; -+ -+ clr = tx ? BCM2708_I2S_TXCLR : 0; -+ clr |= rx ? BCM2708_I2S_RXCLR : 0; -+ -+ /* Backup the current state */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON); -+ -+ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); -+ clk_active_state = clkreg & BCM2708_CLK_ENAB; -+ -+ /* Start clock if not running */ -+ if (!clk_active_state) { -+ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, -+ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, -+ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); -+ } -+ -+ /* Stop I2S module */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0); -+ -+ /* -+ * Clear the FIFOs -+ * Requires at least 2 PCM clock cycles to take effect -+ */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr); -+ -+ /* Wait for 2 PCM clock cycles */ -+ -+ /* -+ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back -+ * FIXME: This does not seem to work for slave mode! -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval); -+ syncval &= BCM2708_I2S_SYNC; -+ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_SYNC, ~syncval); -+ -+ /* Wait for the SYNC flag changing it's state */ -+ while (--timeout) { -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ if ((csreg & BCM2708_I2S_SYNC) != syncval) -+ break; -+ } -+ -+ if (!timeout) -+ dev_err(dev->dev, "I2S SYNC error!\n"); -+ -+ /* Stop clock if it was not running before */ -+ if (!clk_active_state) -+ bcm2708_i2s_stop_clock(dev); -+ -+ /* Restore I2S state */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state); -+} -+ -+static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai, -+ unsigned int fmt) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ dev->fmt = fmt; -+ return 0; -+} -+ -+static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, -+ unsigned int ratio) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ dev->bclk_ratio = ratio; -+ return 0; -+} -+ -+ -+static int bcm2708_i2s_set_function(unsigned offset, int function) -+{ -+ #define GPIOFSEL(x) (0x00+(x)*4) -+ void __iomem *gpio = __io_address(GPIO_BASE); -+ unsigned alt = function <= 3 ? function + 4: function == 4 ? 3 : 2; -+ unsigned gpiodir; -+ unsigned gpio_bank = offset / 10; -+ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ gpiodir = readl(gpio + GPIOFSEL(gpio_bank)); -+ gpiodir &= ~(7 << gpio_field_offset); -+ gpiodir |= alt << gpio_field_offset; -+ writel(gpiodir, gpio + GPIOFSEL(gpio_bank)); -+ return 0; -+} -+ -+static void bcm2708_i2s_setup_gpio(void) -+{ -+ /* -+ * This is the common way to handle the GPIO pins for -+ * the Raspberry Pi. -+ * TODO Better way would be to handle -+ * this in the device tree! -+ */ -+ int pin,pinconfig,startpin,alt; -+ -+ /* SPI is on different GPIOs on different boards */ -+ /* for Raspberry Pi B+, this is pin GPIO18-21, for original on 28-31 */ -+ if (bcm2708_i2s_gpio==BCM2708_I2S_GPIO_AUTO) { -+ if ((system_rev & 0xffffff) >= 0x10) { -+ /* Model B+ */ -+ pinconfig=BCM2708_I2S_GPIO_PIN18; -+ } else { -+ /* original */ -+ pinconfig=BCM2708_I2S_GPIO_PIN28; -+ } -+ } else { -+ pinconfig=bcm2708_i2s_gpio; -+ } -+ -+ if (pinconfig==BCM2708_I2S_GPIO_PIN18) { -+ startpin=18; -+ alt=BCM2708_I2S_GPIO_PIN18_ALT; -+ } else if (pinconfig==BCM2708_I2S_GPIO_PIN28) { -+ startpin=28; -+ alt=BCM2708_I2S_GPIO_PIN28_ALT; -+ } else { -+ printk(KERN_INFO "Can't configure I2S GPIOs, unknown pin mode for I2S: %i\n",pinconfig); -+ return; -+ } -+ -+ /* configure I2S pins to correct ALT mode */ -+ for (pin = startpin; pin <= startpin+3; pin++) { -+ bcm2708_i2s_set_function(pin, alt); -+ } -+} -+ -+static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ unsigned int sampling_rate = params_rate(params); -+ unsigned int data_length, data_delay, bclk_ratio; -+ unsigned int ch1pos, ch2pos, mode, format; -+ unsigned int mash = BCM2708_CLK_MASH_1; -+ unsigned int divi, divf, target_frequency; -+ int clk_src = -1; -+ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; -+ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS -+ || master == SND_SOC_DAIFMT_CBS_CFM); -+ -+ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS -+ || master == SND_SOC_DAIFMT_CBM_CFS); -+ uint32_t csreg; -+ -+ /* -+ * If a stream is already enabled, -+ * the registers are already set properly. -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); -+ -+ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON)) -+ return 0; -+ -+ -+ bcm2708_i2s_setup_gpio(); -+ -+ /* -+ * Adjust the data length according to the format. -+ * We prefill the half frame length with an integer -+ * divider of 2400 as explained at the clock settings. -+ * Maybe it is overwritten there, if the Integer mode -+ * does not apply. -+ */ -+ switch (params_format(params)) { -+ case SNDRV_PCM_FORMAT_S16_LE: -+ data_length = 16; -+ bclk_ratio = 50; -+ break; -+ case SNDRV_PCM_FORMAT_S24_LE: -+ data_length = 24; -+ bclk_ratio = 50; -+ break; -+ case SNDRV_PCM_FORMAT_S32_LE: -+ data_length = 32; -+ bclk_ratio = 100; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* If bclk_ratio already set, use that one. */ -+ if (dev->bclk_ratio) -+ bclk_ratio = dev->bclk_ratio; -+ -+ /* -+ * Clock Settings -+ * -+ * The target frequency of the bit clock is -+ * sampling rate * frame length -+ * -+ * Integer mode: -+ * Sampling rates that are multiples of 8000 kHz -+ * can be driven by the oscillator of 19.2 MHz -+ * with an integer divider as long as the frame length -+ * is an integer divider of 19200000/8000=2400 as set up above. -+ * This is no longer possible if the sampling rate -+ * is too high (e.g. 192 kHz), because the oscillator is too slow. -+ * -+ * MASH mode: -+ * For all other sampling rates, it is not possible to -+ * have an integer divider. Approximate the clock -+ * with the MASH module that induces a slight frequency -+ * variance. To minimize that it is best to have the fastest -+ * clock here. That is PLLD with 500 MHz. -+ */ -+ target_frequency = sampling_rate * bclk_ratio; -+ clk_src = BCM2708_CLK_SRC_OSC; -+ mash = BCM2708_CLK_MASH_0; -+ -+ if (bcm2708_clk_freq[clk_src] % target_frequency == 0 -+ && bit_master && frame_master) { -+ divi = bcm2708_clk_freq[clk_src] / target_frequency; -+ divf = 0; -+ } else { -+ uint64_t dividend; -+ -+ if (!dev->bclk_ratio) { -+ /* -+ * Overwrite bclk_ratio, because the -+ * above trick is not needed or can -+ * not be used. -+ */ -+ bclk_ratio = 2 * data_length; -+ } -+ -+ target_frequency = sampling_rate * bclk_ratio; -+ -+ clk_src = BCM2708_CLK_SRC_PLLD; -+ mash = BCM2708_CLK_MASH_1; -+ -+ dividend = bcm2708_clk_freq[clk_src]; -+ dividend <<= BCM2708_CLK_SHIFT; -+ do_div(dividend, target_frequency); -+ divi = dividend >> BCM2708_CLK_SHIFT; -+ divf = dividend & BCM2708_CLK_DIVF_MASK; -+ } -+ -+ /* Set clock divider */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_DIVI(divi) -+ | BCM2708_CLK_DIVF(divf)); -+ -+ /* Setup clock, but don't start it yet */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_MASH(mash) -+ | BCM2708_CLK_SRC(clk_src)); -+ -+ /* Setup the frame format */ -+ format = BCM2708_I2S_CHEN; -+ -+ if (data_length >= 24) -+ format |= BCM2708_I2S_CHWEX; -+ -+ format |= BCM2708_I2S_CHWID((data_length-8)&0xf); -+ -+ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { -+ case SND_SOC_DAIFMT_I2S: -+ data_delay = 1; -+ break; -+ default: -+ /* -+ * TODO -+ * Others are possible but are not implemented at the moment. -+ */ -+ dev_err(dev->dev, "%s:bad format\n", __func__); -+ return -EINVAL; -+ } -+ -+ ch1pos = data_delay; -+ ch2pos = bclk_ratio / 2 + data_delay; -+ -+ switch (params_channels(params)) { -+ case 2: -+ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format); -+ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos)); -+ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos)); -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ /* -+ * Set format for both streams. -+ * We cannot set another frame length -+ * (and therefore word length) anyway, -+ * so the format will be the same. -+ */ -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format); -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format); -+ -+ /* Setup the I2S mode */ -+ mode = 0; -+ -+ if (data_length <= 16) { -+ /* -+ * Use frame packed mode (2 channels per 32 bit word) -+ * We cannot set another frame length in the second stream -+ * (and therefore word length) anyway, -+ * so the format will be the same. -+ */ -+ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP; -+ } -+ -+ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1); -+ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2); -+ -+ /* Master or slave? */ -+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { -+ case SND_SOC_DAIFMT_CBS_CFS: -+ /* CPU is master */ -+ break; -+ case SND_SOC_DAIFMT_CBM_CFS: -+ /* -+ * CODEC is bit clock master -+ * CPU is frame master -+ */ -+ mode |= BCM2708_I2S_CLKM; -+ break; -+ case SND_SOC_DAIFMT_CBS_CFM: -+ /* -+ * CODEC is frame master -+ * CPU is bit clock master -+ */ -+ mode |= BCM2708_I2S_FSM; -+ break; -+ case SND_SOC_DAIFMT_CBM_CFM: -+ /* CODEC is master */ -+ mode |= BCM2708_I2S_CLKM; -+ mode |= BCM2708_I2S_FSM; -+ break; -+ default: -+ dev_err(dev->dev, "%s:bad master\n", __func__); -+ return -EINVAL; -+ } -+ -+ /* -+ * Invert clocks? -+ * -+ * The BCM approach seems to be inverted to the classical I2S approach. -+ */ -+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { -+ case SND_SOC_DAIFMT_NB_NF: -+ /* None. Therefore, both for BCM */ -+ mode |= BCM2708_I2S_CLKI; -+ mode |= BCM2708_I2S_FSI; -+ break; -+ case SND_SOC_DAIFMT_IB_IF: -+ /* Both. Therefore, none for BCM */ -+ break; -+ case SND_SOC_DAIFMT_NB_IF: -+ /* -+ * Invert only frame sync. Therefore, -+ * invert only bit clock for BCM -+ */ -+ mode |= BCM2708_I2S_CLKI; -+ break; -+ case SND_SOC_DAIFMT_IB_NF: -+ /* -+ * Invert only bit clock. Therefore, -+ * invert only frame sync for BCM -+ */ -+ mode |= BCM2708_I2S_FSI; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode); -+ -+ /* Setup the DMA parameters */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_RXTHR(1) -+ | BCM2708_I2S_TXTHR(1) -+ | BCM2708_I2S_DMAEN, 0xffffffff); -+ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG, -+ BCM2708_I2S_TX_PANIC(0x10) -+ | BCM2708_I2S_RX_PANIC(0x30) -+ | BCM2708_I2S_TX(0x30) -+ | BCM2708_I2S_RX(0x20), 0xffffffff); -+ -+ /* Clear FIFOs */ -+ bcm2708_i2s_clear_fifos(dev, true, true); ++ seq_printf(f, "%s\n", log_value ? log_value : "(null)"); + + return 0; +} + -+static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++static int debugfs_log_open(struct inode *inode, struct file *file) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ uint32_t cs_reg; -+ -+ bcm2708_i2s_start_clock(dev); -+ -+ /* -+ * Clear both FIFOs if the one that should be started -+ * is not empty at the moment. This should only happen -+ * after overrun. Otherwise, hw_params would have cleared -+ * the FIFO. -+ */ -+ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg); -+ -+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK -+ && !(cs_reg & BCM2708_I2S_TXE)) -+ bcm2708_i2s_clear_fifos(dev, true, false); -+ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE -+ && (cs_reg & BCM2708_I2S_RXD)) -+ bcm2708_i2s_clear_fifos(dev, false, true); -+ -+ return 0; ++ return single_open(file, debugfs_log_show, inode->i_private); +} + -+static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev, -+ struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++static int debugfs_log_write(struct file *file, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) +{ -+ uint32_t mask; ++ struct seq_file *f = (struct seq_file *)file->private_data; ++ int *levp = f->private; ++ char kbuf[DEBUGFS_WRITE_BUF_SIZE + 1]; + -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ mask = BCM2708_I2S_RXON; ++ memset(kbuf, 0, DEBUGFS_WRITE_BUF_SIZE + 1); ++ if (count >= DEBUGFS_WRITE_BUF_SIZE) ++ count = DEBUGFS_WRITE_BUF_SIZE; ++ ++ if (copy_from_user(kbuf, buffer, count) != 0) ++ return -EFAULT; ++ kbuf[count - 1] = 0; ++ ++ if (strncmp("error", kbuf, strlen("error")) == 0) ++ *levp = VCHIQ_LOG_ERROR; ++ else if (strncmp("warning", kbuf, strlen("warning")) == 0) ++ *levp = VCHIQ_LOG_WARNING; ++ else if (strncmp("info", kbuf, strlen("info")) == 0) ++ *levp = VCHIQ_LOG_INFO; ++ else if (strncmp("trace", kbuf, strlen("trace")) == 0) ++ *levp = VCHIQ_LOG_TRACE; + else -+ mask = BCM2708_I2S_TXON; ++ *levp = VCHIQ_LOG_DEFAULT; + -+ regmap_update_bits(dev->i2s_regmap, -+ BCM2708_I2S_CS_A_REG, mask, 0); ++ *ppos += count; + -+ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ -+ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT)) -+ bcm2708_i2s_stop_clock(dev); ++ return count; +} + -+static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) ++static const struct file_operations debugfs_log_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_log_open, ++ .write = debugfs_log_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* create an entry under /vchiq/log for each log category */ ++static int vchiq_debugfs_create_log_entries(struct dentry *top) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ uint32_t mask; ++ struct dentry *dir; ++ size_t i; ++ int ret = 0; ++ dir = debugfs_create_dir("log", vchiq_debugfs_top()); ++ if (!dir) ++ return -ENOMEM; ++ debugfs_info.log_categories = dir; + -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ case SNDRV_PCM_TRIGGER_RESUME: -+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: -+ bcm2708_i2s_start_clock(dev); ++ for (i = 0; i < n_log_entries; i++) { ++ void *levp = (void *)vchiq_debugfs_log_entries[i].plevel; ++ dir = debugfs_create_file(vchiq_debugfs_log_entries[i].name, ++ 0644, ++ debugfs_info.log_categories, ++ levp, ++ &debugfs_log_fops); ++ if (!dir) { ++ ret = -ENOMEM; ++ break; ++ } + -+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) -+ mask = BCM2708_I2S_RXON; -+ else -+ mask = BCM2708_I2S_TXON; ++ vchiq_debugfs_log_entries[i].dir = dir; ++ } ++ return ret; ++} + -+ regmap_update_bits(dev->i2s_regmap, -+ BCM2708_I2S_CS_A_REG, mask, mask); ++static int debugfs_usecount_show(struct seq_file *f, void *offset) ++{ ++ VCHIQ_INSTANCE_T instance = f->private; ++ int use_count; ++ ++ use_count = vchiq_instance_get_use_count(instance); ++ seq_printf(f, "%d\n", use_count); ++ ++ return 0; ++} ++ ++static int debugfs_usecount_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debugfs_usecount_show, inode->i_private); ++} ++ ++static const struct file_operations debugfs_usecount_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_usecount_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int debugfs_trace_show(struct seq_file *f, void *offset) ++{ ++ VCHIQ_INSTANCE_T instance = f->private; ++ int trace; ++ ++ trace = vchiq_instance_get_trace(instance); ++ seq_printf(f, "%s\n", trace ? "Y" : "N"); ++ ++ return 0; ++} ++ ++static int debugfs_trace_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debugfs_trace_show, inode->i_private); ++} ++ ++static int debugfs_trace_write(struct file *file, ++ const char __user *buffer, ++ size_t count, loff_t *ppos) ++{ ++ struct seq_file *f = (struct seq_file *)file->private_data; ++ VCHIQ_INSTANCE_T instance = f->private; ++ char firstchar; ++ ++ if (copy_from_user(&firstchar, buffer, 1) != 0) ++ return -EFAULT; ++ ++ switch (firstchar) { ++ case 'Y': ++ case 'y': ++ case '1': ++ vchiq_instance_set_trace(instance, 1); + break; -+ -+ case SNDRV_PCM_TRIGGER_STOP: -+ case SNDRV_PCM_TRIGGER_SUSPEND: -+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -+ bcm2708_i2s_stop(dev, substream, dai); ++ case 'N': ++ case 'n': ++ case '0': ++ vchiq_instance_set_trace(instance, 0); + break; + default: -+ return -EINVAL; ++ break; + } + ++ *ppos += count; ++ ++ return count; ++} ++ ++static const struct file_operations debugfs_trace_fops = { ++ .owner = THIS_MODULE, ++ .open = debugfs_trace_open, ++ .write = debugfs_trace_write, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++/* add an instance (process) to the debugfs entries */ ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) ++{ ++ char pidstr[16]; ++ struct dentry *top, *use_count, *trace; ++ struct dentry *clients = vchiq_clients_top(); ++ ++ snprintf(pidstr, sizeof(pidstr), "%d", ++ vchiq_instance_get_pid(instance)); ++ ++ top = debugfs_create_dir(pidstr, clients); ++ if (!top) ++ goto fail_top; ++ ++ use_count = debugfs_create_file("use_count", ++ 0444, top, ++ instance, ++ &debugfs_usecount_fops); ++ if (!use_count) ++ goto fail_use_count; ++ ++ trace = debugfs_create_file("trace", ++ 0644, top, ++ instance, ++ &debugfs_trace_fops); ++ if (!trace) ++ goto fail_trace; ++ ++ vchiq_instance_get_debugfs_node(instance)->dentry = top; ++ ++ return 0; ++ ++fail_trace: ++ debugfs_remove(use_count); ++fail_use_count: ++ debugfs_remove(top); ++fail_top: ++ return -ENOMEM; ++} ++ ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_DEBUGFS_NODE_T *node = vchiq_instance_get_debugfs_node(instance); ++ debugfs_remove_recursive(node->dentry); ++} ++ ++ ++int vchiq_debugfs_init(void) ++{ ++ BUG_ON(debugfs_info.vchiq_cfg_dir != NULL); ++ ++ debugfs_info.vchiq_cfg_dir = debugfs_create_dir("vchiq", NULL); ++ if (debugfs_info.vchiq_cfg_dir == NULL) ++ goto fail; ++ ++ debugfs_info.clients = debugfs_create_dir("clients", ++ vchiq_debugfs_top()); ++ if (!debugfs_info.clients) ++ goto fail; ++ ++ if (vchiq_debugfs_create_log_entries(vchiq_debugfs_top()) != 0) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ vchiq_debugfs_deinit(); ++ vchiq_log_error(vchiq_arm_log_level, ++ "%s: failed to create debugfs directory", ++ __func__); ++ ++ return -ENOMEM; ++} ++ ++/* remove all the debugfs entries */ ++void vchiq_debugfs_deinit(void) ++{ ++ debugfs_remove_recursive(vchiq_debugfs_top()); ++} ++ ++static struct dentry *vchiq_clients_top(void) ++{ ++ return debugfs_info.clients; ++} ++ ++static struct dentry *vchiq_debugfs_top(void) ++{ ++ BUG_ON(debugfs_info.vchiq_cfg_dir == NULL); ++ return debugfs_info.vchiq_cfg_dir; ++} ++ ++#else /* CONFIG_DEBUG_FS */ ++ ++int vchiq_debugfs_init(void) ++{ + return 0; +} + -+static int bcm2708_i2s_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++void vchiq_debugfs_deinit(void) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ if (dai->active) -+ return 0; -+ -+ /* Should this still be running stop it */ -+ bcm2708_i2s_stop_clock(dev); -+ -+ /* Enable PCM block */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_EN, BCM2708_I2S_EN); -+ -+ /* -+ * Disable STBY. -+ * Requires at least 4 PCM clock cycles to take effect. -+ */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_STBY, BCM2708_I2S_STBY); ++} + ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance) ++{ + return 0; +} + -+static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance) +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); -+ -+ bcm2708_i2s_stop(dev, substream, dai); -+ -+ /* If both streams are stopped, disable module and clock */ -+ if (dai->active) -+ return; -+ -+ /* Disable the module */ -+ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, -+ BCM2708_I2S_EN, 0); -+ -+ /* -+ * Stopping clock is necessary, because stop does -+ * not stop the clock when SND_SOC_DAIFMT_CONT -+ */ -+ bcm2708_i2s_stop_clock(dev); +} + -+static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = { -+ .startup = bcm2708_i2s_startup, -+ .shutdown = bcm2708_i2s_shutdown, -+ .prepare = bcm2708_i2s_prepare, -+ .trigger = bcm2708_i2s_trigger, -+ .hw_params = bcm2708_i2s_hw_params, -+ .set_fmt = bcm2708_i2s_set_dai_fmt, -+ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio -+}; ++#endif /* CONFIG_DEBUG_FS */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +new file mode 100644 +index 0000000..4d6a378 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_debugfs.h +@@ -0,0 +1,52 @@ ++/** ++ * Copyright (c) 2014 Raspberry Pi (Trading) Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + -+static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) ++#ifndef VCHIQ_DEBUGFS_H ++#define VCHIQ_DEBUGFS_H ++ ++#include "vchiq_core.h" ++ ++typedef struct vchiq_debugfs_node_struct +{ -+ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ struct dentry *dentry; ++} VCHIQ_DEBUGFS_NODE_T; + -+ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; -+ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; ++int vchiq_debugfs_init(void); + -+ return 0; ++void vchiq_debugfs_deinit(void); ++ ++int vchiq_debugfs_add_instance(VCHIQ_INSTANCE_T instance); ++ ++void vchiq_debugfs_remove_instance(VCHIQ_INSTANCE_T instance); ++ ++#endif /* VCHIQ_DEBUGFS_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion +new file mode 100644 +index 0000000..9f5b634 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_genversion +@@ -0,0 +1,87 @@ ++#!/usr/bin/perl -w ++ ++use strict; ++ ++# ++# Generate a version from available information ++# ++ ++my $prefix = shift @ARGV; ++my $root = shift @ARGV; ++ ++ ++if ( not defined $root ) { ++ die "usage: $0 prefix root-dir\n"; +} + -+static struct snd_soc_dai_driver bcm2708_i2s_dai = { -+ .name = "bcm2708-i2s", -+ .probe = bcm2708_i2s_dai_probe, -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE -+ }, -+ .capture = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE -+ | SNDRV_PCM_FMTBIT_S24_LE -+ | SNDRV_PCM_FMTBIT_S32_LE -+ }, -+ .ops = &bcm2708_i2s_dai_ops, -+ .symmetric_rates = 1 -+}; -+ -+static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case BCM2708_I2S_CS_A_REG: -+ case BCM2708_I2S_FIFO_A_REG: -+ case BCM2708_I2S_INTSTC_A_REG: -+ case BCM2708_I2S_GRAY_REG: -+ return true; -+ default: -+ return false; -+ }; ++if ( ! -d $root ) { ++ die "root directory $root not found\n"; +} + -+static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg) -+{ -+ switch (reg) { -+ case BCM2708_I2S_FIFO_A_REG: -+ return true; -+ default: -+ return false; -+ }; ++my $version = "unknown"; ++my $tainted = ""; ++ ++if ( -d "$root/.git" ) { ++ # attempt to work out git version. only do so ++ # on a linux build host, as cygwin builds are ++ # already slow enough ++ ++ if ( -f "/usr/bin/git" || -f "/usr/local/bin/git" ) { ++ if (not open(F, "git --git-dir $root/.git rev-parse --verify HEAD|")) { ++ $version = "no git version"; ++ } ++ else { ++ $version = ; ++ $version =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++ $version =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ } ++ ++ if (open(G, "git --git-dir $root/.git status --porcelain|")) { ++ $tainted = ; ++ $tainted =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++ $tainted =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ if (length $tainted) { ++ $version = join ' ', $version, "(tainted)"; ++ } ++ else { ++ $version = join ' ', $version, "(clean)"; ++ } ++ } ++ } +} + -+static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg) ++my $hostname = `hostname`; ++$hostname =~ s/[ \r\n]*$//; # chomp may not be enough (cygwin). ++$hostname =~ s/^[ \r\n]*//; # chomp may not be enough (cygwin). ++ ++ ++print STDERR "Version $version\n"; ++print < ++ ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_hostname, "$hostname" ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_version, "$version" ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_time, __TIME__ ); ++VC_DEBUG_DECLARE_STRING_VAR( ${prefix}_build_date, __DATE__ ); ++ ++const char *vchiq_get_build_hostname( void ) +{ -+ switch (reg) { -+ case BCM2708_CLK_PCMCTL_REG: -+ return true; -+ default: -+ return false; -+ }; ++ return vchiq_build_hostname; +} + -+static const struct regmap_config bcm2708_regmap_config[] = { -+ { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = BCM2708_I2S_GRAY_REG, -+ .precious_reg = bcm2708_i2s_precious_reg, -+ .volatile_reg = bcm2708_i2s_volatile_reg, -+ .cache_type = REGCACHE_RBTREE, -+ .name = "i2s", -+ }, -+ { -+ .reg_bits = 32, -+ .reg_stride = 4, -+ .val_bits = 32, -+ .max_register = BCM2708_CLK_PCMDIV_REG, -+ .volatile_reg = bcm2708_clk_volatile_reg, -+ .cache_type = REGCACHE_RBTREE, -+ .name = "clk", -+ }, -+}; -+ -+static const struct snd_soc_component_driver bcm2708_i2s_component = { -+ .name = "bcm2708-i2s-comp", -+}; -+ -+static const struct snd_pcm_hardware bcm2708_pcm_hardware = { -+ .info = SNDRV_PCM_INFO_INTERLEAVED | -+ SNDRV_PCM_INFO_JOINT_DUPLEX, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE, -+ .period_bytes_min = 32, -+ .period_bytes_max = 64 * PAGE_SIZE, -+ .periods_min = 2, -+ .periods_max = 255, -+ .buffer_bytes_max = 128 * PAGE_SIZE, -+}; -+ -+static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = { -+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, -+ .pcm_hardware = &bcm2708_pcm_hardware, -+ .prealloc_buffer_size = 256 * PAGE_SIZE, -+}; -+ -+ -+static int bcm2708_i2s_probe(struct platform_device *pdev) ++const char *vchiq_get_build_version( void ) +{ -+ struct bcm2708_i2s_dev *dev; -+ int i; -+ int ret; -+ struct regmap *regmap[2]; -+ struct resource *mem[2]; ++ return vchiq_build_version; ++} + -+ /* Request both ioareas */ -+ for (i = 0; i <= 1; i++) { -+ void __iomem *base; ++const char *vchiq_get_build_date( void ) ++{ ++ return vchiq_build_date; ++} + -+ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); -+ base = devm_ioremap_resource(&pdev->dev, mem[i]); -+ if (IS_ERR(base)) -+ return PTR_ERR(base); ++const char *vchiq_get_build_time( void ) ++{ ++ return vchiq_build_time; ++} ++EOF +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +new file mode 100644 +index 0000000..8067bbe +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +@@ -0,0 +1,189 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + -+ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, -+ &bcm2708_regmap_config[i]); -+ if (IS_ERR(regmap[i])) { -+ dev_err(&pdev->dev, "I2S probe: regmap init failed\n"); -+ return PTR_ERR(regmap[i]); ++#ifndef VCHIQ_IF_H ++#define VCHIQ_IF_H ++ ++#include "interface/vchi/vchi_mh.h" ++ ++#define VCHIQ_SERVICE_HANDLE_INVALID 0 ++ ++#define VCHIQ_SLOT_SIZE 4096 ++#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) ++#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ ++ ++#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) \ ++ (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) ++#define VCHIQ_GET_SERVICE_USERDATA(service) vchiq_get_service_userdata(service) ++#define VCHIQ_GET_SERVICE_FOURCC(service) vchiq_get_service_fourcc(service) ++ ++typedef enum { ++ VCHIQ_SERVICE_OPENED, /* service, -, - */ ++ VCHIQ_SERVICE_CLOSED, /* service, -, - */ ++ VCHIQ_MESSAGE_AVAILABLE, /* service, header, - */ ++ VCHIQ_BULK_TRANSMIT_DONE, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_RECEIVE_DONE, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_TRANSMIT_ABORTED, /* service, -, bulk_userdata */ ++ VCHIQ_BULK_RECEIVE_ABORTED /* service, -, bulk_userdata */ ++} VCHIQ_REASON_T; ++ ++typedef enum { ++ VCHIQ_ERROR = -1, ++ VCHIQ_SUCCESS = 0, ++ VCHIQ_RETRY = 1 ++} VCHIQ_STATUS_T; ++ ++typedef enum { ++ VCHIQ_BULK_MODE_CALLBACK, ++ VCHIQ_BULK_MODE_BLOCKING, ++ VCHIQ_BULK_MODE_NOCALLBACK, ++ VCHIQ_BULK_MODE_WAITING /* Reserved for internal use */ ++} VCHIQ_BULK_MODE_T; ++ ++typedef enum { ++ VCHIQ_SERVICE_OPTION_AUTOCLOSE, ++ VCHIQ_SERVICE_OPTION_SLOT_QUOTA, ++ VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA, ++ VCHIQ_SERVICE_OPTION_SYNCHRONOUS, ++ VCHIQ_SERVICE_OPTION_TRACE ++} VCHIQ_SERVICE_OPTION_T; ++ ++typedef struct vchiq_header_struct { ++ /* The message identifier - opaque to applications. */ ++ int msgid; ++ ++ /* Size of message data. */ ++ unsigned int size; ++ ++ char data[0]; /* message */ ++} VCHIQ_HEADER_T; ++ ++typedef struct { ++ const void *data; ++ unsigned int size; ++} VCHIQ_ELEMENT_T; ++ ++typedef unsigned int VCHIQ_SERVICE_HANDLE_T; ++ ++typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, ++ VCHIQ_SERVICE_HANDLE_T, void *); ++ ++typedef struct vchiq_service_base_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++} VCHIQ_SERVICE_BASE_T; ++ ++typedef struct vchiq_service_params_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++ short version; /* Increment for non-trivial changes */ ++ short version_min; /* Update for incompatible changes */ ++} VCHIQ_SERVICE_PARAMS_T; ++ ++typedef struct vchiq_config_struct { ++ unsigned int max_msg_size; ++ unsigned int bulk_threshold; /* The message size above which it ++ is better to use a bulk transfer ++ (<= max_msg_size) */ ++ unsigned int max_outstanding_bulks; ++ unsigned int max_services; ++ short version; /* The version of VCHIQ */ ++ short version_min; /* The minimum compatible version of VCHIQ */ ++} VCHIQ_CONFIG_T; ++ ++typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; ++typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void *cb_arg); ++ ++extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); ++extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_use_service_no_resume( ++ VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); ++ ++extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, ++ const VCHIQ_ELEMENT_T *elements, unsigned int count); ++extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, ++ VCHIQ_HEADER_T *header); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, ++ const void *data, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, ++ void *data, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle( ++ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, ++ const void *offset, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle( ++ VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, ++ void *offset, unsigned int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, ++ const void *data, unsigned int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, ++ void *data, unsigned int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, ++ VCHI_MEM_HANDLE_T handle, const void *offset, unsigned int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, ++ VCHI_MEM_HANDLE_T handle, void *offset, unsigned int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode); ++extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); ++extern void *vchiq_get_service_userdata(VCHIQ_SERVICE_HANDLE_T service); ++extern int vchiq_get_service_fourcc(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, VCHIQ_CONFIG_T *pconfig); ++extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, ++ VCHIQ_SERVICE_OPTION_T option, int value); ++ ++extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, ++ VCHIQ_REMOTE_USE_CALLBACK_T callback, void *cb_arg); ++extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T vchiq_dump_phys_mem(VCHIQ_SERVICE_HANDLE_T service, ++ void *ptr, size_t num_bytes); ++ ++extern VCHIQ_STATUS_T vchiq_get_peer_version(VCHIQ_SERVICE_HANDLE_T handle, ++ short *peer_version); ++ ++#endif /* VCHIQ_IF_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +new file mode 100644 +index 0000000..6137ae9 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +@@ -0,0 +1,131 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_IOCTLS_H ++#define VCHIQ_IOCTLS_H ++ ++#include ++#include "vchiq_if.h" ++ ++#define VCHIQ_IOC_MAGIC 0xc4 ++#define VCHIQ_INVALID_HANDLE (~0) ++ ++typedef struct { ++ VCHIQ_SERVICE_PARAMS_T params; ++ int is_open; ++ int is_vchi; ++ unsigned int handle; /* OUT */ ++} VCHIQ_CREATE_SERVICE_T; ++ ++typedef struct { ++ unsigned int handle; ++ unsigned int count; ++ const VCHIQ_ELEMENT_T *elements; ++} VCHIQ_QUEUE_MESSAGE_T; ++ ++typedef struct { ++ unsigned int handle; ++ void *data; ++ unsigned int size; ++ void *userdata; ++ VCHIQ_BULK_MODE_T mode; ++} VCHIQ_QUEUE_BULK_TRANSFER_T; ++ ++typedef struct { ++ VCHIQ_REASON_T reason; ++ VCHIQ_HEADER_T *header; ++ void *service_userdata; ++ void *bulk_userdata; ++} VCHIQ_COMPLETION_DATA_T; ++ ++typedef struct { ++ unsigned int count; ++ VCHIQ_COMPLETION_DATA_T *buf; ++ unsigned int msgbufsize; ++ unsigned int msgbufcount; /* IN/OUT */ ++ void **msgbufs; ++} VCHIQ_AWAIT_COMPLETION_T; ++ ++typedef struct { ++ unsigned int handle; ++ int blocking; ++ unsigned int bufsize; ++ void *buf; ++} VCHIQ_DEQUEUE_MESSAGE_T; ++ ++typedef struct { ++ unsigned int config_size; ++ VCHIQ_CONFIG_T *pconfig; ++} VCHIQ_GET_CONFIG_T; ++ ++typedef struct { ++ unsigned int handle; ++ VCHIQ_SERVICE_OPTION_T option; ++ int value; ++} VCHIQ_SET_SERVICE_OPTION_T; ++ ++typedef struct { ++ void *virt_addr; ++ size_t num_bytes; ++} VCHIQ_DUMP_MEM_T; ++ ++#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) ++#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) ++#define VCHIQ_IOC_CREATE_SERVICE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) ++#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) ++#define VCHIQ_IOC_QUEUE_MESSAGE \ ++ _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) ++#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT \ ++ _IOWR(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_QUEUE_BULK_RECEIVE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_AWAIT_COMPLETION \ ++ _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) ++#define VCHIQ_IOC_DEQUEUE_MESSAGE \ ++ _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) ++#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) ++#define VCHIQ_IOC_GET_CONFIG \ ++ _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) ++#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) ++#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) ++#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) ++#define VCHIQ_IOC_SET_SERVICE_OPTION \ ++ _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) ++#define VCHIQ_IOC_DUMP_PHYS_MEM \ ++ _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) ++#define VCHIQ_IOC_LIB_VERSION _IO(VCHIQ_IOC_MAGIC, 16) ++#define VCHIQ_IOC_CLOSE_DELIVERED _IO(VCHIQ_IOC_MAGIC, 17) ++#define VCHIQ_IOC_MAX 17 ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +new file mode 100644 +index 0000000..25e7011 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +@@ -0,0 +1,458 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#include ++#include ++#include ++ ++#include "vchiq_core.h" ++#include "vchiq_arm.h" ++#include "vchiq_killable.h" ++ ++/* ---- Public Variables ------------------------------------------------- */ ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++struct bulk_waiter_node { ++ struct bulk_waiter bulk_waiter; ++ int pid; ++ struct list_head list; ++}; ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ ++ int connected; ++ ++ struct list_head bulk_waiter_list; ++ struct mutex bulk_waiter_list_mutex; ++}; ++ ++static VCHIQ_STATUS_T ++vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, VCHIQ_BULK_DIR_T dir); ++ ++/**************************************************************************** ++* ++* vchiq_initialise ++* ++***************************************************************************/ ++#define VCHIQ_INIT_RETRIES 10 ++VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *instanceOut) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance = NULL; ++ int i; ++ ++ vchiq_log_trace(vchiq_core_log_level, "%s called", __func__); ++ ++ /* VideoCore may not be ready due to boot up timing. ++ It may never be ready if kernel and firmware are mismatched, so don't block forever. */ ++ for (i=0; i0) { ++ vchiq_log_warning(vchiq_core_log_level, ++ "%s: videocore initialized after %d retries\n", __func__, i); ++ } ++ ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%s: error allocating vchiq instance\n", __func__); ++ goto failed; ++ } ++ ++ instance->connected = 0; ++ instance->state = state; ++ mutex_init(&instance->bulk_waiter_list_mutex); ++ INIT_LIST_HEAD(&instance->bulk_waiter_list); ++ ++ *instanceOut = instance; ++ ++ status = VCHIQ_SUCCESS; ++ ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_initialise); ++ ++/**************************************************************************** ++* ++* vchiq_shutdown ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ if (mutex_lock_interruptible(&state->mutex) != 0) ++ return VCHIQ_RETRY; ++ ++ /* Remove all services */ ++ status = vchiq_shutdown_internal(state, instance); ++ ++ mutex_unlock(&state->mutex); ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ if (status == VCHIQ_SUCCESS) { ++ struct list_head *pos, *next; ++ list_for_each_safe(pos, next, ++ &instance->bulk_waiter_list) { ++ struct bulk_waiter_node *waiter; ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); ++ vchiq_log_info(vchiq_arm_log_level, ++ "bulk_waiter - cleaned up %x " ++ "for pid %d", ++ (unsigned int)waiter, waiter->pid); ++ kfree(waiter); ++ } ++ kfree(instance); ++ } ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_shutdown); ++ ++/**************************************************************************** ++* ++* vchiq_is_connected ++* ++***************************************************************************/ ++ ++int vchiq_is_connected(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->connected; ++} ++ ++/**************************************************************************** ++* ++* vchiq_connect ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ if (mutex_lock_interruptible(&state->mutex) != 0) { ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s: call to mutex_lock failed", __func__); ++ status = VCHIQ_RETRY; ++ goto failed; ++ } ++ status = vchiq_connect_internal(state, instance); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ ++ mutex_unlock(&state->mutex); ++ ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_connect); ++ ++/**************************************************************************** ++* ++* vchiq_add_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_add_service( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *phandle) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service = NULL; ++ int srvstate; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; ++ ++ srvstate = vchiq_is_connected(instance) ++ ? VCHIQ_SRVSTATE_LISTENING ++ : VCHIQ_SRVSTATE_HIDDEN; ++ ++ service = vchiq_add_service_internal( ++ state, ++ params, ++ srvstate, ++ instance, ++ NULL); ++ ++ if (service) { ++ *phandle = service->handle; ++ status = VCHIQ_SUCCESS; ++ } else ++ status = VCHIQ_ERROR; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); ++ ++ return status; ++} ++EXPORT_SYMBOL(vchiq_add_service); ++ ++/**************************************************************************** ++* ++* vchiq_open_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_open_service( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *phandle) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service = NULL; ++ ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p) called", __func__, instance); ++ ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; ++ ++ if (!vchiq_is_connected(instance)) ++ goto failed; ++ ++ service = vchiq_add_service_internal(state, ++ params, ++ VCHIQ_SRVSTATE_OPENING, ++ instance, ++ NULL); ++ ++ if (service) { ++ *phandle = service->handle; ++ status = vchiq_open_service_internal(service, current->pid); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_remove_service(service->handle); ++ *phandle = VCHIQ_SERVICE_HANDLE_INVALID; + } + } + -+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), -+ GFP_KERNEL); -+ if (IS_ERR(dev)) -+ return PTR_ERR(dev); ++failed: ++ vchiq_log_trace(vchiq_core_log_level, ++ "%s(%p): returning %d", __func__, instance, status); + -+ dev->i2s_regmap = regmap[0]; -+ dev->clk_regmap = regmap[1]; ++ return status; ++} ++EXPORT_SYMBOL(vchiq_open_service); + -+ /* Set the DMA address */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = -+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, ++ const void *data, unsigned int size, void *userdata) ++{ ++ return vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); ++} ++EXPORT_SYMBOL(vchiq_queue_bulk_transmit); + -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = -+ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, void *userdata) ++{ ++ return vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); ++} ++EXPORT_SYMBOL(vchiq_queue_bulk_receive); + -+ /* Set the DREQ */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id = -+ BCM2708_DMA_DREQ_PCM_TX; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id = -+ BCM2708_DMA_DREQ_PCM_RX; ++VCHIQ_STATUS_T ++vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, ++ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_STATUS_T status; + -+ /* Set the bus width */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = -+ DMA_SLAVE_BUSWIDTH_4_BYTES; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = -+ DMA_SLAVE_BUSWIDTH_4_BYTES; -+ -+ /* Set burst */ -+ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; -+ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; -+ -+ /* BCLK ratio - use default */ -+ dev->bclk_ratio = 0; -+ -+ /* Store the pdev */ -+ dev->dev = &pdev->dev; -+ dev_set_drvdata(&pdev->dev, dev); -+ -+ ret = snd_soc_register_component(&pdev->dev, -+ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1); -+ -+ if (ret) { -+ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); -+ ret = -ENOMEM; -+ return ret; ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ status = vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ mode, VCHIQ_BULK_TRANSMIT); ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ status = vchiq_blocking_bulk_transfer(handle, ++ (void *)data, size, VCHIQ_BULK_TRANSMIT); ++ break; ++ default: ++ return VCHIQ_ERROR; + } + -+ ret = snd_dmaengine_pcm_register(&pdev->dev, -+ &bcm2708_dmaengine_pcm_config, -+ SND_DMAENGINE_PCM_FLAG_COMPAT); -+ if (ret) { -+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); -+ snd_soc_unregister_component(&pdev->dev); -+ return ret; ++ return status; ++} ++EXPORT_SYMBOL(vchiq_bulk_transmit); ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_STATUS_T status; ++ ++ switch (mode) { ++ case VCHIQ_BULK_MODE_NOCALLBACK: ++ case VCHIQ_BULK_MODE_CALLBACK: ++ status = vchiq_bulk_transfer(handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ mode, VCHIQ_BULK_RECEIVE); ++ break; ++ case VCHIQ_BULK_MODE_BLOCKING: ++ status = vchiq_blocking_bulk_transfer(handle, ++ (void *)data, size, VCHIQ_BULK_RECEIVE); ++ break; ++ default: ++ return VCHIQ_ERROR; + } + -+ return 0; ++ return status; +} ++EXPORT_SYMBOL(vchiq_bulk_receive); + -+static int bcm2708_i2s_remove(struct platform_device *pdev) ++static VCHIQ_STATUS_T ++vchiq_blocking_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, void *data, ++ unsigned int size, VCHIQ_BULK_DIR_T dir) +{ -+ snd_dmaengine_pcm_unregister(&pdev->dev); -+ snd_soc_unregister_component(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver bcm2708_i2s_driver = { -+ .probe = bcm2708_i2s_probe, -+ .remove = bcm2708_i2s_remove, -+ .driver = { -+ .name = "bcm2708-i2s", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(bcm2708_i2s_driver); -+ -+MODULE_ALIAS("platform:bcm2708-i2s"); -+MODULE_DESCRIPTION("BCM2708 I2S interface"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); -diff --git a/sound/soc/bcm/bcm2708-i2s.h b/sound/soc/bcm/bcm2708-i2s.h -new file mode 100644 -index 0000000..6fdcbc1 ---- /dev/null -+++ b/sound/soc/bcm/bcm2708-i2s.h -@@ -0,0 +1,35 @@ -+/* -+ * I2S configuration for sound cards. -+ * -+ * Copyright (c) 2014 Daniel Matuschek -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef BCM2708_I2S_H -+#define BCM2708_I2S_H -+ -+/* I2S pin assignment */ -+#define BCM2708_I2S_GPIO_AUTO 0 -+#define BCM2708_I2S_GPIO_PIN18 1 -+#define BCM2708_I2S_GPIO_PIN28 2 -+ -+/* Alt mode to enable I2S */ -+#define BCM2708_I2S_GPIO_PIN18_ALT 0 -+#define BCM2708_I2S_GPIO_PIN28_ALT 2 -+ -+extern void bcm2708_i2s_set_gpio(int gpio); -+ -+#endif - -From 2ef6c0731bcb96af4407789a69e4bcf345ec7902 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 14:59:51 +0100 -Subject: [PATCH 032/172] ASoC: Add support for PCM5102A codec - -Some definitions to support the PCM5102A codec -by Texas Instruments. - -Signed-off-by: Florian Meier ---- - sound/soc/codecs/Kconfig | 4 +++ - sound/soc/codecs/Makefile | 2 ++ - sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 69 insertions(+) - create mode 100644 sound/soc/codecs/pcm5102a.c - -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index ef2c70e..1fc4390 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -81,6 +81,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_PCM512x_I2C if I2C - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RT286 if I2C -+ select SND_SOC_PCM5102A if I2C - select SND_SOC_RT5631 if I2C - select SND_SOC_RT5640 if I2C - select SND_SOC_RT5645 if I2C -@@ -502,6 +503,9 @@ config SND_SOC_RT286 - tristate - depends on I2C - -+config SND_SOC_PCM5102A -+ tristate -+ - config SND_SOC_RT5631 - tristate "Realtek ALC5631/RT5631 CODEC" - depends on I2C -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index bbdfd1e..3dd07b1 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -76,6 +76,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o - snd-soc-pcm512x-spi-objs := pcm512x-spi.o - snd-soc-rl6231-objs := rl6231.o - snd-soc-rt286-objs := rt286.o -+snd-soc-pcm5102a-objs := pcm5102a.o - snd-soc-rt5631-objs := rt5631.o - snd-soc-rt5640-objs := rt5640.o - snd-soc-rt5645-objs := rt5645.o -@@ -257,6 +258,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o - obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o - obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o -+obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o - obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o - obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o - obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o -diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c -new file mode 100644 -index 0000000..126f1e9 ---- /dev/null -+++ b/sound/soc/codecs/pcm5102a.c -@@ -0,0 +1,63 @@ -+/* -+ * Driver for the PCM5102A codec -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static struct snd_soc_dai_driver pcm5102a_dai = { -+ .name = "pcm5102a-hifi", -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE | -+ SNDRV_PCM_FMTBIT_S32_LE -+ }, -+}; -+ -+static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; -+ -+static int pcm5102a_probe(struct platform_device *pdev) -+{ -+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, -+ &pcm5102a_dai, 1); -+} -+ -+static int pcm5102a_remove(struct platform_device *pdev) -+{ -+ snd_soc_unregister_codec(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver pcm5102a_codec_driver = { -+ .probe = pcm5102a_probe, -+ .remove = pcm5102a_remove, -+ .driver = { -+ .name = "pcm5102a-codec", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(pcm5102a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); - -From 2bac890f221788793aff1fcefaeab3eaa383c536 Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:04:54 +0100 -Subject: [PATCH 033/172] BCM2708: Add I2S support to board file - -Adds the required initializations for I2S -to the board file of mach-bcm2708. - -Signed-off-by: Florian Meier ---- - arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a740344..dca28ad 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -587,6 +587,28 @@ static struct platform_device bcm2835_thermal_device = { - .name = "bcm2835_thermal", - }; - -+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+static struct resource bcm2708_i2s_resources[] = { -+ { -+ .start = I2S_BASE, -+ .end = I2S_BASE + 0x20, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = PCM_CLOCK_BASE, -+ .end = PCM_CLOCK_BASE + 0x02, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_i2s_device = { -+ .name = "bcm2708-i2s", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), -+ .resource = bcm2708_i2s_resources, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -707,6 +729,10 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); - -+#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+ bcm_register_device(&bcm2708_i2s_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); - -From 1395417905d319eb6d2299a90e8fee3931ebc40c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:19:08 +0100 -Subject: [PATCH 034/172] ASoC: Add support for HifiBerry DAC - -This adds a machine driver for the HifiBerry DAC. -It is a sound card that can -be stacked onto the Raspberry Pi. - -Signed-off-by: Florian Meier ---- - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 5 +++ - sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 112 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_dac.c - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 7e5b945..b36a62f 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -18,3 +18,10 @@ config SND_BCM2708_SOC_I2S - Say Y or M if you want to add support for codecs attached to - the BCM2708 I2S interface. You will also need - to select the audio interfaces to support below. -+ -+config SND_BCM2708_SOC_HIFIBERRY_DAC -+ tristate "Support for HifiBerry DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM5102A -+ help -+ Say Y or M if you want to add support for HifiBerry DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index f8bbe1f..be90a49cb 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -7,3 +7,8 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o - snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o - - obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o -+ -+# BCM2708 Machine Support -+snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+ -+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -diff --git a/sound/soc/bcm/hifiberry_dac.c b/sound/soc/bcm/hifiberry_dac.c -new file mode 100644 -index 0000000..4b70b45 ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_dac.c -@@ -0,0 +1,100 @@ -+/* -+ * ASoC Driver for HifiBerry DAC -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ unsigned int sample_bits = -+ snd_pcm_format_physical_width(params_format(params)); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = { -+ .hw_params = snd_rpi_hifiberry_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = { -+{ -+ .name = "HifiBerry DAC", -+ .stream_name = "HifiBerry DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm5102a-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm5102a-codec", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_hifiberry_dac_ops, -+ .init = snd_rpi_hifiberry_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_hifiberry_dac = { -+ .name = "snd_rpi_hifiberry_dac", -+ .dai_link = snd_rpi_hifiberry_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), -+}; -+ -+static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac); -+} -+ -+static struct platform_driver snd_rpi_hifiberry_dac_driver = { -+ .driver = { -+ .name = "snd-hifiberry-dac", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_hifiberry_dac_probe, -+ .remove = snd_rpi_hifiberry_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_hifiberry_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC"); -+MODULE_LICENSE("GPL v2"); - -From da52bd290dc3f3af3f0836a958f42b34531018cb Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 22 Nov 2013 19:21:34 +0100 -Subject: [PATCH 035/172] BCM2708: Add HifiBerry DAC to board file - -This adds the initalization of the HifiBerry DAC -to the mach-bcm2708 board file. - -Signed-off-by: Florian Meier ---- - arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index dca28ad..50d4991 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -609,6 +609,20 @@ static struct platform_device bcm2708_i2s_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+static struct platform_device snd_hifiberry_dac_device = { -+ .name = "snd-hifiberry-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm5102a_codec_device = { -+ .name = "pcm5102a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -733,6 +747,11 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_i2s_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+ bcm_register_device(&snd_hifiberry_dac_device); -+ bcm_register_device(&snd_pcm5102a_codec_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); - -From c413288cdaabf5aa977f0afec318222b06adf10f Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Mon, 2 Dec 2013 20:28:22 +0100 -Subject: [PATCH 036/172] BCM2708: Add I2S and DMA support to default config - -This commit adds several modules that are needed for -I2S support for the Raspberry Pi to the defconfig. - -Signed-off-by: Florian Meier ---- - arch/arm/configs/bcmrpi_defconfig | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 63a6546..9a0e922 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -779,6 +779,10 @@ CONFIG_SND_USB_UA101=m - CONFIG_SND_USB_CAIAQ=m - CONFIG_SND_USB_CAIAQ_INPUT=y - CONFIG_SND_USB_6FIRE=m -+CONFIG_SND_SOC=m -+CONFIG_SND_BCM2708_SOC_I2S=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y - CONFIG_HID_A4TECH=m -@@ -977,6 +981,8 @@ CONFIG_RTC_DRV_RS5C348=m - CONFIG_RTC_DRV_DS3234=m - CONFIG_RTC_DRV_PCF2123=m - CONFIG_RTC_DRV_RX4581=m -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y - CONFIG_UIO=m - CONFIG_UIO_PDRV_GENIRQ=m - CONFIG_STAGING=y - -From 272884346f761c002999fa0aafdfa64e3c038b3c Mon Sep 17 00:00:00 2001 -From: Florian Meier -Date: Fri, 6 Dec 2013 20:50:28 +0100 -Subject: [PATCH 037/172] ASoC: BCM2708: Add support for RPi-DAC - -This adds a machine driver for the RPi-DAC. - -Signed-off-by: Florian Meier ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++ - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/Kconfig | 4 ++ - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/pcm1794a.c | 62 +++++++++++++++++++++++++ - 8 files changed, 194 insertions(+) - create mode 100644 sound/soc/bcm/rpi-dac.c - create mode 100644 sound/soc/codecs/pcm1794a.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 9a0e922..3f7a237 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 50d4991..100c223 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -623,6 +623,20 @@ static struct platform_device snd_pcm5102a_codec_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+static struct platform_device snd_rpi_dac_device = { -+ .name = "snd-rpi-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm1794a_codec_device = { -+ .name = "pcm1794a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -752,6 +766,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+ bcm_register_device(&snd_rpi_dac_device); -+ bcm_register_device(&snd_pcm1794a_codec_device); -+#endif -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index b36a62f..714841d 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -25,3 +25,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC - select SND_SOC_PCM5102A - help - Say Y or M if you want to add support for HifiBerry DAC. -+ -+config SND_BCM2708_SOC_RPI_DAC -+ tristate "Support for RPi-DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM1794A -+ help -+ Say Y or M if you want to add support for RPi-DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index be90a49cb..ccc9809 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -10,5 +10,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - - # BCM2708 Machine Support - snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+snd-soc-rpi-dac-objs := rpi-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c -new file mode 100644 -index 0000000..6d6e0ba ---- /dev/null -+++ b/sound/soc/bcm/rpi-dac.c -@@ -0,0 +1,97 @@ -+/* -+ * ASoC Driver for RPi-DAC. -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ return 0; -+} -+ -+static int snd_rpi_rpi_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_rpi_dac_ops = { -+ .hw_params = snd_rpi_rpi_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = { -+{ -+ .name = "RPi-DAC", -+ .stream_name = "RPi-DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm1794a-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm1794a-codec", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_rpi_dac_ops, -+ .init = snd_rpi_rpi_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_rpi_dac = { -+ .name = "snd_rpi_rpi_dac", -+ .dai_link = snd_rpi_rpi_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), -+}; -+ -+static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_rpi_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_rpi_dac); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_rpi_dac); -+} -+ -+static struct platform_driver snd_rpi_rpi_dac_driver = { -+ .driver = { -+ .name = "snd-rpi-dac", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_rpi_dac_probe, -+ .remove = snd_rpi_rpi_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_rpi_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); -+MODULE_LICENSE("GPL v2"); -diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index 1fc4390..0a7aaf7 100644 ---- a/sound/soc/codecs/Kconfig -+++ b/sound/soc/codecs/Kconfig -@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS - select SND_SOC_PCM512x_SPI if SPI_MASTER - select SND_SOC_RT286 if I2C - select SND_SOC_PCM5102A if I2C -+ select SND_SOC_PCM1794A if I2C - select SND_SOC_RT5631 if I2C - select SND_SOC_RT5640 if I2C - select SND_SOC_RT5645 if I2C -@@ -503,6 +504,9 @@ config SND_SOC_RT286 - tristate - depends on I2C - -+config SND_SOC_PCM1794A -+ tristate -+ - config SND_SOC_PCM5102A - tristate - -diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index 3dd07b1..1548bf33 100644 ---- a/sound/soc/codecs/Makefile -+++ b/sound/soc/codecs/Makefile -@@ -76,6 +76,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o - snd-soc-pcm512x-spi-objs := pcm512x-spi.o - snd-soc-rl6231-objs := rl6231.o - snd-soc-rt286-objs := rt286.o -+snd-soc-pcm1794a-objs := pcm1794a.o - snd-soc-pcm5102a-objs := pcm5102a.o - snd-soc-rt5631-objs := rt5631.o - snd-soc-rt5640-objs := rt5640.o -@@ -258,6 +259,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o - obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o - obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o - obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o -+obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o - obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o - obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o - obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o -diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c -new file mode 100644 -index 0000000..b4eaa44 ---- /dev/null -+++ b/sound/soc/codecs/pcm1794a.c -@@ -0,0 +1,62 @@ -+/* -+ * Driver for the PCM1794A codec -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static struct snd_soc_dai_driver pcm1794a_dai = { -+ .name = "pcm1794a-hifi", -+ .playback = { -+ .channels_min = 2, -+ .channels_max = 2, -+ .rates = SNDRV_PCM_RATE_8000_192000, -+ .formats = SNDRV_PCM_FMTBIT_S16_LE | -+ SNDRV_PCM_FMTBIT_S24_LE -+ }, -+}; -+ -+static struct snd_soc_codec_driver soc_codec_dev_pcm1794a; -+ -+static int pcm1794a_probe(struct platform_device *pdev) -+{ -+ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a, -+ &pcm1794a_dai, 1); -+} -+ -+static int pcm1794a_remove(struct platform_device *pdev) -+{ -+ snd_soc_unregister_codec(&pdev->dev); -+ return 0; -+} -+ -+static struct platform_driver pcm1794a_codec_driver = { -+ .probe = pcm1794a_probe, -+ .remove = pcm1794a_remove, -+ .driver = { -+ .name = "pcm1794a-codec", -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+module_platform_driver(pcm1794a_codec_driver); -+ -+MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); -+MODULE_AUTHOR("Florian Meier "); -+MODULE_LICENSE("GPL v2"); - -From 38defc6ccd2879e3b61ee7fbeb71da1982fda8fc Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:41:23 +0100 -Subject: [PATCH 038/172] ASoC: wm8804: Implement MCLK configuration options, - add 32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs - for most sample rates. At 192kHz only 128xfs is supported. The existing - driver selects 128xfs automatically for some lower samples rates. By using an - additional mclk_div divider, it is now possible to control the behaviour. - This allows using 256xfs PLL frequency on all sample rates up to 96kHz. It - should allow lower jitter and better signal quality. The behavior has to be - controlled by the sound card driver, because some sample frequency share the - same setting. e.g. 192kHz and 96kHz use 24.576MHz master clock. The only - difference is the MCLK divider. - -This also added support for 32bit data. - -Signed-off-by: Daniel Matuschek ---- - sound/soc/codecs/wm8804.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c -index 1315f76..3d86ba8 100644 ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -278,6 +278,7 @@ static int wm8804_hw_params(struct snd_pcm_substream *substream, - blen = 0x1; - break; - case 24: -+ case 32: - blen = 0x2; - break; - default: -@@ -621,7 +622,7 @@ static const struct snd_soc_dai_ops wm8804_dai_ops = { - }; - - #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ -- SNDRV_PCM_FMTBIT_S24_LE) -+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) - - #define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ - -From 061cb19834ec2ebf649ae2028efbcaff9b4cf516 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Wed, 15 Jan 2014 21:42:08 +0100 -Subject: [PATCH 039/172] ASoC: BCM:Add support for HiFiBerry Digi. Driver is - based on the patched WM8804 driver. - -Signed-off-by: Daniel Matuschek ---- - sound/soc/bcm/Kconfig | 7 ++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_digi.c | 153 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 162 insertions(+) - create mode 100644 sound/soc/bcm/hifiberry_digi.c - -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 714841d..e563dbc 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC - help - Say Y or M if you want to add support for HifiBerry DAC. - -+config SND_BCM2708_SOC_HIFIBERRY_DIGI -+ tristate "Support for HifiBerry Digi" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_WM8804 -+ help -+ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. -+ - config SND_BCM2708_SOC_RPI_DAC - tristate "Support for RPi-DAC" - depends on SND_BCM2708_SOC_I2S -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index ccc9809..826df7d 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -10,7 +10,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - - # BCM2708 Machine Support - snd-soc-hifiberry-dac-objs := hifiberry_dac.o -+snd-soc-hifiberry-digi-objs := hifiberry_digi.o - snd-soc-rpi-dac-objs := rpi-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o - obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -new file mode 100644 -index 0000000..e4f769d ---- /dev/null -+++ b/sound/soc/bcm/hifiberry_digi.c -@@ -0,0 +1,153 @@ -+/* -+ * ASoC Driver for HifiBerry Digi -+ * -+ * Author: Daniel Matuschek -+ * based on the HifiBerry DAC driver by Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+#include "../codecs/wm8804.h" -+ -+static int samplerate=44100; -+ -+static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) -+{ -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ /* enable TX output */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); -+ -+ return 0; -+} -+ -+static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+ struct snd_soc_dai *codec_dai = rtd->codec_dai; -+ struct snd_soc_codec *codec = rtd->codec; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ int sysclk = 27000000; /* This is fixed on this board */ -+ -+ long mclk_freq=0; -+ int mclk_div=1; -+ -+ int ret; -+ -+ samplerate = params_rate(params); -+ -+ switch (samplerate) { -+ case 44100: -+ case 48000: -+ case 88200: -+ case 96000: -+ mclk_freq=samplerate*256; -+ mclk_div=WM8804_MCLKDIV_256FS; ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_SERVICE_T *service; ++ VCHIQ_STATUS_T status; ++ struct bulk_waiter_node *waiter = NULL; ++ struct list_head *pos; ++ ++ service = find_service_by_handle(handle); ++ if (!service) ++ return VCHIQ_ERROR; ++ ++ instance = service->instance; ++ ++ unlock_service(service); ++ ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_for_each(pos, &instance->bulk_waiter_list) { ++ if (list_entry(pos, struct bulk_waiter_node, ++ list)->pid == current->pid) { ++ waiter = list_entry(pos, ++ struct bulk_waiter_node, ++ list); ++ list_del(pos); + break; -+ case 176400: -+ case 192000: -+ mclk_freq=samplerate*128; -+ mclk_div=WM8804_MCLKDIV_128FS; -+ break; -+ default: -+ dev_err(substream->pcm->dev, -+ "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); -+ } -+ -+ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); -+ snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); -+ -+ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, -+ sysclk, SND_SOC_CLOCK_OUT); -+ if (ret < 0) { -+ dev_err(substream->pcm->dev, -+ "Failed to set WM8804 SYSCLK: %d\n", ret); -+ return ret; -+ } -+ -+ /* Enable TX output */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); -+ -+ /* Power on */ -+ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai,64); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { -+ .hw_params = snd_rpi_hifiberry_digi_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { -+{ -+ .name = "HifiBerry Digi", -+ .stream_name = "HifiBerry Digi HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "wm8804-spdif", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "wm8804.1-003b", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBM_CFM, -+ .ops = &snd_rpi_hifiberry_digi_ops, -+ .init = snd_rpi_hifiberry_digi_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_hifiberry_digi = { -+ .name = "snd_rpi_hifiberry_digi", -+ .dai_link = snd_rpi_hifiberry_digi_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), -+}; -+ -+static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_hifiberry_digi.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_hifiberry_digi); -+ if (ret) -+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_hifiberry_digi); -+} -+ -+static struct platform_driver snd_rpi_hifiberry_digi_driver = { -+ .driver = { -+ .name = "snd-hifiberry-digi", -+ .owner = THIS_MODULE, -+ }, -+ .probe = snd_rpi_hifiberry_digi_probe, -+ .remove = snd_rpi_hifiberry_digi_remove, -+}; -+ -+module_platform_driver(snd_rpi_hifiberry_digi_driver); -+ -+MODULE_AUTHOR("Daniel Matuschek "); -+MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi"); -+MODULE_LICENSE("GPL v2"); - -From 0d3ca97a92ba10f025d4beb3bd8f58e097847840 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:26:08 +0100 -Subject: [PATCH 040/172] BCM2708: Added support for HiFiBerry Digi board Board - initalization by I2C - -Signed-off-by: Daniel Matuschek ---- - arch/arm/mach-bcm2708/bcm2708.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 100c223..a57cb85 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -623,6 +623,21 @@ static struct platform_device snd_pcm5102a_codec_device = { - }; - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+static struct platform_device snd_hifiberry_digi_device = { -+ .name = "snd-hifiberry-digi", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("wm8804", 0x3b) -+ }, -+}; -+ -+#endif -+ - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - static struct platform_device snd_rpi_dac_device = { - .name = "snd-rpi-dac", -@@ -766,6 +781,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+ bcm_register_device(&snd_hifiberry_digi_device); -+ i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+#endif -+ - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - bcm_register_device(&snd_rpi_dac_device); - bcm_register_device(&snd_pcm1794a_codec_device); - -From b8e27019fe2d79dc6f12b04d4a2f4de1f7a7dc75 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:27:28 +0100 -Subject: [PATCH 041/172] BCM2708: Added HiFiBerry Digi configuration option It - will be compiled as a module by default. This also includes the WM8804 - driver. - -Signed-off-by: Daniel Matuschek ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 3f7a237..cbd53b1 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - -From b8243e95d60019cefde3bac2db44f25c773946d5 Mon Sep 17 00:00:00 2001 -From: Daniel Matuschek -Date: Thu, 16 Jan 2014 07:36:35 +0100 -Subject: [PATCH 042/172] ASoC: wm8804: Set idle_bias_off to false Idle bias - has been change to remove warning on driver startup - -Signed-off-by: Daniel Matuschek ---- - sound/soc/codecs/wm8804.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c -index 3d86ba8..34f7f76 100644 ---- a/sound/soc/codecs/wm8804.c -+++ b/sound/soc/codecs/wm8804.c -@@ -653,7 +653,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { - .probe = wm8804_probe, - .remove = wm8804_remove, - .set_bias_level = wm8804_set_bias_level, -- .idle_bias_off = true, -+ .idle_bias_off = false, - - .controls = wm8804_snd_controls, - .num_controls = ARRAY_SIZE(wm8804_snd_controls), - -From 88ba2e5743d41046c39726fd5a483b1bb02f76ce Mon Sep 17 00:00:00 2001 -From: Gordon Garrity -Date: Sat, 8 Mar 2014 16:56:57 +0000 -Subject: [PATCH 043/172] Add IQaudIO Sound Card support for Raspberry Pi - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 22 ++++++++ - sound/soc/bcm/Kconfig | 7 +++ - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/iqaudio-dac.c | 111 ++++++++++++++++++++++++++++++++++++++ - 5 files changed, 143 insertions(+) - create mode 100644 sound/soc/bcm/iqaudio-dac.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index cbd53b1..7ba662a 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -784,6 +784,7 @@ CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m -+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m - CONFIG_SND_SIMPLE_CARD=m - CONFIG_SOUND_PRIME=m - CONFIG_HIDRAW=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a57cb85..633be19 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -652,6 +652,22 @@ static struct platform_device snd_pcm1794a_codec_device = { - }; - #endif - -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+static struct platform_device snd_rpi_iqaudio_dac_device = { -+ .name = "snd-rpi-iqaudio-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+// Use the actual device name rather than generic driver name -+static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4c) -+ }, -+}; -+#endif -+ - int __init bcm_register_device(struct platform_device *pdev) - { - int ret; -@@ -791,6 +807,12 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm1794a_codec_device); - #endif - -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+ bcm_register_device(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+#endif -+ -+ - for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { - struct amba_device *d = amba_devs[i]; - amba_device_register(d, &iomem_resource); -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index e563dbc..c621a5e 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -39,3 +39,10 @@ config SND_BCM2708_SOC_RPI_DAC - select SND_SOC_PCM1794A - help - Say Y or M if you want to add support for RPi-DAC. -+ -+config SND_BCM2708_SOC_IQAUDIO_DAC -+ tristate "Support for IQaudIO-DAC" -+ depends on SND_BCM2708_SOC_I2S -+ select SND_SOC_PCM512x_I2C -+ help -+ Say Y or M if you want to add support for IQaudIO-DAC. -diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile -index 826df7d..d597fb0 100644 ---- a/sound/soc/bcm/Makefile -+++ b/sound/soc/bcm/Makefile -@@ -12,7 +12,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o - snd-soc-hifiberry-dac-objs := hifiberry_dac.o - snd-soc-hifiberry-digi-objs := hifiberry_digi.o - snd-soc-rpi-dac-objs := rpi-dac.o -+snd-soc-iqaudio-dac-objs := iqaudio-dac.o - - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o - obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o - obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o -+obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o -diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c -new file mode 100644 -index 0000000..8d0e2ae ---- /dev/null -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -0,0 +1,111 @@ -+/* -+ * ASoC Driver for IQaudIO DAC -+ * -+ * Author: Florian Meier -+ * Copyright 2013 -+ * -+ * 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. -+ * -+ * This program is distributed in the hope that it will be useful, but -+ * WITHOUT 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 -+ -+static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) -+{ -+// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ -+ return 0; -+} -+ -+static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params) -+{ -+ struct snd_soc_pcm_runtime *rtd = substream->private_data; -+// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai; -+// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; -+ -+ unsigned int sample_bits = -+ snd_pcm_format_physical_width(params_format(params)); -+ -+ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); -+} -+ -+/* machine stream operations */ -+static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = { -+ .hw_params = snd_rpi_iqaudio_dac_hw_params, -+}; -+ -+static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = { -+{ -+ .name = "IQaudIO DAC", -+ .stream_name = "IQaudIO DAC HiFi", -+ .cpu_dai_name = "bcm2708-i2s.0", -+ .codec_dai_name = "pcm512x-hifi", -+ .platform_name = "bcm2708-i2s.0", -+ .codec_name = "pcm512x.1-004c", -+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | -+ SND_SOC_DAIFMT_CBS_CFS, -+ .ops = &snd_rpi_iqaudio_dac_ops, -+ .init = snd_rpi_iqaudio_dac_init, -+}, -+}; -+ -+/* audio machine driver */ -+static struct snd_soc_card snd_rpi_iqaudio_dac = { -+ .name = "IQaudIODAC", -+ .dai_link = snd_rpi_iqaudio_dac_dai, -+ .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), -+}; -+ -+static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ -+ snd_rpi_iqaudio_dac.dev = &pdev->dev; -+ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); -+ if (ret) -+ dev_err(&pdev->dev, -+ "snd_soc_register_card() failed: %d\n", ret); -+ -+ return ret; -+} -+ -+static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev) -+{ -+ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac); -+} -+ -+static const struct of_device_id iqaudio_of_match[] = { -+ { .compatible = "iqaudio,iqaudio-dac", }, -+ {}, -+}; -+ -+static struct platform_driver snd_rpi_iqaudio_dac_driver = { -+ .driver = { -+ .name = "snd-rpi-iqaudio-dac", -+ .owner = THIS_MODULE, -+ .of_match_table = iqaudio_of_match, -+ }, -+ .probe = snd_rpi_iqaudio_dac_probe, -+ .remove = snd_rpi_iqaudio_dac_remove, -+}; -+ -+module_platform_driver(snd_rpi_iqaudio_dac_driver); -+ -+MODULE_AUTHOR("Florian Meier "); -+MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC"); -+MODULE_LICENSE("GPL v2"); - -From 397ad3a8dabc60f4ce6b1c261de7e2c31b7b1030 Mon Sep 17 00:00:00 2001 -From: Howard Mitchell -Date: Mon, 2 Mar 2015 17:28:02 +0000 -Subject: [PATCH 044/172] Set a limit of 0dB on Digital Volume Control - -The main volume control in the PCM512x DAC has a range up to -+24dB. This is dangerously loud and can potentially cause massive -clipping in the output stages. Therefore this sets a sensible -limit of 0dB for this control. ---- - sound/soc/bcm/iqaudio-dac.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c -index 8d0e2ae..aff7377 100644 ---- a/sound/soc/bcm/iqaudio-dac.c -+++ b/sound/soc/bcm/iqaudio-dac.c -@@ -25,7 +25,13 @@ - - static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) - { --// NOT USED struct snd_soc_codec *codec = rtd->codec; -+ int ret; -+ struct snd_soc_card *card = rtd->card; -+ struct snd_soc_codec *codec = rtd->codec; -+ -+ ret = snd_soc_limit_volume(codec, "Digital Playback Volume", 207); -+ if (ret < 0) -+ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); - - return 0; - } - -From 11c1054be4dcd9cb41f69225c2ec6ddf6f863d0a Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Jun 2014 13:42:01 +0100 -Subject: [PATCH 045/172] vmstat: Workaround for issue where dirty page count - goes negative - -See: -https://github.com/raspberrypi/linux/issues/617 -http://www.spinics.net/lists/linux-mm/msg72236.html ---- - include/linux/vmstat.h | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h -index 82e7db7..f87d16d 100644 ---- a/include/linux/vmstat.h -+++ b/include/linux/vmstat.h -@@ -241,7 +241,11 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item) - static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) - { - atomic_long_dec(&zone->vm_stat[item]); -+ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&zone->vm_stat[item]) < 0)) -+ atomic_long_set(&zone->vm_stat[item], 0); - atomic_long_dec(&vm_stat[item]); -+ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&vm_stat[item]) < 0)) -+ atomic_long_set(&vm_stat[item], 0); - } - - static inline void __inc_zone_page_state(struct page *page, - -From 6bc933a1c5417c9ce3d1f14fabd36ec806467de5 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 20 Jun 2014 16:03:12 +0100 -Subject: [PATCH 046/172] dwc_otg: Fix various issues with root port and - transaction errors - -Process the host port interrupts correctly (and don't trample them). -Root port hotplug now functional again. - -Fix a few thinkos with the transaction error passthrough for fiq_fsm. ---- - drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 7 +++---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 6 +++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2 +- - 3 files changed, 9 insertions(+), 6 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -index 065807f..96c76e3 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c -@@ -1348,10 +1348,9 @@ static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if, gin - local_fiq_disable(); - /* Pull in the interrupts that the FIQ has masked */ - gintmsk.d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -+ gintmsk.d32 |= gintmsk_common.d32; - /* for the upstairs function to reenable - have to read it here in case FIQ triggers again */ -- reenable_gintmsk->d32 |= gintmsk.d32; -- reenable_gintmsk->d32 |= ~(hcd->fiq_state->gintmsk_saved.d32); -- reenable_gintmsk->d32 &= gintmsk_common.d32; -+ reenable_gintmsk->d32 = gintmsk.d32; - local_fiq_enable(); - } - -@@ -1535,7 +1534,7 @@ int32_t dwc_otg_handle_common_intr(void *dev) - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "CILOUT %1d", retval); - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintsts.d32); - // fiq_print(FIQDBG_INT, otg_dev->hcd->fiq_state, "%08x", gintmsk_reenable.d32); -- if (retval) { -+ if (retval && fiq_enable) { - DWC_WRITE_REG32(&core_if->core_global_regs->gintmsk, gintmsk_reenable.d32); - } - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 1be6e71..284f902 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -696,7 +696,11 @@ static int notrace noinline fiq_fsm_do_hcintr(struct fiq_state *state, int num_c - fiq_print(FIQDBG_ERR, state, "ERRST %02d", n); - if (hcint_probe.b.nak || hcint_probe.b.ack || hcint_probe.b.datatglerr) { - fiq_print(FIQDBG_ERR, state, "RESET %02d", n); -- st->nr_errors = 0; -+ /* In some random cases we can get a NAK interrupt coincident with a Xacterr -+ * interrupt, after the device has disappeared. -+ */ -+ if (!hcint.b.xacterr) -+ st->nr_errors = 0; - hcintmsk.b.nak = 0; - hcintmsk.b.ack = 0; - hcintmsk.b.datatglerr = 0; -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 4195ff2..a5566bc 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -2619,7 +2619,7 @@ int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) - case FIQ_PASSTHROUGH_ERRORSTATE: - /* Hook into the error count */ - fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "HCDERR%02d", num); -- if (dwc_otg_hcd->fiq_state->channel[num].nr_errors) { -+ if (!dwc_otg_hcd->fiq_state->channel[num].nr_errors) { - qtd->error_count = 0; - fiq_print(FIQDBG_ERR, dwc_otg_hcd->fiq_state, "RESET "); - } - -From 2a2ae2efa4a1c663642cefb333a2042590818785 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 20 Jun 2014 17:23:20 +0100 -Subject: [PATCH 047/172] fiq_fsm: Implement hack for Split Interrupt - transactions - -Hubs aren't too picky about which endpoint we send Control type split -transactions to. By treating Interrupt transfers as Control, it is -possible to use the non-periodic queue in the OTG core as well as the -non-periodic FIFOs in the hub itself. This massively reduces the -microframe exclusivity/contention that periodic split transactions -otherwise have to enforce. - -It goes without saying that this is a fairly egregious USB specification -violation, but it works. - -Original idea by Hans Petter Selasky @ FreeBSD.org. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 32 ++++++++++++++++++++++++++------ - 1 file changed, 26 insertions(+), 6 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 130096b..68d4f3b 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1055,10 +1055,11 @@ int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) - for (i=0; i < hcd->core_if->core_params->host_channels; i++) { - dwc_otg_cleanup_fiq_channel(hcd, i); - } -- DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s", -+ DWC_PRINTF("FIQ FSM acceleration enabled for :\n%s%s%s%s", - (fiq_fsm_mask & 0x1) ? "Non-periodic Split Transactions\n" : "", - (fiq_fsm_mask & 0x2) ? "Periodic Split Transactions\n" : "", -- (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : ""); -+ (fiq_fsm_mask & 0x4) ? "High-Speed Isochronous Endpoints\n" : "", -+ (fiq_fsm_mask & 0x8) ? "Interrupt/Control Split Transaction hack enabled\n" : ""); - } - } - -@@ -1789,6 +1790,20 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcintmsk_copy.b.chhltd = 1; - st->hcintmsk_copy.b.ahberr = 1; - -+ /* Hack courtesy of FreeBSD: apparently forcing Interrupt Split transactions -+ * as Control puts the transfer into the non-periodic request queue and the -+ * non-periodic handler in the hub. Makes things lots easier. -+ */ -+ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) { -+ st->hcchar_copy.b.multicnt = 0; -+ st->hcchar_copy.b.oddfrm = 0; -+ st->hcchar_copy.b.eptype = UE_CONTROL; -+ if (hc->align_buff) { -+ st->hcdma_copy.d32 = hc->align_buff; -+ } else { -+ st->hcdma_copy.d32 = ((unsigned long) hc->xfer_buff & 0xFFFFFFFF); + } + } - DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); - DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); -@@ -1842,6 +1857,9 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - } - } -+ if ((fiq_fsm_mask & 0x8) && hc->ep_type == UE_INTERRUPT) -+ start_immediate = 1; ++ mutex_unlock(&instance->bulk_waiter_list_mutex); + - fiq_print(FIQDBG_INT, hcd->fiq_state, "FSMQ %01d %01d", hc->hc_num, start_immediate); - fiq_print(FIQDBG_INT, hcd->fiq_state, "%08d", hfnum.b.frrem); - //fiq_print(FIQDBG_INT, hcd->fiq_state, "H:%02dP:%02d", hub_addr, port_addr); -@@ -1873,11 +1891,13 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - break; - case UE_INTERRUPT: -- if (start_immediate) { -+ if (fiq_fsm_mask & 0x8) { -+ st->fsm = FIQ_NP_SSPLIT_STARTED; -+ } else if (start_immediate) { - st->fsm = FIQ_PER_SSPLIT_STARTED; -- } else { -- st->fsm = FIQ_PER_SSPLIT_QUEUED; -- } -+ } else { -+ st->fsm = FIQ_PER_SSPLIT_QUEUED; ++ if (waiter) { ++ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; ++ if (bulk) { ++ /* This thread has an outstanding bulk transfer. */ ++ if ((bulk->data != data) || ++ (bulk->size != size)) { ++ /* This is not a retry of the previous one. ++ ** Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); + } - default: - break; - } - -From f8598c5aa941bdd6ebd3e83c1e2634f1ac6057c9 Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 6 Jul 2014 12:07:25 +0200 -Subject: [PATCH 048/172] spi-bcm2708: Prepare for Common Clock Framework - migration - -As part of migrating to use the Common Clock Framework, replace clk_enable() -with clk_prepare_enable() and clk_disable() with clk_disable_unprepare(). -This does not affect behaviour under the current clock implementation. - -Also add a missing clk_disable_unprepare() in the probe error path. - -Signed-off-by: Noralf Tronnes ---- - drivers/spi/spi-bcm2708.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -index b04a57d..349d21f 100644 ---- a/drivers/spi/spi-bcm2708.c -+++ b/drivers/spi/spi-bcm2708.c -@@ -545,7 +545,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - } - - /* initialise the hardware */ -- clk_enable(clk); -+ clk_prepare_enable(clk); - bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); - - err = spi_register_master(master); -@@ -561,6 +561,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - - out_free_irq: - free_irq(bs->irq, master); -+ clk_disable_unprepare(bs->clk); - out_workqueue: - destroy_workqueue(bs->workq); - out_iounmap: -@@ -585,7 +586,7 @@ static int bcm2708_spi_remove(struct platform_device *pdev) - - flush_work(&bs->work); - -- clk_disable(bs->clk); -+ clk_disable_unprepare(bs->clk); - clk_put(bs->clk); - free_irq(bs->irq, master); - iounmap(bs->base); - -From 0e203cd8312e0f2ecbaaa91b84302ebd08b7c7ab Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 6 Jul 2014 12:09:30 +0200 -Subject: [PATCH 049/172] BCM2708: Migrate to the Common Clock Framework - -As part of moving towards using Device Tree, the Common Clock Framework -has to be used instead of the BCM2708 clock implementation. - -Selecting COMMON_CLK removes the need to set CLKDEV_LOOKUP and HAVE_CLK explicitly. - -CONFIG_ARCH_BCM2708_CHIPIT #ifdef's are removed. They are no longer in use. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/Kconfig | 3 +- - arch/arm/mach-bcm2708/Makefile | 2 +- - arch/arm/mach-bcm2708/bcm2708.c | 79 +++++++++++++++++------------------------ - arch/arm/mach-bcm2708/clock.c | 61 ------------------------------- - arch/arm/mach-bcm2708/clock.h | 24 ------------- - 5 files changed, 34 insertions(+), 135 deletions(-) - delete mode 100644 arch/arm/mach-bcm2708/clock.c - delete mode 100644 arch/arm/mach-bcm2708/clock.h - -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index f607868..96f98ce 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -371,11 +371,10 @@ config ARCH_BCM2708 - bool "Broadcom BCM2708 family" - select CPU_V6 - select ARM_AMBA -- select HAVE_CLK - select HAVE_SCHED_CLOCK - select NEED_MACH_GPIO_H - select NEED_MACH_MEMORY_H -- select CLKDEV_LOOKUP -+ select COMMON_CLK - select ARCH_HAS_CPUFREQ - select GENERIC_CLOCKEVENTS - select ARM_ERRATA_411920 -diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile -index a722f3f..21e3521 100644 ---- a/arch/arm/mach-bcm2708/Makefile -+++ b/arch/arm/mach-bcm2708/Makefile -@@ -2,6 +2,6 @@ - # Makefile for the linux kernel. - # - --obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o -+obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o dma.o - obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o - obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 633be19..ef12cb8 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -27,6 +27,8 @@ - #include - #include - #include -+#include -+#include - #include - #include - #include -@@ -58,7 +60,6 @@ - - #include "bcm2708.h" - #include "armctrl.h" --#include "clock.h" - - #ifdef CONFIG_BCM_VC_CMA - #include -@@ -84,7 +85,7 @@ - - /* command line parameters */ - static unsigned boardrev, serial; --static unsigned uart_clock; -+static unsigned uart_clock = UART0_CLOCK; - static unsigned disk_led_gpio = 16; - static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; -@@ -196,51 +197,39 @@ static void __init bcm2708_clocksource_init(void) - } - } - -+struct clk __init *bcm2708_clk_register(const char *name, unsigned long fixed_rate) -+{ -+ struct clk *clk; - --/* -- * These are fixed clocks. -- */ --static struct clk ref24_clk = { -- .rate = UART0_CLOCK, /* The UART is clocked at 3MHz via APB_CLK */ --}; -+ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, -+ fixed_rate); -+ if (IS_ERR(clk)) -+ pr_err("%s not registered\n", name); - --static struct clk osc_clk = { --#ifdef CONFIG_ARCH_BCM2708_CHIPIT -- .rate = 27000000, --#else -- .rate = 500000000, /* ARM clock is set from the VideoCore booter */ --#endif --}; -+ return clk; -+} - --/* warning - the USB needs a clock > 34MHz */ -+void __init bcm2708_register_clkdev(struct clk *clk, const char *name) -+{ -+ int ret; - --static struct clk sdhost_clk = { --#ifdef CONFIG_ARCH_BCM2708_CHIPIT -- .rate = 4000000, /* 4MHz */ --#else -- .rate = 250000000, /* 250MHz */ --#endif --}; -+ ret = clk_register_clkdev(clk, NULL, name); -+ if (ret) -+ pr_err("%s alias not registered\n", name); -+} - --static struct clk_lookup lookups[] = { -- { /* UART0 */ -- .dev_id = "dev:f1", -- .clk = &ref24_clk, -- }, -- { /* USB */ -- .dev_id = "bcm2708_usb", -- .clk = &osc_clk, -- }, { /* SPI */ -- .dev_id = "bcm2708_spi.0", -- .clk = &sdhost_clk, -- }, { /* BSC0 */ -- .dev_id = "bcm2708_i2c.0", -- .clk = &sdhost_clk, -- }, { /* BSC1 */ -- .dev_id = "bcm2708_i2c.1", -- .clk = &sdhost_clk, -- } --}; -+void __init bcm2708_init_clocks(void) -+{ -+ struct clk *clk; -+ -+ clk = bcm2708_clk_register("uart0_clk", uart_clock); -+ bcm2708_register_clkdev(clk, "dev:f1"); -+ -+ clk = bcm2708_clk_register("sdhost_clk", 250000000); -+ bcm2708_register_clkdev(clk, "bcm2708_spi.0"); -+ bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); -+ bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); -+} - - #define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } - #define UART0_DMA { 15, 14 } -@@ -755,11 +744,7 @@ void __init bcm2708_init(void) - printk("bcm2708.uart_clock = %d\n", uart_clock); - pm_power_off = bcm2708_power_off; - -- if (uart_clock) -- lookups[0].clk->rate = uart_clock; -- -- for (i = 0; i < ARRAY_SIZE(lookups); i++) -- clkdev_add(&lookups[i]); -+ bcm2708_init_clocks(); - - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); -diff --git a/arch/arm/mach-bcm2708/clock.c b/arch/arm/mach-bcm2708/clock.c -deleted file mode 100644 -index 4fc556e..0000000 ---- a/arch/arm/mach-bcm2708/clock.c -+++ /dev/null -@@ -1,61 +0,0 @@ --/* -- * linux/arch/arm/mach-bcm2708/clock.c -- * -- * Copyright (C) 2010 Broadcom -- * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version. -- * -- * This program is distributed in the hope that it will be useful, -- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- */ --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "clock.h" -- --int clk_enable(struct clk *clk) --{ -- return 0; --} --EXPORT_SYMBOL(clk_enable); -- --void clk_disable(struct clk *clk) --{ --} --EXPORT_SYMBOL(clk_disable); -- --unsigned long clk_get_rate(struct clk *clk) --{ -- return clk->rate; --} --EXPORT_SYMBOL(clk_get_rate); -- --long clk_round_rate(struct clk *clk, unsigned long rate) --{ -- return clk->rate; --} --EXPORT_SYMBOL(clk_round_rate); -- --int clk_set_rate(struct clk *clk, unsigned long rate) --{ -- return -EIO; --} --EXPORT_SYMBOL(clk_set_rate); -diff --git a/arch/arm/mach-bcm2708/clock.h b/arch/arm/mach-bcm2708/clock.h -deleted file mode 100644 -index 5f9d725..0000000 ---- a/arch/arm/mach-bcm2708/clock.h -+++ /dev/null -@@ -1,24 +0,0 @@ --/* -- * linux/arch/arm/mach-bcm2708/clock.h -- * -- * Copyright (C) 2010 Broadcom -- * -- * This program is free software; you can redistribute it and/or modify -- * it under the terms of the GNU General Public License as published by -- * the Free Software Foundation; either version 2 of the License, or -- * (at your option) any later version. -- * -- * This program is distributed in the hope that it will be useful, -- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -- */ --struct module; -- --struct clk { -- unsigned long rate; --}; - -From ec2d83be8df77ac09c47dac5f3ac7218d92e1619 Mon Sep 17 00:00:00 2001 -From: notro -Date: Wed, 9 Jul 2014 14:46:08 +0200 -Subject: [PATCH 050/172] BCM2708: Add core Device Tree support - -Add the bare minimum needed to boot BCM2708 from a Device Tree. - -Signed-off-by: Noralf Tronnes - -BCM2708: DT: change 'axi' nodename to 'soc' - -Change DT node named 'axi' to 'soc' so it matches ARCH_BCM2835. -The VC4 bootloader fills in certain properties in the 'axi' subtree, -but since this is part of an upstreaming effort, the name is changed. - -Signed-off-by: Noralf Tronnes notro@tronnes.org ---- - arch/arm/boot/dts/Makefile | 1 + - arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 ++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 27 +++++++++++++++++++++++++++ - arch/arm/mach-bcm2708/Kconfig | 8 ++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 24 ++++++++++++++++++++++++ - 5 files changed, 68 insertions(+) - create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b.dts - create mode 100644 arch/arm/boot/dts/bcm2708.dtsi - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 91bd5bd..d177201 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -53,6 +53,7 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb -+dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -new file mode 100644 -index 0000000..e319c8e ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -0,0 +1,8 @@ -+/dts-v1/; -+ -+/include/ "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi"; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -new file mode 100644 -index 0000000..50da059 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -0,0 +1,27 @@ -+/include/ "skeleton.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "BCM2708"; -+ -+ chosen { -+ /* -+ bootargs must be 1024 characters long because the -+ VC bootloader can't expand it -+ */ -+ bootargs = "console=ttyAMA0 "; -+ }; -+ -+ soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges = <0x7e000000 0x20000000 0x02000000>; -+ }; -+ -+ clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ }; -+}; -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index e151ed4..182e7ba 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -9,6 +9,14 @@ config MACH_BCM2708 - help - Include support for the Broadcom(R) BCM2708 platform. - -+config BCM2708_DT -+ bool "BCM2708 Device Tree support" -+ depends on MACH_BCM2708 -+ default n -+ select USE_OF -+ help -+ Enable Device Tree support for BCM2708 -+ - config BCM2708_GPIO - bool "BCM2708 gpio support" - depends on MACH_BCM2708 -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index ef12cb8..747e27a 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -33,6 +33,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -734,6 +735,22 @@ static void bcm2708_power_off(void) - } - } - -+#ifdef CONFIG_OF -+static void __init bcm2708_dt_init(void) -+{ -+ int ret; -+ -+ of_clk_init(NULL); -+ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -+ if (ret) { -+ pr_err("of_platform_populate failed: %d\n", ret); -+ BUG(); -+ } -+} -+#else -+static void __init bcm2708_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ - void __init bcm2708_init(void) - { - int i; -@@ -745,6 +762,7 @@ void __init bcm2708_init(void) - pm_power_off = bcm2708_power_off; - - bcm2708_init_clocks(); -+ bcm2708_dt_init(); - - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); -@@ -965,6 +983,11 @@ static void __init board_reserve(void) - #endif - } - -+static const char * const bcm2708_compat[] = { -+ "brcm,bcm2708", -+ NULL -+}; -+ - MACHINE_START(BCM2708, "BCM2708") - /* Maintainer: Broadcom Europe Ltd. */ - .map_io = bcm2708_map_io, -@@ -974,6 +997,7 @@ MACHINE_START(BCM2708, "BCM2708") - .init_early = bcm2708_init_early, - .reserve = board_reserve, - .restart = bcm2708_restart, -+ .dt_compat = bcm2708_compat, - MACHINE_END - - module_param(boardrev, uint, 0644); - -From be8c3d8a94f6744f2e6b3f3d41c3604d4c6cb217 Mon Sep 17 00:00:00 2001 -From: notro -Date: Wed, 9 Jul 2014 14:47:48 +0200 -Subject: [PATCH 051/172] BCM2708: armctrl: Add IRQ Device Tree support - -Add Device Tree IRQ support for BCM2708. -Usage is the same as for irq-bcm2835. -See binding document: brcm,bcm2835-armctrl-ic.txt - -A bank 3 is added to handle GPIO interrupts. This is done because -armctrl also handles GPIO interrupts. - -Signed-off-by: Noralf Tronnes - -BCM2708: armctrl: remove irq bank 3 - -irq bank 3 was needed by the pinctrl-bcm2708 and bcm2708_gpio -combination. It is no longer required. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708.dtsi | 9 ++++ - arch/arm/mach-bcm2708/armctrl.c | 96 +++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 105 insertions(+) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 50da059..a06f5b8 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -4,6 +4,8 @@ - compatible = "brcm,bcm2708"; - model = "BCM2708"; - -+ interrupt-parent = <&intc>; -+ - chosen { - /* - bootargs must be 1024 characters long because the -@@ -17,6 +19,13 @@ - #address-cells = <1>; - #size-cells = <1>; - ranges = <0x7e000000 0x20000000 0x02000000>; -+ -+ intc: interrupt-controller { -+ compatible = "brcm,bcm2708-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c -index 96fa9b9..74bacb3 100644 ---- a/arch/arm/mach-bcm2708/armctrl.c -+++ b/arch/arm/mach-bcm2708/armctrl.c -@@ -23,6 +23,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -79,6 +81,99 @@ static void armctrl_unmask_irq(struct irq_data *d) - } - } - -+#ifdef CONFIG_OF -+ -+#define NR_IRQS_BANK0 21 -+#define NR_BANKS 3 -+#define IRQS_PER_BANK 32 -+ -+/* from drivers/irqchip/irq-bcm2835.c */ -+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, -+ const u32 *intspec, unsigned int intsize, -+ unsigned long *out_hwirq, unsigned int *out_type) -+{ -+ if (WARN_ON(intsize != 2)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] >= NR_BANKS)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) -+ return -EINVAL; -+ -+ if (intspec[0] == 0) -+ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; -+ else if (intspec[0] == 1) -+ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; -+ else -+ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; -+ -+ /* reverse remap_irqs[] */ -+ switch (*out_hwirq) { -+ case INTERRUPT_VC_JPEG: -+ *out_hwirq = INTERRUPT_JPEG; -+ break; -+ case INTERRUPT_VC_USB: -+ *out_hwirq = INTERRUPT_USB; -+ break; -+ case INTERRUPT_VC_3D: -+ *out_hwirq = INTERRUPT_3D; -+ break; -+ case INTERRUPT_VC_DMA2: -+ *out_hwirq = INTERRUPT_DMA2; -+ break; -+ case INTERRUPT_VC_DMA3: -+ *out_hwirq = INTERRUPT_DMA3; -+ break; -+ case INTERRUPT_VC_I2C: -+ *out_hwirq = INTERRUPT_I2C; -+ break; -+ case INTERRUPT_VC_SPI: -+ *out_hwirq = INTERRUPT_SPI; -+ break; -+ case INTERRUPT_VC_I2SPCM: -+ *out_hwirq = INTERRUPT_I2SPCM; -+ break; -+ case INTERRUPT_VC_SDIO: -+ *out_hwirq = INTERRUPT_SDIO; -+ break; -+ case INTERRUPT_VC_UART: -+ *out_hwirq = INTERRUPT_UART; -+ break; -+ case INTERRUPT_VC_ARASANSDIO: -+ *out_hwirq = INTERRUPT_ARASANSDIO; -+ break; ++ } + } + -+ *out_type = IRQ_TYPE_NONE; -+ return 0; ++ if (!waiter) { ++ waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL); ++ if (!waiter) { ++ vchiq_log_error(vchiq_core_log_level, ++ "%s - out of memory", __func__); ++ return VCHIQ_ERROR; ++ } ++ } ++ ++ status = vchiq_bulk_transfer(handle, VCHI_MEM_HANDLE_INVALID, ++ data, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, ++ dir); ++ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || ++ !waiter->bulk_waiter.bulk) { ++ VCHIQ_BULK_T *bulk = waiter->bulk_waiter.bulk; ++ if (bulk) { ++ /* Cancel the signal when the transfer ++ ** completes. */ ++ spin_lock(&bulk_waiter_spinlock); ++ bulk->userdata = NULL; ++ spin_unlock(&bulk_waiter_spinlock); ++ } ++ kfree(waiter); ++ } else { ++ waiter->pid = current->pid; ++ mutex_lock(&instance->bulk_waiter_list_mutex); ++ list_add(&waiter->list, &instance->bulk_waiter_list); ++ mutex_unlock(&instance->bulk_waiter_list_mutex); ++ vchiq_log_info(vchiq_arm_log_level, ++ "saved bulk_waiter %x for pid %d", ++ (unsigned int)waiter, current->pid); ++ } ++ ++ return status; +} -+ -+static struct irq_domain_ops armctrl_ops = { -+ .xlate = armctrl_xlate -+}; -+ -+void __init armctrl_dt_init(void) -+{ -+ struct device_node *np; -+ struct irq_domain *domain; -+ -+ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); -+ if (!np) -+ return; -+ -+ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, -+ IRQ_ARMCTRL_START, 0, -+ &armctrl_ops, NULL); -+ WARN_ON(!domain); -+} -+#else -+void __init armctrl_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ - #if defined(CONFIG_PM) - - /* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ -@@ -215,5 +310,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, - - armctrl_pm_register(base, irq_start, resume_sources); - init_FIQ(FIQ_START); -+ armctrl_dt_init(); - return 0; - } - -From 235c1c651b6b85fc9f83940c08bc2055902767c9 Mon Sep 17 00:00:00 2001 -From: notro -Date: Thu, 10 Jul 2014 13:59:47 +0200 -Subject: [PATCH 052/172] BCM2708: use pinctrl-bcm2835 - -Use pinctrl-bcm2835 instead of the pinctrl-bcm2708 and bcm2708_gpio -combination. - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708.dtsi | 12 ++++++++++++ - arch/arm/mach-bcm2708/Kconfig | 3 +++ - arch/arm/mach-bcm2708/bcm2708.c | 2 +- - drivers/pinctrl/pinctrl-bcm2835.c | 2 +- - 4 files changed, 17 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index a06f5b8..b2920c8 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -26,6 +26,18 @@ - interrupt-controller; - #interrupt-cells = <2>; - }; -+ -+ gpio: gpio { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ interrupts = <2 17>, <2 18>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig -index 182e7ba..4cfae55 100644 ---- a/arch/arm/mach-bcm2708/Kconfig -+++ b/arch/arm/mach-bcm2708/Kconfig -@@ -14,6 +14,9 @@ config BCM2708_DT - depends on MACH_BCM2708 - default n - select USE_OF -+ select ARCH_REQUIRE_GPIOLIB -+ select PINCTRL -+ select PINCTRL_BCM2835 - help - Enable Device Tree support for BCM2708 - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 747e27a..0e5bd43 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -767,7 +767,7 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_dmaman_device); - bcm_register_device(&bcm2708_vcio_device); - #ifdef CONFIG_BCM2708_GPIO -- bcm_register_device(&bcm2708_gpio_device); -+ bcm_register_device_dt(&bcm2708_gpio_device); - #endif - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; -diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index 9aa8a3f..9d1149e 100644 ---- a/drivers/pinctrl/pinctrl-bcm2835.c -+++ b/drivers/pinctrl/pinctrl-bcm2835.c -@@ -382,7 +382,7 @@ static struct gpio_chip bcm2835_gpio_chip = { - .get = bcm2835_gpio_get, - .set = bcm2835_gpio_set, - .to_irq = bcm2835_gpio_to_irq, -- .base = -1, -+ .base = 0, - .ngpio = BCM2835_NUM_GPIOS, - .can_sleep = false, - }; - -From 2fddb74c345dcb1cd2b03b60f64725bb5fbb26f0 Mon Sep 17 00:00:00 2001 -From: notro -Date: Sun, 27 Jul 2014 20:12:58 +0200 -Subject: [PATCH 053/172] spi: bcm2708: add device tree support - -Add DT support to driver and add to .dtsi file. -Setup pins and spidev in .dts file. -SPI is disabled by default. - -Signed-off-by: Noralf Tronnes - -BCM2708: don't register SPI controller when using DT - -The device for the SPI controller is in the Device Tree. -Only register the device when not using DT. - -Signed-off-by: Noralf Tronnes - -spi: bcm2835: make driver available on ARCH_BCM2708 - -Make this driver available on ARCH_BCM2708 - -Signed-off-by: Noralf Tronnes - -bcm2708: Remove the prohibition on mixing SPIDEV and DT ---- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 32 ++++++++++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 18 ++++++++++++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++++++++++--- - drivers/spi/Kconfig | 2 +- - drivers/spi/spi-bcm2708.c | 8 ++++++++ - 5 files changed, 75 insertions(+), 4 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index e319c8e..30107fb 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -5,4 +5,36 @@ - / { - compatible = "brcm,bcm2708"; - model = "Raspberry Pi"; -+ -+ aliases { -+ spi0 = &spi0; -+ }; -+}; -+ -+&gpio { -+ spi0_pins: spi0_pins { -+ brcm,pins = <7 8 9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins>; -+ -+ spidev@0{ -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+ -+ spidev@1{ -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; - }; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index b2920c8..e90bf4c 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -38,11 +38,29 @@ - interrupt-controller; - #interrupt-cells = <2>; - }; -+ -+ spi0: spi@7e204000 { -+ compatible = "brcm,bcm2708-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clk_spi>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; - }; - - clocks { - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <0>; -+ -+ clk_spi: clock@2 { -+ compatible = "fixed-clock"; -+ reg = <2>; -+ #clock-cells = <0>; -+ clock-output-names = "spi"; -+ clock-frequency = <250000000>; -+ }; - }; - }; -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 0e5bd43..a2069f8 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -486,6 +486,7 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - -+#ifndef CONFIG_OF - static struct resource bcm2708_spi_resources[] = { - { - .start = SPI0_BASE, -@@ -509,6 +510,7 @@ static struct platform_device bcm2708_spi_device = { - .dma_mask = &bcm2708_spi_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, - }; -+#endif - - #ifdef CONFIG_BCM2708_SPIDEV - static struct spi_board_info bcm2708_spi_devices[] = { -@@ -670,6 +672,16 @@ int __init bcm_register_device(struct platform_device *pdev) - return ret; - } - -+/* -+ * Use this macro for platform devices that are present in the Device Tree. -+ * This way the device is only added on non-DT builds. +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h +new file mode 100644 +index 0000000..335446e +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_killable.h +@@ -0,0 +1,69 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -+#ifdef CONFIG_OF -+#define bcm_register_device_dt(pdev) -+#else -+#define bcm_register_device_dt(pdev) bcm_register_device(pdev) ++ ++#ifndef VCHIQ_KILLABLE_H ++#define VCHIQ_KILLABLE_H ++ ++#include ++#include ++ ++#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTRAP) | sigmask(SIGSTOP) | sigmask(SIGCONT)) ++ ++static inline int __must_check down_interruptible_killable(struct semaphore *sem) ++{ ++ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ ++ int ret; ++ sigset_t blocked, oldset; ++ siginitsetinv(&blocked, SHUTDOWN_SIGS); ++ sigprocmask(SIG_SETMASK, &blocked, &oldset); ++ ret = down_interruptible(sem); ++ sigprocmask(SIG_SETMASK, &oldset, NULL); ++ return ret; ++} ++#define down_interruptible down_interruptible_killable ++ ++ ++static inline int __must_check mutex_lock_interruptible_killable(struct mutex *lock) ++{ ++ /* Allow interception of killable signals only. We don't want to be interrupted by harmless signals like SIGALRM */ ++ int ret; ++ sigset_t blocked, oldset; ++ siginitsetinv(&blocked, SHUTDOWN_SIGS); ++ sigprocmask(SIG_SETMASK, &blocked, &oldset); ++ ret = mutex_lock_interruptible(lock); ++ sigprocmask(SIG_SETMASK, &oldset, NULL); ++ return ret; ++} ++#define mutex_lock_interruptible mutex_lock_interruptible_killable ++ +#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +new file mode 100644 +index 0000000..d02e776 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +@@ -0,0 +1,71 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ + - int calc_rsts(int partition) - { - return PM_PASSWORD | -@@ -784,7 +796,7 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -- bcm_register_device(&bcm2708_spi_device); -+ bcm_register_device_dt(&bcm2708_spi_device); - bcm_register_device(&bcm2708_bsc0_device); - bcm_register_device(&bcm2708_bsc1_device); - -@@ -824,8 +836,9 @@ void __init bcm2708_init(void) - system_serial_low = serial; - - #ifdef CONFIG_BCM2708_SPIDEV -- spi_register_board_info(bcm2708_spi_devices, -- ARRAY_SIZE(bcm2708_spi_devices)); -+ if (!use_dt) -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); - #endif - } - -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index ae4ea48..9f202dd 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -77,7 +77,7 @@ config SPI_ATMEL - - config SPI_BCM2835 - tristate "BCM2835 SPI controller" -- depends on ARCH_BCM2835 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || COMPILE_TEST - help - This selects a driver for the Broadcom BCM2835 SPI master. - -diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c -index 349d21f..041b5e2 100644 ---- a/drivers/spi/spi-bcm2708.c -+++ b/drivers/spi/spi-bcm2708.c -@@ -512,6 +512,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) - master->setup = bcm2708_spi_setup; - master->transfer = bcm2708_spi_transfer; - master->cleanup = bcm2708_spi_cleanup; -+ master->dev.of_node = pdev->dev.of_node; - platform_set_drvdata(pdev, master); - - bs = spi_master_get_devdata(master); -@@ -596,10 +597,17 @@ static int bcm2708_spi_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_spi_match[] = { -+ { .compatible = "brcm,bcm2708-spi", }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_spi_match); ++#ifndef VCHIQ_MEMDRV_H ++#define VCHIQ_MEMDRV_H + - static struct platform_driver bcm2708_spi_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_spi_match, - }, - .probe = bcm2708_spi_probe, - .remove = bcm2708_spi_remove, - -From a9dfef3e2161f0ec30100b32639de3d6d0682836 Mon Sep 17 00:00:00 2001 -From: notro -Date: Tue, 29 Jul 2014 11:04:49 +0200 -Subject: [PATCH 054/172] i2c: bcm2708: add device tree support - -Add DT support to driver and add to .dtsi file. -Setup pins in .dts file. -i2c is disabled by default. - -Signed-off-by: Noralf Tronnes - -bcm2708: don't register i2c controllers when using DT - -The devices for the i2c controllers are in the Device Tree. -Only register devices when not using DT. - -Signed-off-by: Noralf Tronnes - -i2c: bcm2835: make driver available on ARCH_BCM2708 - -Make this driver available on ARCH_BCM2708 - -Signed-off-by: Noralf Tronnes ---- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 24 ++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708.dtsi | 27 +++++++++++++++++++++++++++ - arch/arm/mach-bcm2708/bcm2708.c | 6 ++++-- - drivers/i2c/busses/Kconfig | 2 +- - drivers/i2c/busses/i2c-bcm2708.c | 24 ++++++++++++++++++++++++ - 5 files changed, 80 insertions(+), 3 deletions(-) - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 30107fb..5893122 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -8,6 +8,8 @@ - - aliases { - spi0 = &spi0; -+ i2c0 = &i2c0; -+ i2c1 = &i2c1; - }; - }; - -@@ -16,6 +18,16 @@ - brcm,pins = <7 8 9 10 11>; - brcm,function = <4>; /* alt0 */ - }; ++/* ---- Include Files ----------------------------------------------------- */ + -+ i2c0_pins: i2c0 { -+ brcm,pins = <0 1>; -+ brcm,function = <4>; -+ }; ++#include ++#include "vchiq_if.h" + -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; -+ }; - }; - - &spi0 { -@@ -38,3 +50,15 @@ - spi-max-frequency = <500000>; - }; - }; ++/* ---- Constants and Types ---------------------------------------------- */ + -+&i2c0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c0_pins>; -+ clock-frequency = <100000>; -+}; ++typedef struct { ++ void *armSharedMemVirt; ++ dma_addr_t armSharedMemPhys; ++ size_t armSharedMemSize; + -+&i2c1 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c1_pins>; -+ clock-frequency = <100000>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index e90bf4c..2ca6d63 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -48,6 +48,26 @@ - #size-cells = <0>; - status = "disabled"; - }; ++ void *vcSharedMemVirt; ++ dma_addr_t vcSharedMemPhys; ++ size_t vcSharedMemSize; ++} VCHIQ_SHARED_MEM_INFO_T; + -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; ++/* ---- Variable Externs ------------------------------------------------- */ + -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; - }; - - clocks { -@@ -55,6 +75,13 @@ - #address-cells = <1>; - #size-cells = <0>; - -+ clk_i2c: i2c { -+ compatible = "fixed-clock"; -+ reg = <1>; -+ #clock-cells = <0>; -+ clock-frequency = <250000000>; -+ }; ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_get_shared_mem_info(VCHIQ_SHARED_MEM_INFO_T *info); ++ ++VCHIQ_STATUS_T vchiq_memdrv_initialise(void); ++ ++VCHIQ_STATUS_T vchiq_userdrv_create_instance( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ ++VCHIQ_STATUS_T vchiq_userdrv_suspend( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); ++ ++VCHIQ_STATUS_T vchiq_userdrv_resume( ++ const VCHIQ_PLATFORM_DATA_T * platform_data); + - clk_spi: clock@2 { - compatible = "fixed-clock"; - reg = <2>; -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index a2069f8..b45f327 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -532,6 +532,7 @@ static struct spi_board_info bcm2708_spi_devices[] = { - }; - #endif - -+#ifndef CONFIG_OF - static struct resource bcm2708_bsc0_resources[] = { - { - .start = BSC0_BASE, -@@ -570,6 +571,7 @@ static struct platform_device bcm2708_bsc1_device = { - .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), - .resource = bcm2708_bsc1_resources, - }; +#endif - - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", -@@ -797,8 +799,8 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_alsa_devices[i]); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device(&bcm2708_bsc0_device); -- bcm_register_device(&bcm2708_bsc1_device); -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); - - bcm_register_device(&bcm2835_hwmon_device); - bcm_register_device(&bcm2835_thermal_device); -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 68362ab..7b7ba6f 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -362,7 +362,7 @@ config I2C_AXXIA - - config I2C_BCM2835 - tristate "Broadcom BCM2835 I2C controller" -- depends on ARCH_BCM2835 -+ depends on ARCH_BCM2835 || ARCH_BCM2708 - help - If you say yes to this option, support will be included for the - BCM2835 I2C controller. -diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c -index 7d385a3..526129b 100644 ---- a/drivers/i2c/busses/i2c-bcm2708.c -+++ b/drivers/i2c/busses/i2c-bcm2708.c -@@ -26,6 +26,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -303,6 +304,21 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) - unsigned long bus_hz; - u32 cdiv; - -+ if (pdev->dev.of_node) { -+ u32 bus_clk_rate; -+ pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); -+ if (pdev->id < 0) { -+ dev_err(&pdev->dev, "alias is missing\n"); -+ return -EINVAL; -+ } -+ if (!of_property_read_u32(pdev->dev.of_node, -+ "clock-frequency", &bus_clk_rate)) -+ baudrate = bus_clk_rate; -+ else -+ dev_warn(&pdev->dev, -+ "Could not read clock-frequency property\n"); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +new file mode 100644 +index 0000000..54a3ece +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +@@ -0,0 +1,58 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_PAGELIST_H ++#define VCHIQ_PAGELIST_H ++ ++#ifndef PAGE_SIZE ++#define PAGE_SIZE 4096 ++#endif ++#define CACHE_LINE_SIZE 32 ++#define PAGELIST_WRITE 0 ++#define PAGELIST_READ 1 ++#define PAGELIST_READ_WITH_FRAGMENTS 2 ++ ++typedef struct pagelist_struct { ++ unsigned long length; ++ unsigned short type; ++ unsigned short offset; ++ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following ++ pages at consecutive addresses. */ ++} PAGELIST_T; ++ ++typedef struct fragments_struct { ++ char headbuf[CACHE_LINE_SIZE]; ++ char tailbuf[CACHE_LINE_SIZE]; ++} FRAGMENTS_T; ++ ++#endif /* VCHIQ_PAGELIST_H */ +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +new file mode 100644 +index 0000000..8072ff6 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -0,0 +1,860 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include ++#include ++ ++#include "interface/vchi/vchi.h" ++#include "vchiq.h" ++#include "vchiq_core.h" ++ ++#include "vchiq_util.h" ++ ++#include ++ ++#define vchiq_status_to_vchi(status) ((int32_t)status) ++ ++typedef struct { ++ VCHIQ_SERVICE_HANDLE_T handle; ++ ++ VCHIU_QUEUE_T queue; ++ ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++} SHIM_SERVICE_T; ++ ++/* ---------------------------------------------------------------------- ++ * return pointer to the mphi message driver function table ++ * -------------------------------------------------------------------- */ ++const VCHI_MESSAGE_DRIVER_T * ++vchi_mphi_message_driver_func_table(void) ++{ ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- ++ * return a pointer to the 'single' connection driver fops ++ * -------------------------------------------------------------------- */ ++const VCHI_CONNECTION_API_T * ++single_get_func_table(void) ++{ ++ return NULL; ++} ++ ++VCHI_CONNECTION_T *vchi_create_connection( ++ const VCHI_CONNECTION_API_T *function_table, ++ const VCHI_MESSAGE_DRIVER_T *low_level) ++{ ++ (void)function_table; ++ (void)low_level; ++ return NULL; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_peek ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ ++ ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to return a pointer to the current message (to allow in ++ * place processing). The message can be removed using ++ * vchi_msg_remove when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_peek(VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_peek(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_peek); ++ ++/*********************************************************** ++ * Name: vchi_msg_remove ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * ++ * Description: Routine to remove a message (after it has been read with ++ * vchi_msg_peek) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_remove(VCHI_SERVICE_HANDLE_T handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_remove); ++ ++/*********************************************************** ++ * Name: vchi_msg_queue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle, ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_ELEMENT_T element = {data, data_size}; ++ VCHIQ_STATUS_T status; ++ ++ (void)msg_handle; ++ ++ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ status = vchiq_queue_message(service->handle, &element, 1); ++ ++ /* vchiq_queue_message() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_queue_message(service->handle, &element, 1); + } + - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!regs) { - dev_err(&pdev->dev, "could not get IO memory\n"); -@@ -336,6 +352,7 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) - adap->dev.parent = &pdev->dev; - adap->nr = pdev->id; - strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); -+ adap->dev.of_node = pdev->dev.of_node; - - switch (pdev->id) { - case 0: -@@ -416,10 +433,17 @@ static int bcm2708_i2c_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_i2c_of_match[] = { -+ { .compatible = "brcm,bcm2708-i2c" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_msg_queue); + - static struct platform_driver bcm2708_i2c_driver = { - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_i2c_of_match, - }, - .probe = bcm2708_i2c_probe, - .remove = bcm2708_i2c_remove, - -From 822709b4718fcc2922306017b9307f1523eb49ed Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Mon, 14 Jul 2014 22:02:09 +0100 -Subject: [PATCH 055/172] hid: Reduce default mouse polling interval to 60Hz - -Reduces overhead when using X ---- - drivers/hid/usbhid/hid-core.c | 10 +++++++--- - 1 file changed, 7 insertions(+), 3 deletions(-) - -diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c -index bfbe1be..a738b25 100644 ---- a/drivers/hid/usbhid/hid-core.c -+++ b/drivers/hid/usbhid/hid-core.c -@@ -49,7 +49,7 @@ - * Module parameters. - */ - --static unsigned int hid_mousepoll_interval; -+static unsigned int hid_mousepoll_interval = ~0; - module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); - MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); - -@@ -1090,8 +1090,12 @@ static int usbhid_start(struct hid_device *hid) - } - - /* Change the polling interval of mice. */ -- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) -- interval = hid_mousepoll_interval; -+ if (hid->collection->usage == HID_GD_MOUSE) { -+ if (hid_mousepoll_interval == ~0 && interval < 16) -+ interval = 16; -+ else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0) -+ interval = hid_mousepoll_interval; ++/*********************************************************** ++ * Name: vchi_bulk_queue_receive ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * void *data_dst, ++ * const uint32_t data_size, ++ * VCHI_FLAGS_T flags ++ * void *bulk_handle ++ * ++ * Description: Routine to setup a rcv buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_receive(VCHI_SERVICE_HANDLE_T handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE ++ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ WARN_ON(!service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ WARN(1, "unsupported message\n"); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_receive(service->handle, data_dst, data_size, ++ bulk_handle, mode); ++ ++ /* vchiq_bulk_receive() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_bulk_receive(service->handle, data_dst, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_bulk_queue_receive); ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_transmit ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * const void *data_src, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *bulk_handle ++ * ++ * Description: Routine to transmit some data ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_transmit(VCHI_SERVICE_HANDLE_T handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE ++ | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ WARN_ON(!service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ WARN(1, "unsupported message\n"); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_transmit(service->handle, data_src, data_size, ++ bulk_handle, mode); ++ ++ /* vchiq_bulk_transmit() may return VCHIQ_RETRY, so we need to ++ ** implement a retry mechanism since this function is supposed ++ ** to block until queued ++ */ ++ while (status == VCHIQ_RETRY) { ++ msleep(1); ++ status = vchiq_bulk_transmit(service->handle, data_src, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_bulk_queue_transmit); ++ ++/*********************************************************** ++ * Name: vchi_msg_dequeue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void *data, ++ * uint32_t max_data_size_to_read, ++ * uint32_t *actual_msg_size ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to dequeue a message into the supplied buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_dequeue(VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ memcpy(data, header->data, header->size < max_data_size_to_read ? ++ header->size : max_data_size_to_read); ++ ++ *actual_msg_size = header->size; ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_dequeue); ++ ++/*********************************************************** ++ * Name: vchi_msg_queuev ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * VCHI_MSG_VECTOR_T *vector, ++ * uint32_t count, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++vchiq_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); ++vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == ++ offsetof(VCHIQ_ELEMENT_T, data)); ++vchiq_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == ++ offsetof(VCHIQ_ELEMENT_T, size)); ++ ++int32_t vchi_msg_queuev(VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ ++ (void)msg_handle; ++ ++ WARN_ON(flags != VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, ++ (const VCHIQ_ELEMENT_T *)vector, count)); ++} ++EXPORT_SYMBOL(vchi_msg_queuev); ++ ++/*********************************************************** ++ * Name: vchi_held_msg_release ++ * ++ * Arguments: VCHI_HELD_MSG_T *message ++ * ++ * Description: Routine to release a held message (after it has been read with ++ * vchi_msg_hold) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) ++{ ++ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, ++ (VCHIQ_HEADER_T *)message->message); ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_held_msg_release); ++ ++/*********************************************************** ++ * Name: vchi_msg_hold ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags, ++ * VCHI_HELD_MSG_T *message_handle ++ * ++ * Description: Routine to return a pointer to the current message (to allow ++ * in place processing). The message is dequeued - don't forget ++ * to release the message using vchi_held_msg_release when you're ++ * finished. ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_handle) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ WARN_ON((flags != VCHI_FLAGS_NONE) && ++ (flags != VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE)); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ message_handle->service = ++ (struct opaque_vchi_service_t *)service->handle; ++ message_handle->message = header; ++ ++ return 0; ++} ++EXPORT_SYMBOL(vchi_msg_hold); ++ ++/*********************************************************** ++ * Name: vchi_initialise ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * ++ * Description: Initialises the hardware but does not transmit anything ++ * When run as a Host App this will be called twice hence the need ++ * to malloc the state information ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++ ++int32_t vchi_initialise(VCHI_INSTANCE_T *instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_STATUS_T status; ++ ++ status = vchiq_initialise(&instance); ++ ++ *instance_handle = (VCHI_INSTANCE_T)instance; ++ ++ return vchiq_status_to_vchi(status); ++} ++EXPORT_SYMBOL(vchi_initialise); ++ ++/*********************************************************** ++ * Name: vchi_connect ++ * ++ * Arguments: VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * VCHI_INSTANCE_T instance_handle) ++ * ++ * Description: Starts the command service on each connection, ++ * causing INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_connect(VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ ++ (void)connections; ++ (void)num_connections; ++ ++ return vchiq_connect(instance); ++} ++EXPORT_SYMBOL(vchi_connect); ++ ++ ++/*********************************************************** ++ * Name: vchi_disconnect ++ * ++ * Arguments: VCHI_INSTANCE_T instance_handle ++ * ++ * Description: Stops the command service on each connection, ++ * causing DE-INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_disconnect(VCHI_INSTANCE_T instance_handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ return vchiq_status_to_vchi(vchiq_shutdown(instance)); ++} ++EXPORT_SYMBOL(vchi_disconnect); ++ ++ ++/*********************************************************** ++ * Name: vchi_service_open ++ * Name: vchi_service_create ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * SERVICE_CREATION_T *setup, ++ * VCHI_SERVICE_HANDLE_T *handle ++ * ++ * Description: Routine to open a service ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) ++{ ++ SHIM_SERVICE_T *service = ++ (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); ++ ++ if (!service->callback) ++ goto release; ++ ++ switch (reason) { ++ case VCHIQ_MESSAGE_AVAILABLE: ++ vchiu_queue_push(&service->queue, header); ++ ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_MSG_AVAILABLE, NULL); ++ ++ goto done; ++ break; ++ ++ case VCHIQ_BULK_TRANSMIT_DONE: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_SENT, bulk_user); ++ break; ++ ++ case VCHIQ_BULK_RECEIVE_DONE: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVED, bulk_user); ++ break; ++ ++ case VCHIQ_SERVICE_CLOSED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_SERVICE_CLOSED, NULL); ++ break; ++ ++ case VCHIQ_SERVICE_OPENED: ++ /* No equivalent VCHI reason */ ++ break; ++ ++ case VCHIQ_BULK_TRANSMIT_ABORTED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++ bulk_user); ++ break; ++ ++ case VCHIQ_BULK_RECEIVE_ABORTED: ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ bulk_user); ++ break; ++ ++ default: ++ WARN(1, "not supported\n"); ++ break; ++ } ++ ++release: ++ vchiq_release_message(service->handle, header); ++done: ++ return VCHIQ_SUCCESS; ++} ++ ++static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, ++ SERVICE_CREATION_T *setup) ++{ ++ SHIM_SERVICE_T *service = kzalloc(sizeof(SHIM_SERVICE_T), GFP_KERNEL); ++ ++ (void)instance; ++ ++ if (service) { ++ if (vchiu_queue_init(&service->queue, 64)) { ++ service->callback = setup->callback; ++ service->callback_param = setup->callback_param; ++ } else { ++ kfree(service); ++ service = NULL; + } - - ret = -ENOMEM; - if (usb_endpoint_dir_in(endpoint)) { ++ } ++ ++ return service; ++} ++ ++static void service_free(SHIM_SERVICE_T *service) ++{ ++ if (service) { ++ vchiu_queue_delete(&service->queue); ++ kfree(service); ++ } ++} ++ ++int32_t vchi_service_open(VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ if (service) { ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.callback = shim_callback; ++ params.userdata = service; ++ params.version = setup->version.version; ++ params.version_min = setup->version.version_min; ++ ++ status = vchiq_open_service(instance, ¶ms, ++ &service->handle); ++ if (status != VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ *handle = NULL; ++ } ++ } ++ ++ return (service != NULL) ? 0 : -1; ++} ++EXPORT_SYMBOL(vchi_service_open); ++ ++int32_t vchi_service_create(VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ if (service) { ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.callback = shim_callback; ++ params.userdata = service; ++ params.version = setup->version.version; ++ params.version_min = setup->version.version_min; ++ status = vchiq_add_service(instance, ¶ms, &service->handle); ++ ++ if (status != VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ *handle = NULL; ++ } ++ } ++ ++ return (service != NULL) ? 0 : -1; ++} ++EXPORT_SYMBOL(vchi_service_create); ++ ++int32_t vchi_service_close(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) { ++ VCHIQ_STATUS_T status = vchiq_close_service(service->handle); ++ if (status == VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_close); ++ ++int32_t vchi_service_destroy(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) { ++ VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); ++ if (status == VCHIQ_SUCCESS) { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_destroy); ++ ++int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_SERVICE_OPTION_T option, ++ int value) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_SERVICE_OPTION_T vchiq_option; ++ switch (option) { ++ case VCHI_SERVICE_OPTION_TRACE: ++ vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; ++ break; ++ case VCHI_SERVICE_OPTION_SYNCHRONOUS: ++ vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; ++ break; ++ default: ++ service = NULL; ++ break; ++ } ++ if (service) { ++ VCHIQ_STATUS_T status = ++ vchiq_set_service_option(service->handle, ++ vchiq_option, ++ value); ++ ++ ret = vchiq_status_to_vchi(status); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_set_option); ++ ++int32_t vchi_get_peer_version( const VCHI_SERVICE_HANDLE_T handle, short *peer_version ) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ VCHIQ_STATUS_T status = vchiq_get_peer_version(service->handle, peer_version); ++ ret = vchiq_status_to_vchi( status ); ++ } ++ return ret; ++} ++EXPORT_SYMBOL(vchi_get_peer_version); ++ ++/* ---------------------------------------------------------------------- ++ * read a uint32_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint32_t ++vchi_readbuf_uint32(const void *_ptr) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint32_t to buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint32(void *_ptr, uint32_t value) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (unsigned char)((value >> 0) & 0xFF); ++ ptr[1] = (unsigned char)((value >> 8) & 0xFF); ++ ptr[2] = (unsigned char)((value >> 16) & 0xFF); ++ ptr[3] = (unsigned char)((value >> 24) & 0xFF); ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint16_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint16_t ++vchi_readbuf_uint16(const void *_ptr) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint16_t into the buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint16(void *_ptr, uint16_t value) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (value >> 0) & 0xFF; ++ ptr[1] = (value >> 8) & 0xFF; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_use ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to increment refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_use(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) ++ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_use); ++ ++/*********************************************************** ++ * Name: vchi_service_release ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to decrement refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_release(const VCHI_SERVICE_HANDLE_T handle) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if (service) ++ ret = vchiq_status_to_vchi( ++ vchiq_release_service(service->handle)); ++ return ret; ++} ++EXPORT_SYMBOL(vchi_service_release); +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +new file mode 100644 +index 0000000..05e7979 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +@@ -0,0 +1,152 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "vchiq_util.h" ++#include "vchiq_killable.h" ++ ++static inline int is_pow2(int i) ++{ ++ return i && !(i & (i - 1)); ++} ++ ++int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) ++{ ++ WARN_ON(!is_pow2(size)); ++ ++ queue->size = size; ++ queue->read = 0; ++ queue->write = 0; ++ ++ sema_init(&queue->pop, 0); ++ sema_init(&queue->push, 0); ++ ++ queue->storage = kzalloc(size * sizeof(VCHIQ_HEADER_T *), GFP_KERNEL); ++ if (queue->storage == NULL) { ++ vchiu_queue_delete(queue); ++ return 0; ++ } ++ return 1; ++} ++ ++void vchiu_queue_delete(VCHIU_QUEUE_T *queue) ++{ ++ if (queue->storage != NULL) ++ kfree(queue->storage); ++} ++ ++int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) ++{ ++ return queue->read == queue->write; ++} ++ ++int vchiu_queue_is_full(VCHIU_QUEUE_T *queue) ++{ ++ return queue->write == queue->read + queue->size; ++} ++ ++void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) ++{ ++ while (queue->write == queue->read + queue->size) { ++ if (down_interruptible(&queue->pop) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ /* ++ * Write to queue->storage must be visible after read from ++ * queue->read ++ */ ++ smp_mb(); ++ ++ queue->storage[queue->write & (queue->size - 1)] = header; ++ ++ /* ++ * Write to queue->storage must be visible before write to ++ * queue->write ++ */ ++ smp_wmb(); ++ ++ queue->write++; ++ ++ up(&queue->push); ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) ++{ ++ while (queue->write == queue->read) { ++ if (down_interruptible(&queue->push) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ up(&queue->push); // We haven't removed anything from the queue. ++ ++ /* ++ * Read from queue->storage must be visible after read from ++ * queue->write ++ */ ++ smp_rmb(); ++ ++ return queue->storage[queue->read & (queue->size - 1)]; ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) ++{ ++ VCHIQ_HEADER_T *header; ++ ++ while (queue->write == queue->read) { ++ if (down_interruptible(&queue->push) != 0) { ++ flush_signals(current); ++ } ++ } ++ ++ /* ++ * Read from queue->storage must be visible after read from ++ * queue->write ++ */ ++ smp_rmb(); ++ ++ header = queue->storage[queue->read & (queue->size - 1)]; ++ ++ /* ++ * Read from queue->storage must be visible before write to ++ * queue->read ++ */ ++ smp_mb(); ++ ++ queue->read++; ++ ++ up(&queue->pop); ++ ++ return header; ++} +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +new file mode 100644 +index 0000000..f4d0b66 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +@@ -0,0 +1,81 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef VCHIQ_UTIL_H ++#define VCHIQ_UTIL_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* for time_t */ ++#include ++#include ++ ++#include "vchiq_if.h" ++ ++typedef struct { ++ int size; ++ int read; ++ int write; ++ ++ struct semaphore pop; ++ struct semaphore push; ++ ++ VCHIQ_HEADER_T **storage; ++} VCHIU_QUEUE_T; ++ ++extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); ++extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); ++ ++extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); ++extern int vchiu_queue_is_full(VCHIU_QUEUE_T *queue); ++ ++extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); ++ ++extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); ++extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); ++ ++#endif +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c +new file mode 100644 +index 0000000..b6bfa21 +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_version.c +@@ -0,0 +1,59 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "vchiq_build_info.h" ++#include ++ ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_hostname, "dc4-arm-01" ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_version, "9245b4c35b99b3870e1f7dc598c5692b3c66a6f0 (tainted)" ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_time, __TIME__ ); ++VC_DEBUG_DECLARE_STRING_VAR( vchiq_build_date, __DATE__ ); ++ ++const char *vchiq_get_build_hostname( void ) ++{ ++ return vchiq_build_hostname; ++} ++ ++const char *vchiq_get_build_version( void ) ++{ ++ return vchiq_build_version; ++} ++ ++const char *vchiq_get_build_date( void ) ++{ ++ return vchiq_build_date; ++} ++ ++const char *vchiq_get_build_time( void ) ++{ ++ return vchiq_build_time; ++} -From 4da45e92b0bf783d7ab22c971310d2a5f0f5fda9 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 24 Jul 2014 21:24:03 +0100 -Subject: [PATCH 056/172] usb: core: make overcurrent messages more prominent - -Hub overcurrent messages are more serious than "debug". Increase loglevel. ---- - drivers/usb/core/hub.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c -index b4bfa3a..252a3f0 100644 ---- a/drivers/usb/core/hub.c -+++ b/drivers/usb/core/hub.c -@@ -4921,7 +4921,7 @@ static void port_event(struct usb_hub *hub, int port1) - if (portchange & USB_PORT_STAT_C_OVERCURRENT) { - u16 status = 0, unused; - -- dev_dbg(&port_dev->dev, "over-current change\n"); -+ dev_notice(&port_dev->dev, "over-current change\n"); - usb_clear_port_feature(hdev, port1, - USB_PORT_FEAT_C_OVER_CURRENT); - msleep(100); /* Cool down */ - -From 1da5f92cf41f89b2415bfb8d6726604b2fbb9aca Mon Sep 17 00:00:00 2001 +From ecd6be479b786f60efbd6937f3e9be1c42122474 Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Tue, 22 Jul 2014 15:41:04 +0100 -Subject: [PATCH 057/172] vcsm: VideoCore shared memory service for BCM2835 +Subject: [PATCH 012/104] vcsm: VideoCore shared memory service for BCM2835 Add experimental support for the VideoCore shared memory service. This allows user processes to allocate memory from VideoCore's @@ -114892,10 +102031,15093 @@ index 0000000..da1c523 +MODULE_DESCRIPTION("VideoCore SharedMemory Driver"); +MODULE_LICENSE("GPL v2"); -From c98028400be74c9edc4d170293f49dc2e27d5d80 Mon Sep 17 00:00:00 2001 +From 9e35eb3125ebff976e4c9ad85a74527f75f7426c Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:51:55 +0100 +Subject: [PATCH 013/104] Add hwrng (hardware random number generator) driver + +--- + drivers/char/hw_random/Kconfig | 11 ++++ + drivers/char/hw_random/Makefile | 1 + + drivers/char/hw_random/bcm2708-rng.c | 118 +++++++++++++++++++++++++++++++++++ + 3 files changed, 130 insertions(+) + create mode 100755 drivers/char/hw_random/bcm2708-rng.c + +diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig +index de57b38..1c97093 100644 +--- a/drivers/char/hw_random/Kconfig ++++ b/drivers/char/hw_random/Kconfig +@@ -320,6 +320,17 @@ config HW_RANDOM_TPM + + If unsure, say Y. + ++config HW_RANDOM_BCM2708 ++ tristate "BCM2708 generic true random number generator support" ++ depends on HW_RANDOM && (ARCH_BCM2708 || ARCH_BCM2709) ++ ---help--- ++ This driver provides the kernel-side support for the BCM2708 hardware. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2708-rng. ++ ++ If unsure, say N. ++ + config HW_RANDOM_MSM + tristate "Qualcomm SoCs Random Number Generator support" + depends on HW_RANDOM && ARCH_QCOM +diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile +index 0b4cd57..78b019c 100644 +--- a/drivers/char/hw_random/Makefile ++++ b/drivers/char/hw_random/Makefile +@@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o + obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o + obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o + obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o ++obj-$(CONFIG_HW_RANDOM_BCM2708) += bcm2708-rng.o + obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o + obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o +diff --git a/drivers/char/hw_random/bcm2708-rng.c b/drivers/char/hw_random/bcm2708-rng.c +new file mode 100755 +index 0000000..340f004 +--- /dev/null ++++ b/drivers/char/hw_random/bcm2708-rng.c +@@ -0,0 +1,118 @@ ++/** ++ * Copyright (c) 2010-2012 Broadcom. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") version 2, as published by the Free ++ * Software Foundation. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define RNG_CTRL (0x0) ++#define RNG_STATUS (0x4) ++#define RNG_DATA (0x8) ++#define RNG_FF_THRESHOLD (0xc) ++ ++/* enable rng */ ++#define RNG_RBGEN 0x1 ++/* double speed, less random mode */ ++#define RNG_RBG2X 0x2 ++ ++/* the initial numbers generated are "less random" so will be discarded */ ++#define RNG_WARMUP_COUNT 0x40000 ++ ++static int bcm2708_rng_data_read(struct hwrng *rng, u32 *buffer) ++{ ++ void __iomem *rng_base = (void __iomem *)rng->priv; ++ unsigned words; ++ /* wait for a random number to be in fifo */ ++ do { ++ words = __raw_readl(rng_base + RNG_STATUS)>>24; ++ } ++ while (words == 0); ++ /* read the random number */ ++ *buffer = __raw_readl(rng_base + RNG_DATA); ++ return 4; ++} ++ ++static struct hwrng bcm2708_rng_ops = { ++ .name = "bcm2708", ++ .data_read = bcm2708_rng_data_read, ++}; ++ ++static int __init bcm2708_rng_init(void) ++{ ++ void __iomem *rng_base; ++ int err; ++ ++ /* map peripheral */ ++ rng_base = ioremap(RNG_BASE, 0x10); ++ pr_info("bcm2708_rng_init=%p\n", rng_base); ++ if (!rng_base) { ++ pr_err("bcm2708_rng_init failed to ioremap\n"); ++ return -ENOMEM; ++ } ++ bcm2708_rng_ops.priv = (unsigned long)rng_base; ++ ++ /* set warm-up count & enable */ ++ __raw_writel(RNG_WARMUP_COUNT, rng_base + RNG_STATUS); ++ __raw_writel(RNG_RBGEN, rng_base + RNG_CTRL); ++ ++ /* register driver */ ++ err = hwrng_register(&bcm2708_rng_ops); ++ if (err) { ++ pr_err("bcm2708_rng_init hwrng_register()=%d\n", err); ++ iounmap(rng_base); ++ } ++ return err; ++} ++ ++static void __exit bcm2708_rng_exit(void) ++{ ++ void __iomem *rng_base = (void __iomem *)bcm2708_rng_ops.priv; ++ pr_info("bcm2708_rng_exit\n"); ++ /* disable rng hardware */ ++ __raw_writel(0, rng_base + RNG_CTRL); ++ /* unregister driver */ ++ hwrng_unregister(&bcm2708_rng_ops); ++ iounmap(rng_base); ++} ++ ++module_init(bcm2708_rng_init); ++module_exit(bcm2708_rng_exit); ++ ++MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver"); ++MODULE_LICENSE("GPL and additional rights"); + +From 0a983bacf33d058f1ab3c05daf9d384b82b320aa Mon Sep 17 00:00:00 2001 +From: Aron Szabo +Date: Sat, 16 Jun 2012 12:15:55 +0200 +Subject: [PATCH 014/104] lirc: added support for RaspberryPi GPIO + +lirc_rpi: Use read_current_timer to determine transmitter delay. Thanks to jjmz and others +See: https://github.com/raspberrypi/linux/issues/525 + +lirc: Remove restriction on gpio pins that can be used with lirc + +Compute Module, for example could use different pins + +lirc_rpi: Add parameter to specify input pin pull + +Depending on the connected IR circuitry it might be desirable to change the +gpios internal pull from it pull-down default behaviour. Add a module +parameter to allow the user to set it explicitly. + +Signed-off-by: Julian Scheel + +lirc-rpi: Use the higher-level irq control functions + +This module used to access the irq_chip methods of the +gpio controller directly, rather than going through the +standard enable_irq/irq_set_irq_type functions. This +caused problems on pinctrl-bcm2835 which only implements +the irq_enable/disable methods and not irq_unmask/mask. + +lirc-rpi: Correct the interrupt usage + +1) Correct the use of enable_irq (i.e. don't call it so often) +2) Correct the shutdown sequence. +3) Avoid a bcm2708_gpio driver quirk by setting the irq flags earlier + +lirc-rpi: use getnstimeofday instead of read_current_timer + +read_current_timer isn't guaranteed to return values in +microseconds, and indeed it doesn't on a Pi2. + +Issue: linux#827 +--- + drivers/staging/media/lirc/Kconfig | 6 + + drivers/staging/media/lirc/Makefile | 1 + + drivers/staging/media/lirc/lirc_rpi.c | 667 ++++++++++++++++++++++++++++++++++ + 3 files changed, 674 insertions(+) + create mode 100644 drivers/staging/media/lirc/lirc_rpi.c + +diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig +index 6879c46..77c69f2 100644 +--- a/drivers/staging/media/lirc/Kconfig ++++ b/drivers/staging/media/lirc/Kconfig +@@ -32,6 +32,12 @@ config LIRC_PARALLEL + help + Driver for Homebrew Parallel Port Receivers + ++config LIRC_RPI ++ tristate "Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi" ++ depends on LIRC ++ help ++ Driver for Homebrew GPIO Port Receiver/Transmitter for the RaspberryPi ++ + config LIRC_SASEM + tristate "Sasem USB IR Remote" + depends on LIRC && USB +diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile +index 5430adf..9e53cd0 100644 +--- a/drivers/staging/media/lirc/Makefile ++++ b/drivers/staging/media/lirc/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o + obj-$(CONFIG_LIRC_IMON) += lirc_imon.o + obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o ++obj-$(CONFIG_LIRC_RPI) += lirc_rpi.o + obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o + obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o + obj-$(CONFIG_LIRC_SIR) += lirc_sir.o +diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c +new file mode 100644 +index 0000000..8aa452d +--- /dev/null ++++ b/drivers/staging/media/lirc/lirc_rpi.c +@@ -0,0 +1,667 @@ ++/* ++ * lirc_rpi.c ++ * ++ * lirc_rpi - Device driver that records pulse- and pause-lengths ++ * (space-lengths) (just like the lirc_serial driver does) ++ * between GPIO interrupt events on the Raspberry Pi. ++ * Lots of code has been taken from the lirc_serial module, ++ * so I would like say thanks to the authors. ++ * ++ * Copyright (C) 2012 Aron Robert Szabo , ++ * Michael Bishop ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define LIRC_DRIVER_NAME "lirc_rpi" ++#define RBUF_LEN 256 ++#define LIRC_TRANSMITTER_LATENCY 50 ++ ++#ifndef MAX_UDELAY_MS ++#define MAX_UDELAY_US 5000 ++#else ++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) ++#endif ++ ++#define dprintk(fmt, args...) \ ++ do { \ ++ if (debug) \ ++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ ++ fmt, ## args); \ ++ } while (0) ++ ++/* module parameters */ ++ ++/* set the default GPIO input pin */ ++static int gpio_in_pin = 18; ++/* set the default pull behaviour for input pin */ ++static int gpio_in_pull = BCM2708_PULL_DOWN; ++/* set the default GPIO output pin */ ++static int gpio_out_pin = 17; ++/* enable debugging messages */ ++static bool debug; ++/* -1 = auto, 0 = active high, 1 = active low */ ++static int sense = -1; ++/* use softcarrier by default */ ++static bool softcarrier = 1; ++/* 0 = do not invert output, 1 = invert output */ ++static bool invert = 0; ++ ++struct gpio_chip *gpiochip; ++static int irq_num; ++ ++/* forward declarations */ ++static long send_pulse(unsigned long length); ++static void send_space(long length); ++static void lirc_rpi_exit(void); ++ ++static struct platform_device *lirc_rpi_dev; ++static struct timeval lasttv = { 0, 0 }; ++static struct lirc_buffer rbuf; ++static spinlock_t lock; ++ ++/* initialized/set in init_timing_params() */ ++static unsigned int freq = 38000; ++static unsigned int duty_cycle = 50; ++static unsigned long period; ++static unsigned long pulse_width; ++static unsigned long space_width; ++ ++static void safe_udelay(unsigned long usecs) ++{ ++ while (usecs > MAX_UDELAY_US) { ++ udelay(MAX_UDELAY_US); ++ usecs -= MAX_UDELAY_US; ++ } ++ udelay(usecs); ++} ++ ++static unsigned long read_current_us(void) ++{ ++ struct timespec now; ++ getnstimeofday(&now); ++ return (now.tv_sec * 1000000) + (now.tv_nsec/1000); ++} ++ ++static int init_timing_params(unsigned int new_duty_cycle, ++ unsigned int new_freq) ++{ ++ if (1000 * 1000000L / new_freq * new_duty_cycle / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ if (1000 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= ++ LIRC_TRANSMITTER_LATENCY) ++ return -EINVAL; ++ duty_cycle = new_duty_cycle; ++ freq = new_freq; ++ period = 1000 * 1000000L / freq; ++ pulse_width = period * duty_cycle / 100; ++ space_width = period - pulse_width; ++ dprintk("in init_timing_params, freq=%d pulse=%ld, " ++ "space=%ld\n", freq, pulse_width, space_width); ++ return 0; ++} ++ ++static long send_pulse_softcarrier(unsigned long length) ++{ ++ int flag; ++ unsigned long actual, target; ++ unsigned long actual_us, initial_us, target_us; ++ ++ length *= 1000; ++ ++ actual = 0; target = 0; flag = 0; ++ actual_us = read_current_us(); ++ ++ while (actual < length) { ++ if (flag) { ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ target += space_width; ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ target += pulse_width; ++ } ++ initial_us = actual_us; ++ target_us = actual_us + (target - actual) / 1000; ++ /* ++ * Note - we've checked in ioctl that the pulse/space ++ * widths are big enough so that d is > 0 ++ */ ++ if ((int)(target_us - actual_us) > 0) ++ udelay(target_us - actual_us); ++ actual_us = read_current_us(); ++ actual += (actual_us - initial_us) * 1000; ++ flag = !flag; ++ } ++ return (actual-length) / 1000; ++} ++ ++static long send_pulse(unsigned long length) ++{ ++ if (length <= 0) ++ return 0; ++ ++ if (softcarrier) { ++ return send_pulse_softcarrier(length); ++ } else { ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); ++ safe_udelay(length); ++ return 0; ++ } ++} ++ ++static void send_space(long length) ++{ ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ if (length <= 0) ++ return; ++ safe_udelay(length); ++} ++ ++static void rbwrite(int l) ++{ ++ if (lirc_buffer_full(&rbuf)) { ++ /* no new signals will be accepted */ ++ dprintk("Buffer overrun\n"); ++ return; ++ } ++ lirc_buffer_write(&rbuf, (void *)&l); ++} ++ ++static void frbwrite(int l) ++{ ++ /* simple noise filter */ ++ static int pulse, space; ++ static unsigned int ptr; ++ ++ if (ptr > 0 && (l & PULSE_BIT)) { ++ pulse += l & PULSE_MASK; ++ if (pulse > 250) { ++ rbwrite(space); ++ rbwrite(pulse | PULSE_BIT); ++ ptr = 0; ++ pulse = 0; ++ } ++ return; ++ } ++ if (!(l & PULSE_BIT)) { ++ if (ptr == 0) { ++ if (l > 20000) { ++ space = l; ++ ptr++; ++ return; ++ } ++ } else { ++ if (l > 20000) { ++ space += pulse; ++ if (space > PULSE_MASK) ++ space = PULSE_MASK; ++ space += l; ++ if (space > PULSE_MASK) ++ space = PULSE_MASK; ++ pulse = 0; ++ return; ++ } ++ rbwrite(space); ++ rbwrite(pulse | PULSE_BIT); ++ ptr = 0; ++ pulse = 0; ++ } ++ } ++ rbwrite(l); ++} ++ ++static irqreturn_t irq_handler(int i, void *blah, struct pt_regs *regs) ++{ ++ struct timeval tv; ++ long deltv; ++ int data; ++ int signal; ++ ++ /* use the GPIO signal level */ ++ signal = gpiochip->get(gpiochip, gpio_in_pin); ++ ++ if (sense != -1) { ++ /* get current time */ ++ do_gettimeofday(&tv); ++ ++ /* calc time since last interrupt in microseconds */ ++ deltv = tv.tv_sec-lasttv.tv_sec; ++ if (tv.tv_sec < lasttv.tv_sec || ++ (tv.tv_sec == lasttv.tv_sec && ++ tv.tv_usec < lasttv.tv_usec)) { ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": AIEEEE: your clock just jumped backwards\n"); ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": %d %d %lx %lx %lx %lx\n", signal, sense, ++ tv.tv_sec, lasttv.tv_sec, ++ tv.tv_usec, lasttv.tv_usec); ++ data = PULSE_MASK; ++ } else if (deltv > 15) { ++ data = PULSE_MASK; /* really long time */ ++ if (!(signal^sense)) { ++ /* sanity check */ ++ printk(KERN_WARNING LIRC_DRIVER_NAME ++ ": AIEEEE: %d %d %lx %lx %lx %lx\n", ++ signal, sense, tv.tv_sec, lasttv.tv_sec, ++ tv.tv_usec, lasttv.tv_usec); ++ /* ++ * detecting pulse while this ++ * MUST be a space! ++ */ ++ sense = sense ? 0 : 1; ++ } ++ } else { ++ data = (int) (deltv*1000000 + ++ (tv.tv_usec - lasttv.tv_usec)); ++ } ++ frbwrite(signal^sense ? data : (data|PULSE_BIT)); ++ lasttv = tv; ++ wake_up_interruptible(&rbuf.wait_poll); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int is_right_chip(struct gpio_chip *chip, void *data) ++{ ++ dprintk("is_right_chip %s %d\n", chip->label, strcmp(data, chip->label)); ++ ++ if (strcmp(data, chip->label) == 0) ++ return 1; ++ return 0; ++} ++ ++static int init_port(void) ++{ ++ int i, nlow, nhigh, ret; ++ ++ gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip); ++ ++ if (!gpiochip) ++ return -ENODEV; ++ ++ if (gpio_request(gpio_out_pin, LIRC_DRIVER_NAME " ir/out")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_out_pin); ++ ret = -ENODEV; ++ goto exit_init_port; ++ } ++ ++ if (gpio_request(gpio_in_pin, LIRC_DRIVER_NAME " ir/in")) { ++ printk(KERN_ALERT LIRC_DRIVER_NAME ++ ": cant claim gpio pin %d\n", gpio_in_pin); ++ ret = -ENODEV; ++ goto exit_gpio_free_out_pin; ++ } ++ ++ bcm2708_gpio_setpull(gpiochip, gpio_in_pin, gpio_in_pull); ++ gpiochip->direction_input(gpiochip, gpio_in_pin); ++ gpiochip->direction_output(gpiochip, gpio_out_pin, 1); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); ++ dprintk("to_irq %d\n", irq_num); ++ ++ /* if pin is high, then this must be an active low receiver. */ ++ if (sense == -1) { ++ /* wait 1/2 sec for the power supply */ ++ msleep(500); ++ ++ /* ++ * probe 9 times every 0.04s, collect "votes" for ++ * active high/low ++ */ ++ nlow = 0; ++ nhigh = 0; ++ for (i = 0; i < 9; i++) { ++ if (gpiochip->get(gpiochip, gpio_in_pin)) ++ nlow++; ++ else ++ nhigh++; ++ msleep(40); ++ } ++ sense = (nlow >= nhigh ? 1 : 0); ++ printk(KERN_INFO LIRC_DRIVER_NAME ++ ": auto-detected active %s receiver on GPIO pin %d\n", ++ sense ? "low" : "high", gpio_in_pin); ++ } else { ++ printk(KERN_INFO LIRC_DRIVER_NAME ++ ": manually using active %s receiver on GPIO pin %d\n", ++ sense ? "low" : "high", gpio_in_pin); ++ } ++ ++ return 0; ++ ++ exit_gpio_free_out_pin: ++ gpio_free(gpio_out_pin); ++ ++ exit_init_port: ++ return ret; ++} ++ ++// called when the character device is opened ++static int set_use_inc(void *data) ++{ ++ int result; ++ ++ /* initialize timestamp */ ++ do_gettimeofday(&lasttv); ++ ++ result = request_irq(irq_num, ++ (irq_handler_t) irq_handler, ++ IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, ++ LIRC_DRIVER_NAME, (void*) 0); ++ ++ switch (result) { ++ case -EBUSY: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": IRQ %d is busy\n", ++ irq_num); ++ return -EBUSY; ++ case -EINVAL: ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": Bad irq number or handler\n"); ++ return -EINVAL; ++ default: ++ dprintk("Interrupt %d obtained\n", ++ irq_num); ++ break; ++ }; ++ ++ /* initialize pulse/space widths */ ++ init_timing_params(duty_cycle, freq); ++ ++ return 0; ++} ++ ++static void set_use_dec(void *data) ++{ ++ /* GPIO Pin Falling/Rising Edge Detect Disable */ ++ irq_set_irq_type(irq_num, 0); ++ disable_irq(irq_num); ++ ++ free_irq(irq_num, (void *) 0); ++ ++ dprintk(KERN_INFO LIRC_DRIVER_NAME ++ ": freed IRQ %d\n", irq_num); ++} ++ ++static ssize_t lirc_write(struct file *file, const char *buf, ++ size_t n, loff_t *ppos) ++{ ++ int i, count; ++ unsigned long flags; ++ long delta = 0; ++ int *wbuf; ++ ++ count = n / sizeof(int); ++ if (n % sizeof(int) || count % 2 == 0) ++ return -EINVAL; ++ wbuf = memdup_user(buf, n); ++ if (IS_ERR(wbuf)) ++ return PTR_ERR(wbuf); ++ spin_lock_irqsave(&lock, flags); ++ ++ for (i = 0; i < count; i++) { ++ if (i%2) ++ send_space(wbuf[i] - delta); ++ else ++ delta = send_pulse(wbuf[i]); ++ } ++ gpiochip->set(gpiochip, gpio_out_pin, invert); ++ ++ spin_unlock_irqrestore(&lock, flags); ++ kfree(wbuf); ++ return n; ++} ++ ++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) ++{ ++ int result; ++ __u32 value; ++ ++ switch (cmd) { ++ case LIRC_GET_SEND_MODE: ++ return -ENOIOCTLCMD; ++ break; ++ ++ case LIRC_SET_SEND_MODE: ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ /* only LIRC_MODE_PULSE supported */ ++ if (value != LIRC_MODE_PULSE) ++ return -ENOSYS; ++ break; ++ ++ case LIRC_GET_LENGTH: ++ return -ENOSYS; ++ break; ++ ++ case LIRC_SET_SEND_DUTY_CYCLE: ++ dprintk("SET_SEND_DUTY_CYCLE\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value <= 0 || value > 100) ++ return -EINVAL; ++ return init_timing_params(value, freq); ++ break; ++ ++ case LIRC_SET_SEND_CARRIER: ++ dprintk("SET_SEND_CARRIER\n"); ++ result = get_user(value, (__u32 *) arg); ++ if (result) ++ return result; ++ if (value > 500000 || value < 20000) ++ return -EINVAL; ++ return init_timing_params(duty_cycle, value); ++ break; ++ ++ default: ++ return lirc_dev_fop_ioctl(filep, cmd, arg); ++ } ++ return 0; ++} ++ ++static const struct file_operations lirc_fops = { ++ .owner = THIS_MODULE, ++ .write = lirc_write, ++ .unlocked_ioctl = lirc_ioctl, ++ .read = lirc_dev_fop_read, ++ .poll = lirc_dev_fop_poll, ++ .open = lirc_dev_fop_open, ++ .release = lirc_dev_fop_close, ++ .llseek = no_llseek, ++}; ++ ++static struct lirc_driver driver = { ++ .name = LIRC_DRIVER_NAME, ++ .minor = -1, ++ .code_length = 1, ++ .sample_rate = 0, ++ .data = NULL, ++ .add_to_buf = NULL, ++ .rbuf = &rbuf, ++ .set_use_inc = set_use_inc, ++ .set_use_dec = set_use_dec, ++ .fops = &lirc_fops, ++ .dev = NULL, ++ .owner = THIS_MODULE, ++}; ++ ++static struct platform_driver lirc_rpi_driver = { ++ .driver = { ++ .name = LIRC_DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init lirc_rpi_init(void) ++{ ++ int result; ++ ++ /* Init read buffer. */ ++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); ++ if (result < 0) ++ return -ENOMEM; ++ ++ result = platform_driver_register(&lirc_rpi_driver); ++ if (result) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": lirc register returned %d\n", result); ++ goto exit_buffer_free; ++ } ++ ++ lirc_rpi_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); ++ if (!lirc_rpi_dev) { ++ result = -ENOMEM; ++ goto exit_driver_unregister; ++ } ++ ++ result = platform_device_add(lirc_rpi_dev); ++ if (result) ++ goto exit_device_put; ++ ++ return 0; ++ ++ exit_device_put: ++ platform_device_put(lirc_rpi_dev); ++ ++ exit_driver_unregister: ++ platform_driver_unregister(&lirc_rpi_driver); ++ ++ exit_buffer_free: ++ lirc_buffer_free(&rbuf); ++ ++ return result; ++} ++ ++static void lirc_rpi_exit(void) ++{ ++ if (!lirc_rpi_dev->dev.of_node) ++ platform_device_unregister(lirc_rpi_dev); ++ platform_driver_unregister(&lirc_rpi_driver); ++ lirc_buffer_free(&rbuf); ++} ++ ++static int __init lirc_rpi_init_module(void) ++{ ++ int result; ++ ++ result = lirc_rpi_init(); ++ if (result) ++ return result; ++ ++ if (gpio_in_pin >= BCM2708_NR_GPIOS || gpio_out_pin >= BCM2708_NR_GPIOS) { ++ result = -EINVAL; ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": invalid GPIO pin(s) specified!\n"); ++ goto exit_rpi; ++ } ++ ++ result = init_port(); ++ if (result < 0) ++ goto exit_rpi; ++ ++ driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | ++ LIRC_CAN_SET_SEND_CARRIER | ++ LIRC_CAN_SEND_PULSE | ++ LIRC_CAN_REC_MODE2; ++ ++ driver.dev = &lirc_rpi_dev->dev; ++ driver.minor = lirc_register_driver(&driver); ++ ++ if (driver.minor < 0) { ++ printk(KERN_ERR LIRC_DRIVER_NAME ++ ": device registration failed with %d\n", result); ++ result = -EIO; ++ goto exit_rpi; ++ } ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); ++ ++ return 0; ++ ++ exit_rpi: ++ lirc_rpi_exit(); ++ ++ return result; ++} ++ ++static void __exit lirc_rpi_exit_module(void) ++{ ++ lirc_unregister_driver(driver.minor); ++ ++ gpio_free(gpio_out_pin); ++ gpio_free(gpio_in_pin); ++ ++ lirc_rpi_exit(); ++ ++ printk(KERN_INFO LIRC_DRIVER_NAME ": cleaned up module\n"); ++} ++ ++module_init(lirc_rpi_init_module); ++module_exit(lirc_rpi_exit_module); ++ ++MODULE_DESCRIPTION("Infra-red receiver and blaster driver for Raspberry Pi GPIO."); ++MODULE_AUTHOR("Aron Robert Szabo "); ++MODULE_AUTHOR("Michael Bishop "); ++MODULE_LICENSE("GPL"); ++ ++module_param(gpio_out_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_out_pin, "GPIO output/transmitter pin number of the BCM" ++ " processor. (default 17"); ++ ++module_param(gpio_in_pin, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pin, "GPIO input pin number of the BCM processor." ++ " (default 18"); ++ ++module_param(gpio_in_pull, int, S_IRUGO); ++MODULE_PARM_DESC(gpio_in_pull, "GPIO input pin pull configuration." ++ " (0 = off, 1 = up, 2 = down, default down)"); ++ ++module_param(sense, int, S_IRUGO); ++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" ++ " (0 = active high, 1 = active low )"); ++ ++module_param(softcarrier, bool, S_IRUGO); ++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); ++ ++module_param(invert, bool, S_IRUGO); ++MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); ++ ++module_param(debug, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(debug, "Enable debugging messages"); + +From 336b54eb6d0d48c2a57b7eabcbf73e54ee2f1c9f Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 3 Jul 2013 00:49:20 +0100 +Subject: [PATCH 015/104] Add cpufreq driver + +--- + arch/arm/Kconfig | 1 + + drivers/cpufreq/Kconfig.arm | 8 ++ + drivers/cpufreq/Makefile | 1 + + drivers/cpufreq/bcm2835-cpufreq.c | 224 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 234 insertions(+) + create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index ee1bb5e..9fcd395 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -378,6 +378,7 @@ config ARCH_BCM2708 + select NEED_MACH_GPIO_H + select NEED_MACH_MEMORY_H + select CLKDEV_LOOKUP ++ select ARCH_HAS_CPUFREQ + select GENERIC_CLOCKEVENTS + select ARM_ERRATA_411920 + select MACH_BCM2708 +diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm +index 1b06fc4..0b90de8 100644 +--- a/drivers/cpufreq/Kconfig.arm ++++ b/drivers/cpufreq/Kconfig.arm +@@ -249,6 +249,14 @@ config ARM_SPEAR_CPUFREQ + help + This adds the CPUFreq driver support for SPEAr SOCs. + ++config ARM_BCM2835_CPUFREQ ++ bool "BCM2835 Driver" ++ default y ++ help ++ This adds the CPUFreq driver for BCM2835 ++ ++ If in doubt, say N. ++ + config ARM_TEGRA_CPUFREQ + bool "TEGRA CPUFreq support" + depends on ARCH_TEGRA +diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile +index 82a1821..50b1954 100644 +--- a/drivers/cpufreq/Makefile ++++ b/drivers/cpufreq/Makefile +@@ -76,6 +76,7 @@ obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o + obj-$(CONFIG_ARM_SA1100_CPUFREQ) += sa1100-cpufreq.o + obj-$(CONFIG_ARM_SA1110_CPUFREQ) += sa1110-cpufreq.o + obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o ++obj-$(CONFIG_ARM_BCM2835_CPUFREQ) += bcm2835-cpufreq.o + obj-$(CONFIG_ARM_TEGRA_CPUFREQ) += tegra-cpufreq.o + obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o + +diff --git a/drivers/cpufreq/bcm2835-cpufreq.c b/drivers/cpufreq/bcm2835-cpufreq.c +new file mode 100755 +index 0000000..447ca09 +--- /dev/null ++++ b/drivers/cpufreq/bcm2835-cpufreq.c +@@ -0,0 +1,224 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++/***************************************************************************** ++* FILENAME: bcm2835-cpufreq.h ++* DESCRIPTION: This driver dynamically manages the CPU Frequency of the ARM ++* processor. Messages are sent to Videocore either setting or requesting the ++* frequency of the ARM in order to match an appropiate frequency to the current ++* usage of the processor. The policy which selects the frequency to use is ++* defined in the kernel .config file, but can be changed during runtime. ++*****************************************************************************/ ++ ++/* ---------- INCLUDES ---------- */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ---------- DEFINES ---------- */ ++/*#define CPUFREQ_DEBUG_ENABLE*/ /* enable debugging */ ++#define MODULE_NAME "bcm2835-cpufreq" ++ ++#define VCMSG_ID_ARM_CLOCK 0x000000003 /* Clock/Voltage ID's */ ++ ++/* debug printk macros */ ++#ifdef CPUFREQ_DEBUG_ENABLE ++#define print_debug(fmt,...) pr_debug("%s:%s:%d: "fmt, MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) pr_err("%s:%s:%d: "fmt, MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++#define print_info(fmt,...) pr_info("%s: "fmt, MODULE_NAME, ##__VA_ARGS__) ++ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the message id */ ++ uint32_t buffer_size; /* size of the buffer (which in this case is always 8 bytes) */ ++ uint32_t data_size; /* amount of data being sent or received */ ++ uint32_t dev_id; /* the ID of the clock/voltage to get or set */ ++ uint32_t val; /* the value (e.g. rate (in Hz)) to set */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++/* ---------- GLOBALS ---------- */ ++static struct cpufreq_driver bcm2835_cpufreq_driver; /* the cpufreq driver global */ ++ ++static struct cpufreq_frequency_table bcm2835_freq_table[] = { ++ {0, 0, 0}, ++ {0, 0, 0}, ++ {0, 0, CPUFREQ_TABLE_END}, ++}; ++ ++/* ++ =============================================== ++ clk_rate either gets or sets the clock rates. ++ =============================================== ++*/ ++static uint32_t bcm2835_cpufreq_set_clock(int cur_rate, int arm_rate) ++{ ++ int s, actual_rate=0; ++ struct vc_msg msg; ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ msg.msg_size = sizeof msg; ++ ++ msg.tag.tag_id = VCMSG_SET_CLOCK_RATE; ++ msg.tag.buffer_size = 8; ++ msg.tag.data_size = 8; /* we're sending the clock ID and the new rates which is a total of 2 words */ ++ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; ++ msg.tag.val = arm_rate * 1000; ++ ++ /* send the message */ ++ s = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in KHz */ ++ if (s == 0 && (msg.request_code & 0x80000000)) ++ actual_rate = msg.tag.val/1000; ++ ++ print_debug("Setting new frequency = %d -> %d (actual %d)\n", cur_rate, arm_rate, actual_rate); ++ return actual_rate; ++} ++ ++static uint32_t bcm2835_cpufreq_get_clock(int tag) ++{ ++ int s; ++ int arm_rate = 0; ++ struct vc_msg msg; ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ msg.msg_size = sizeof msg; ++ msg.tag.tag_id = tag; ++ msg.tag.buffer_size = 8; ++ msg.tag.data_size = 4; /* we're just sending the clock ID which is one word long */ ++ msg.tag.dev_id = VCMSG_ID_ARM_CLOCK; ++ ++ /* send the message */ ++ s = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in KHz */ ++ if (s == 0 && (msg.request_code & 0x80000000)) ++ arm_rate = msg.tag.val/1000; ++ ++ print_debug("%s frequency = %d\n", ++ tag == VCMSG_GET_CLOCK_RATE ? "Current": ++ tag == VCMSG_GET_MIN_CLOCK ? "Min": ++ tag == VCMSG_GET_MAX_CLOCK ? "Max": ++ "Unexpected", arm_rate); ++ ++ return arm_rate; ++} ++ ++/* ++ ==================================================== ++ Module Initialisation registers the cpufreq driver ++ ==================================================== ++*/ ++static int __init bcm2835_cpufreq_module_init(void) ++{ ++ print_debug("IN\n"); ++ return cpufreq_register_driver(&bcm2835_cpufreq_driver); ++} ++ ++/* ++ ============= ++ Module exit ++ ============= ++*/ ++static void __exit bcm2835_cpufreq_module_exit(void) ++{ ++ print_debug("IN\n"); ++ cpufreq_unregister_driver(&bcm2835_cpufreq_driver); ++ return; ++} ++ ++/* ++ ============================================================== ++ Initialisation function sets up the CPU policy for first use ++ ============================================================== ++*/ ++static int bcm2835_cpufreq_driver_init(struct cpufreq_policy *policy) ++{ ++ /* measured value of how long it takes to change frequency */ ++ const unsigned int transition_latency = 355000; /* ns */ ++ ++ /* now find out what the maximum and minimum frequencies are */ ++ bcm2835_freq_table[0].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MIN_CLOCK); ++ bcm2835_freq_table[1].frequency = bcm2835_cpufreq_get_clock(VCMSG_GET_MAX_CLOCK); ++ ++ print_info("min=%d max=%d\n", bcm2835_freq_table[0].frequency, bcm2835_freq_table[1].frequency); ++ return cpufreq_generic_init(policy, bcm2835_freq_table, transition_latency); ++} ++ ++/* ++ ===================================================================== ++ Target index function chooses the requested frequency from the table ++ ===================================================================== ++*/ ++ ++static int bcm2835_cpufreq_driver_target_index(struct cpufreq_policy *policy, unsigned int state) ++{ ++ unsigned int target_freq = bcm2835_freq_table[state].frequency; ++ unsigned int cur = bcm2835_cpufreq_set_clock(policy->cur, target_freq); ++ ++ if (!cur) ++ { ++ print_err("Error occurred setting a new frequency (%d)\n", target_freq); ++ return -EINVAL; ++ } ++ print_debug("%s: %i: freq %d->%d\n", policy->governor->name, state, policy->cur, cur); ++ return 0; ++} ++ ++/* ++ ====================================================== ++ Get function returns the current frequency from table ++ ====================================================== ++*/ ++ ++static unsigned int bcm2835_cpufreq_driver_get(unsigned int cpu) ++{ ++ unsigned int actual_rate = bcm2835_cpufreq_get_clock(VCMSG_GET_CLOCK_RATE); ++ print_debug("%d: freq=%d\n", cpu, actual_rate); ++ return actual_rate <= bcm2835_freq_table[0].frequency ? bcm2835_freq_table[0].frequency : bcm2835_freq_table[1].frequency; ++} ++ ++/* the CPUFreq driver */ ++static struct cpufreq_driver bcm2835_cpufreq_driver = { ++ .name = "BCM2835 CPUFreq", ++ .init = bcm2835_cpufreq_driver_init, ++ .verify = cpufreq_generic_frequency_table_verify, ++ .target_index = bcm2835_cpufreq_driver_target_index, ++ .get = bcm2835_cpufreq_driver_get, ++ .attr = cpufreq_generic_attr, ++}; ++ ++MODULE_AUTHOR("Dorian Peake and Dom Cobley"); ++MODULE_DESCRIPTION("CPU frequency driver for BCM2835 chip"); ++MODULE_LICENSE("GPL"); ++ ++module_init(bcm2835_cpufreq_module_init); ++module_exit(bcm2835_cpufreq_module_exit); + +From 542e09877537f6c05253e0aa86cd8842984a007d Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 19:24:24 +0000 +Subject: [PATCH 016/104] Added hwmon/thermal driver for reporting core + temperature. Thanks Dorian + +--- + arch/arm/mach-bcm2708/bcm2708.c | 11 ++ + drivers/hwmon/Kconfig | 10 ++ + drivers/hwmon/Makefile | 1 + + drivers/hwmon/bcm2835-hwmon.c | 219 ++++++++++++++++++++++++++++++++++++++ + drivers/thermal/Kconfig | 6 ++ + drivers/thermal/Makefile | 1 + + drivers/thermal/bcm2835-thermal.c | 184 ++++++++++++++++++++++++++++++++ + 7 files changed, 432 insertions(+) + create mode 100644 drivers/hwmon/bcm2835-hwmon.c + create mode 100644 drivers/thermal/bcm2835-thermal.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index f54e3e9b..bae8ba5 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -499,6 +499,14 @@ static struct platform_device bcm2708_alsa_devices[] = { + }, + }; + ++static struct platform_device bcm2835_hwmon_device = { ++ .name = "bcm2835_hwmon", ++}; ++ ++static struct platform_device bcm2835_thermal_device = { ++ .name = "bcm2835_thermal", ++}; ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -611,6 +619,9 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + ++ bcm_register_device(&bcm2835_hwmon_device); ++ bcm_register_device(&bcm2835_thermal_device); ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 110fade..8c4474d 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -1703,6 +1703,16 @@ config SENSORS_ULTRA45 + This driver provides support for the Ultra45 workstation environmental + sensors. + ++config SENSORS_BCM2835 ++ depends on THERMAL_BCM2835=n ++ tristate "Broadcom BCM2835 HWMON Driver" ++ help ++ If you say yes here you get support for the hardware ++ monitoring features of the BCM2835 Chip ++ ++ This driver can also be built as a module. If so, the module ++ will be called bcm2835-hwmon. ++ + if ACPI + + comment "ACPI drivers" +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index 6c94147..e7c60cb 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o + obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o + obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o + obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o ++obj-$(CONFIG_SENSORS_BCM2835) += bcm2835-hwmon.o + + obj-$(CONFIG_PMBUS) += pmbus/ + +diff --git a/drivers/hwmon/bcm2835-hwmon.c b/drivers/hwmon/bcm2835-hwmon.c +new file mode 100644 +index 0000000..5bbed45 +--- /dev/null ++++ b/drivers/hwmon/bcm2835-hwmon.c +@@ -0,0 +1,219 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MODULE_NAME "bcm2835_hwmon" ++ ++/*#define HWMON_DEBUG_ENABLE*/ ++ ++#ifdef HWMON_DEBUG_ENABLE ++#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__) ++ ++#define VC_TAG_GET_TEMP 0x00030006 ++#define VC_TAG_GET_MAX_TEMP 0x0003000A ++ ++/* --- STRUCTS --- */ ++struct bcm2835_hwmon_data { ++ struct device *hwmon_dev; ++}; ++ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the tag ID for the temperature */ ++ uint32_t buffer_size; /* size of the buffer (should be 8) */ ++ uint32_t request_code; /* identifies message as a request (should be 0) */ ++ uint32_t id; /* extra ID field (should be 0) */ ++ uint32_t val; /* returned value of the temperature */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++typedef enum { ++ TEMP, ++ MAX_TEMP, ++} temp_type; ++ ++/* --- PROTOTYPES --- */ ++static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf); ++static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf); ++ ++/* --- GLOBALS --- */ ++ ++static struct bcm2835_hwmon_data *bcm2835_data; ++static struct platform_driver bcm2835_hwmon_driver; ++ ++static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0); ++static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP); ++static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP); ++ ++static struct attribute* bcm2835_attributes[] = { ++ &sensor_dev_attr_name.dev_attr.attr, ++ &sensor_dev_attr_temp1_input.dev_attr.attr, ++ &sensor_dev_attr_temp1_max.dev_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group bcm2835_attr_group = { ++ .attrs = bcm2835_attributes, ++}; ++ ++/* --- FUNCTIONS --- */ ++ ++static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ return sprintf(buf,"bcm2835_hwmon\n"); ++} ++ ++static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf) ++{ ++ struct vc_msg msg; ++ int result; ++ uint temp = 0; ++ int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index; ++ ++ print_debug("IN"); ++ ++ /* wipe all previous message data */ ++ memset(&msg, 0, sizeof msg); ++ ++ /* determine the message type */ ++ if(index == TEMP) ++ msg.tag.tag_id = VC_TAG_GET_TEMP; ++ else if (index == MAX_TEMP) ++ msg.tag.tag_id = VC_TAG_GET_MAX_TEMP; ++ else ++ { ++ print_debug("Unknown temperature message!"); ++ return -EINVAL; ++ } ++ ++ msg.msg_size = sizeof msg; ++ msg.tag.buffer_size = 8; ++ ++ /* send the message */ ++ result = bcm_mailbox_property(&msg, sizeof msg); ++ ++ /* check if it was all ok and return the rate in milli degrees C */ ++ if (result == 0 && (msg.request_code & 0x80000000)) ++ temp = (uint)msg.tag.val; ++ #ifdef HWMON_DEBUG_ENABLE ++ else ++ print_debug("Failed to get temperature!"); ++ #endif ++ print_debug("Got temperature as %u",temp); ++ print_debug("OUT"); ++ return sprintf(buf, "%u\n", temp); ++} ++ ++ ++static int bcm2835_hwmon_probe(struct platform_device *pdev) ++{ ++ int err; ++ ++ print_debug("IN"); ++ print_debug("HWMON Driver has been probed!"); ++ ++ /* check that the device isn't null!*/ ++ if(pdev == NULL) ++ { ++ print_debug("Platform device is empty!"); ++ return -ENODEV; ++ } ++ ++ /* allocate memory for neccessary data */ ++ bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL); ++ if(!bcm2835_data) ++ { ++ print_debug("Unable to allocate memory for hwmon data!"); ++ err = -ENOMEM; ++ goto kzalloc_error; ++ } ++ ++ /* create the sysfs files */ ++ if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group)) ++ { ++ print_debug("Unable to create sysfs files!"); ++ err = -EFAULT; ++ goto sysfs_error; ++ } ++ ++ /* register the hwmon device */ ++ bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev); ++ if (IS_ERR(bcm2835_data->hwmon_dev)) ++ { ++ err = PTR_ERR(bcm2835_data->hwmon_dev); ++ goto hwmon_error; ++ } ++ print_debug("OUT"); ++ return 0; ++ ++ /* error goto's */ ++ hwmon_error: ++ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); ++ ++ sysfs_error: ++ kfree(bcm2835_data); ++ ++ kzalloc_error: ++ ++ return err; ++ ++} ++ ++static int bcm2835_hwmon_remove(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ hwmon_device_unregister(bcm2835_data->hwmon_dev); ++ ++ sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group); ++ print_debug("OUT"); ++ return 0; ++} ++ ++/* Hwmon Driver */ ++static struct platform_driver bcm2835_hwmon_driver = { ++ .probe = bcm2835_hwmon_probe, ++ .remove = bcm2835_hwmon_remove, ++ .driver = { ++ .name = "bcm2835_hwmon", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dorian Peake"); ++MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip"); ++ ++module_platform_driver(bcm2835_hwmon_driver); +diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig +index af40db0..4584b1e 100644 +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -238,6 +238,12 @@ config INTEL_POWERCLAMP + enforce idle time which results in more package C-state residency. The + user interface is exposed via generic thermal framework. + ++config THERMAL_BCM2835 ++ tristate "BCM2835 Thermal Driver" ++ help ++ This will enable temperature monitoring for the Broadcom BCM2835 ++ chip. If built as a module, it will be called 'bcm2835-thermal'. ++ + config X86_PKG_TEMP_THERMAL + tristate "X86 package temperature thermal driver" + depends on X86_THERMAL_VECTOR +diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile +index fa0dc48..453d4d9 100644 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o + obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o + obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o + obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o ++obj-$(CONFIG_THERMAL_BCM2835) += bcm2835-thermal.o + obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o + obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o + obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ +diff --git a/drivers/thermal/bcm2835-thermal.c b/drivers/thermal/bcm2835-thermal.c +new file mode 100644 +index 0000000..85fceb5 +--- /dev/null ++++ b/drivers/thermal/bcm2835-thermal.c +@@ -0,0 +1,184 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* --- DEFINITIONS --- */ ++#define MODULE_NAME "bcm2835_thermal" ++ ++/*#define THERMAL_DEBUG_ENABLE*/ ++ ++#ifdef THERMAL_DEBUG_ENABLE ++#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__) ++#else ++#define print_debug(fmt,...) ++#endif ++#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__) ++ ++#define VC_TAG_GET_TEMP 0x00030006 ++#define VC_TAG_GET_MAX_TEMP 0x0003000A ++ ++typedef enum { ++ TEMP, ++ MAX_TEMP, ++} temp_type; ++ ++/* --- STRUCTS --- */ ++/* tag part of the message */ ++struct vc_msg_tag { ++ uint32_t tag_id; /* the tag ID for the temperature */ ++ uint32_t buffer_size; /* size of the buffer (should be 8) */ ++ uint32_t request_code; /* identifies message as a request (should be 0) */ ++ uint32_t id; /* extra ID field (should be 0) */ ++ uint32_t val; /* returned value of the temperature */ ++}; ++ ++/* message structure to be sent to videocore */ ++struct vc_msg { ++ uint32_t msg_size; /* simply, sizeof(struct vc_msg) */ ++ uint32_t request_code; /* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */ ++ struct vc_msg_tag tag; /* the tag structure above to make */ ++ uint32_t end_tag; /* an end identifier, should be set to NULL */ ++}; ++ ++struct bcm2835_thermal_data { ++ struct thermal_zone_device *thermal_dev; ++ struct vc_msg msg; ++}; ++ ++/* --- GLOBALS --- */ ++static struct bcm2835_thermal_data bcm2835_data; ++ ++/* Thermal Device Operations */ ++static struct thermal_zone_device_ops ops; ++ ++/* --- FUNCTIONS --- */ ++ ++static int bcm2835_get_temp_or_max(struct thermal_zone_device *thermal_dev, unsigned long *temp, unsigned tag_id) ++{ ++ int result = -1, retry = 3; ++ print_debug("IN"); ++ ++ *temp = 0; ++ while (result != 0 && retry-- > 0) { ++ /* wipe all previous message data */ ++ memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg); ++ ++ /* prepare message */ ++ bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg; ++ bcm2835_data.msg.tag.buffer_size = 8; ++ bcm2835_data.msg.tag.tag_id = tag_id; ++ ++ /* send the message */ ++ result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg); ++ print_debug("Got %stemperature as %u (%d,%x)\n", tag_id==VC_TAG_GET_MAX_TEMP ? "max ":"", (uint)bcm2835_data.msg.tag.val, result, bcm2835_data.msg.request_code); ++ if (!(bcm2835_data.msg.request_code & 0x80000000)) ++ result = -1; ++ } ++ ++ /* check if it was all ok and return the rate in milli degrees C */ ++ if (result == 0) ++ *temp = (uint)bcm2835_data.msg.tag.val; ++ else ++ print_err("Failed to get temperature! (%x:%d)\n", tag_id, result); ++ print_debug("OUT"); ++ return result; ++} ++ ++static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp) ++{ ++ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_TEMP); ++} ++ ++static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp) ++{ ++ return bcm2835_get_temp_or_max(thermal_dev, temp, VC_TAG_GET_MAX_TEMP); ++} ++ ++static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type) ++{ ++ *trip_type = THERMAL_TRIP_HOT; ++ return 0; ++} ++ ++ ++static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode) ++{ ++ *dev_mode = THERMAL_DEVICE_ENABLED; ++ return 0; ++} ++ ++ ++static int bcm2835_thermal_probe(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ print_debug("THERMAL Driver has been probed!"); ++ ++ /* check that the device isn't null!*/ ++ if(pdev == NULL) ++ { ++ print_debug("Platform device is empty!"); ++ return -ENODEV; ++ } ++ ++ if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal", 1, 0, NULL, &ops, NULL, 0, 0))) ++ { ++ print_debug("Unable to register the thermal device!"); ++ return -EFAULT; ++ } ++ return 0; ++} ++ ++ ++static int bcm2835_thermal_remove(struct platform_device *pdev) ++{ ++ print_debug("IN"); ++ ++ thermal_zone_device_unregister(bcm2835_data.thermal_dev); ++ ++ print_debug("OUT"); ++ ++ return 0; ++} ++ ++static struct thermal_zone_device_ops ops = { ++ .get_temp = bcm2835_get_temp, ++ .get_trip_temp = bcm2835_get_max_temp, ++ .get_trip_type = bcm2835_get_trip_type, ++ .get_mode = bcm2835_get_mode, ++}; ++ ++/* Thermal Driver */ ++static struct platform_driver bcm2835_thermal_driver = { ++ .probe = bcm2835_thermal_probe, ++ .remove = bcm2835_thermal_remove, ++ .driver = { ++ .name = "bcm2835_thermal", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dorian Peake"); ++MODULE_DESCRIPTION("Thermal driver for bcm2835 chip"); ++ ++module_platform_driver(bcm2835_thermal_driver); + +From 1665c59e341b2005edb3da64189cced3b29cde33 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 4 Nov 2013 18:56:10 +0000 +Subject: [PATCH 017/104] Add Chris Boot's i2c and spi drivers. + +i2c-bcm2708: fixed baudrate + +Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock). +In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits. +This resulted in incorrect setting of CDIV and higher baudrate than intended. +Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz +After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz +The correct baudrate is shown in the log after the cdiv > 0xffff correction. + +Perform I2C combined transactions when possible + +Perform I2C combined transactions whenever possible, within the +restrictions of the Broadcomm Serial Controller. + +Disable DONE interrupt during TA poll + +Prevent interrupt from being triggered if poll is missed and transfer +starts and finishes. + +i2c: Make combined transactions optional and disabled by default +--- + arch/arm/mach-bcm2708/Kconfig | 7 + + arch/arm/mach-bcm2708/bcm2708.c | 104 ++++++- + drivers/i2c/busses/Kconfig | 19 ++ + drivers/i2c/busses/Makefile | 2 + + drivers/i2c/busses/i2c-bcm2708.c | 449 ++++++++++++++++++++++++++++ + drivers/spi/Kconfig | 8 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-bcm2708.c | 626 +++++++++++++++++++++++++++++++++++++++ + 8 files changed, 1214 insertions(+), 2 deletions(-) + create mode 100644 drivers/i2c/busses/i2c-bcm2708.c + create mode 100644 drivers/spi/spi-bcm2708.c + +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index 9355841..e151ed4 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -31,4 +31,11 @@ config BCM2708_NOL2CACHE + help + Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. + ++config BCM2708_SPIDEV ++ bool "Bind spidev to SPI0 master" ++ depends on MACH_BCM2708 ++ depends on SPI ++ default y ++ help ++ Binds spidev driver to the SPI0 master + endmenu +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index bae8ba5..752dd46 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -205,7 +206,6 @@ static struct clk osc_clk = { + + /* warning - the USB needs a clock > 34MHz */ + +-#ifdef CONFIG_MMC_BCM2708 + static struct clk sdhost_clk = { + #ifdef CONFIG_ARCH_BCM2708_CHIPIT + .rate = 4000000, /* 4MHz */ +@@ -213,7 +213,6 @@ static struct clk sdhost_clk = { + .rate = 250000000, /* 250MHz */ + #endif + }; +-#endif + + static struct clk_lookup lookups[] = { + { /* UART0 */ +@@ -223,6 +222,15 @@ static struct clk_lookup lookups[] = { + { /* USB */ + .dev_id = "bcm2708_usb", + .clk = &osc_clk, ++ }, { /* SPI */ ++ .dev_id = "bcm2708_spi.0", ++ .clk = &sdhost_clk, ++ }, { /* BSC0 */ ++ .dev_id = "bcm2708_i2c.0", ++ .clk = &sdhost_clk, ++ }, { /* BSC1 */ ++ .dev_id = "bcm2708_i2c.1", ++ .clk = &sdhost_clk, + } + }; + +@@ -499,6 +507,89 @@ static struct platform_device bcm2708_alsa_devices[] = { + }, + }; + ++static struct resource bcm2708_spi_resources[] = { ++ { ++ .start = SPI0_BASE, ++ .end = SPI0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SPI, ++ .end = IRQ_SPI, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++static struct platform_device bcm2708_spi_device = { ++ .name = "bcm2708_spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), ++ .resource = bcm2708_spi_resources, ++ .dev = { ++ .dma_mask = &bcm2708_spi_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, ++}; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++static struct spi_board_info bcm2708_spi_devices[] = { ++#ifdef CONFIG_SPI_SPIDEV ++ { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 0, ++ .mode = SPI_MODE_0, ++ }, { ++ .modalias = "spidev", ++ .max_speed_hz = 500000, ++ .bus_num = 0, ++ .chip_select = 1, ++ .mode = SPI_MODE_0, ++ } ++#endif ++}; ++#endif ++ ++static struct resource bcm2708_bsc0_resources[] = { ++ { ++ .start = BSC0_BASE, ++ .end = BSC0_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc0_device = { ++ .name = "bcm2708_i2c", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), ++ .resource = bcm2708_bsc0_resources, ++}; ++ ++ ++static struct resource bcm2708_bsc1_resources[] = { ++ { ++ .start = BSC1_BASE, ++ .end = BSC1_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = INTERRUPT_I2C, ++ .end = INTERRUPT_I2C, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++static struct platform_device bcm2708_bsc1_device = { ++ .name = "bcm2708_i2c", ++ .id = 1, ++ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), ++ .resource = bcm2708_bsc1_resources, ++}; ++ + static struct platform_device bcm2835_hwmon_device = { + .name = "bcm2835_hwmon", + }; +@@ -619,6 +710,10 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + ++ bcm_register_device(&bcm2708_spi_device); ++ bcm_register_device(&bcm2708_bsc0_device); ++ bcm_register_device(&bcm2708_bsc1_device); ++ + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); + +@@ -628,6 +723,11 @@ void __init bcm2708_init(void) + } + system_rev = boardrev; + system_serial_low = serial; ++ ++#ifdef CONFIG_BCM2708_SPIDEV ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); ++#endif + } + + static void timer_set_mode(enum clock_event_mode mode, +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 22da9c2..00c7baa 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -8,6 +8,25 @@ menu "I2C Hardware Bus support" + comment "PC SMBus host controller drivers" + depends on PCI + ++config I2C_BCM2708 ++ tristate "BCM2708 BSC" ++ depends on MACH_BCM2708 ++ help ++ Enabling this option will add BSC (Broadcom Serial Controller) ++ support for the BCM2708. BSC is a Broadcom proprietary bus compatible ++ with I2C/TWI/SMBus. ++ ++config I2C_BCM2708_BAUDRATE ++ prompt "BCM2708 I2C baudrate" ++ depends on I2C_BCM2708 ++ int ++ default 100000 ++ help ++ Set the I2C baudrate. This will alter the default value. A ++ different baudrate can be set by using a module parameter as well. If ++ no parameter is provided when loading, this is the value that will be ++ used. ++ + config I2C_ALI1535 + tristate "ALI 1535" + depends on PCI +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 3638feb..4905fae 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -2,6 +2,8 @@ + # Makefile for the i2c bus drivers. + # + ++obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o ++ + # ACPI drivers + obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o + +diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c +new file mode 100644 +index 0000000..7d385a3 +--- /dev/null ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -0,0 +1,449 @@ ++/* ++ * Driver for Broadcom BCM2708 BSC Controllers ++ * ++ * Copyright (C) 2012 Chris Boot & Frank Buss ++ * ++ * This driver is inspired by: ++ * i2c-ocores.c, by Peter Korsgaard ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* BSC register offsets */ ++#define BSC_C 0x00 ++#define BSC_S 0x04 ++#define BSC_DLEN 0x08 ++#define BSC_A 0x0c ++#define BSC_FIFO 0x10 ++#define BSC_DIV 0x14 ++#define BSC_DEL 0x18 ++#define BSC_CLKT 0x1c ++ ++/* Bitfields in BSC_C */ ++#define BSC_C_I2CEN 0x00008000 ++#define BSC_C_INTR 0x00000400 ++#define BSC_C_INTT 0x00000200 ++#define BSC_C_INTD 0x00000100 ++#define BSC_C_ST 0x00000080 ++#define BSC_C_CLEAR_1 0x00000020 ++#define BSC_C_CLEAR_2 0x00000010 ++#define BSC_C_READ 0x00000001 ++ ++/* Bitfields in BSC_S */ ++#define BSC_S_CLKT 0x00000200 ++#define BSC_S_ERR 0x00000100 ++#define BSC_S_RXF 0x00000080 ++#define BSC_S_TXE 0x00000040 ++#define BSC_S_RXD 0x00000020 ++#define BSC_S_TXD 0x00000010 ++#define BSC_S_RXR 0x00000008 ++#define BSC_S_TXW 0x00000004 ++#define BSC_S_DONE 0x00000002 ++#define BSC_S_TA 0x00000001 ++ ++#define I2C_TIMEOUT_MS 150 ++ ++#define DRV_NAME "bcm2708_i2c" ++ ++static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE; ++module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); ++MODULE_PARM_DESC(baudrate, "The I2C baudrate"); ++ ++static bool combined = false; ++module_param(combined, bool, 0644); ++MODULE_PARM_DESC(combined, "Use combined transactions"); ++ ++struct bcm2708_i2c { ++ struct i2c_adapter adapter; ++ ++ spinlock_t lock; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ ++ struct completion done; ++ ++ struct i2c_msg *msg; ++ int pos; ++ int nmsgs; ++ bool error; ++}; ++ ++/* ++ * This function sets the ALT mode on the I2C pins so that we can use them with ++ * the BSC hardware. ++ * ++ * FIXME: This is a hack. Use pinmux / pinctrl. ++ */ ++static void bcm2708_i2c_init_pinmode(int id) ++{ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ ++ int pin; ++ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); ++ ++ BUG_ON(id != 0 && id != 1); ++ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */ ++ for (pin = id*2+0; pin <= id*2+1; pin++) { ++printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin); ++ INP_GPIO(pin); /* set mode to GPIO input first */ ++ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ ++ } ++ ++ iounmap(gpio); ++ ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg) ++{ ++ return readl(bi->base + reg); ++} ++ ++static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val) ++{ ++ writel(val, bi->base + reg); ++} ++ ++static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi) ++{ ++ bcm2708_wr(bi, BSC_C, 0); ++ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); ++} ++ ++static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi) ++{ ++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len)) ++ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); ++} ++ ++static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) ++{ ++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len)) ++ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); ++} ++ ++static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi) ++{ ++ unsigned long bus_hz; ++ u32 cdiv, s; ++ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; ++ ++ bus_hz = clk_get_rate(bi->clk); ++ cdiv = bus_hz / baudrate; ++ if (cdiv > 0xffff) ++ cdiv = 0xffff; ++ ++ if (bi->msg->flags & I2C_M_RD) ++ c |= BSC_C_INTR | BSC_C_READ; ++ else ++ c |= BSC_C_INTT; ++ ++ bcm2708_wr(bi, BSC_DIV, cdiv); ++ bcm2708_wr(bi, BSC_A, bi->msg->addr); ++ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); ++ if (combined) ++ { ++ /* Do the next two messages meet combined transaction criteria? ++ - Current message is a write, next message is a read ++ - Both messages to same slave address ++ - Write message can fit inside FIFO (16 bytes or less) */ ++ if ( (bi->nmsgs > 1) && ++ !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && ++ (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { ++ /* Fill FIFO with entire write message (16 byte FIFO) */ ++ while (bi->pos < bi->msg->len) ++ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); ++ /* Start write transfer (no interrupts, don't clear FIFO) */ ++ bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); ++ /* poll for transfer start bit (should only take 1-20 polls) */ ++ do { ++ s = bcm2708_rd(bi, BSC_S); ++ } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE))); ++ /* Send next read message before the write transfer finishes. */ ++ bi->nmsgs--; ++ bi->msg++; ++ bi->pos = 0; ++ bcm2708_wr(bi, BSC_DLEN, bi->msg->len); ++ c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; ++ } ++ } ++ bcm2708_wr(bi, BSC_C, c); ++} ++ ++static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) ++{ ++ struct bcm2708_i2c *bi = dev_id; ++ bool handled = true; ++ u32 s; ++ ++ spin_lock(&bi->lock); ++ ++ /* we may see camera interrupts on the "other" I2C channel ++ Just return if we've not sent anything */ ++ if (!bi->nmsgs || !bi->msg ) ++ goto early_exit; ++ ++ s = bcm2708_rd(bi, BSC_S); ++ ++ if (s & (BSC_S_CLKT | BSC_S_ERR)) { ++ bcm2708_bsc_reset(bi); ++ bi->error = true; ++ ++ /* wake up our bh */ ++ complete(&bi->done); ++ } else if (s & BSC_S_DONE) { ++ bi->nmsgs--; ++ ++ if (bi->msg->flags & I2C_M_RD) ++ bcm2708_bsc_fifo_drain(bi); ++ ++ bcm2708_bsc_reset(bi); ++ ++ if (bi->nmsgs) { ++ /* advance to next message */ ++ bi->msg++; ++ bi->pos = 0; ++ bcm2708_bsc_setup(bi); ++ } else { ++ /* wake up our bh */ ++ complete(&bi->done); ++ } ++ } else if (s & BSC_S_TXW) { ++ bcm2708_bsc_fifo_fill(bi); ++ } else if (s & BSC_S_RXR) { ++ bcm2708_bsc_fifo_drain(bi); ++ } else { ++ handled = false; ++ } ++ ++early_exit: ++ spin_unlock(&bi->lock); ++ ++ return handled ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap, ++ struct i2c_msg *msgs, int num) ++{ ++ struct bcm2708_i2c *bi = adap->algo_data; ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&bi->lock, flags); ++ ++ reinit_completion(&bi->done); ++ bi->msg = msgs; ++ bi->pos = 0; ++ bi->nmsgs = num; ++ bi->error = false; ++ ++ bcm2708_bsc_setup(bi); ++ ++ /* unlockig _after_ the setup to avoid races with the interrupt routine */ ++ spin_unlock_irqrestore(&bi->lock, flags); ++ ++ ret = wait_for_completion_timeout(&bi->done, ++ msecs_to_jiffies(I2C_TIMEOUT_MS)); ++ if (ret == 0) { ++ dev_err(&adap->dev, "transfer timed out\n"); ++ spin_lock_irqsave(&bi->lock, flags); ++ bcm2708_bsc_reset(bi); ++ spin_unlock_irqrestore(&bi->lock, flags); ++ return -ETIMEDOUT; ++ } ++ ++ return bi->error ? -EIO : num; ++} ++ ++static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm bcm2708_i2c_algorithm = { ++ .master_xfer = bcm2708_i2c_master_xfer, ++ .functionality = bcm2708_i2c_functionality, ++}; ++ ++static int bcm2708_i2c_probe(struct platform_device *pdev) ++{ ++ struct resource *regs; ++ int irq, err = -ENOMEM; ++ struct clk *clk; ++ struct bcm2708_i2c *bi; ++ struct i2c_adapter *adap; ++ unsigned long bus_hz; ++ u32 cdiv; ++ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(&pdev->dev, "could not get IO memory\n"); ++ return -ENXIO; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "could not get IRQ\n"); ++ return irq; ++ } ++ ++ clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); ++ return PTR_ERR(clk); ++ } ++ ++ bcm2708_i2c_init_pinmode(pdev->id); ++ ++ bi = kzalloc(sizeof(*bi), GFP_KERNEL); ++ if (!bi) ++ goto out_clk_put; ++ ++ platform_set_drvdata(pdev, bi); ++ ++ adap = &bi->adapter; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC; ++ adap->algo = &bcm2708_i2c_algorithm; ++ adap->algo_data = bi; ++ adap->dev.parent = &pdev->dev; ++ adap->nr = pdev->id; ++ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ ++ switch (pdev->id) { ++ case 0: ++ adap->class = I2C_CLASS_HWMON; ++ break; ++ case 1: ++ adap->class = I2C_CLASS_DDC; ++ break; ++ default: ++ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n"); ++ err = -ENXIO; ++ goto out_free_bi; ++ } ++ ++ spin_lock_init(&bi->lock); ++ init_completion(&bi->done); ++ ++ bi->base = ioremap(regs->start, resource_size(regs)); ++ if (!bi->base) { ++ dev_err(&pdev->dev, "could not remap memory\n"); ++ goto out_free_bi; ++ } ++ ++ bi->irq = irq; ++ bi->clk = clk; ++ ++ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED, ++ dev_name(&pdev->dev), bi); ++ if (err) { ++ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); ++ goto out_iounmap; ++ } ++ ++ bcm2708_bsc_reset(bi); ++ ++ err = i2c_add_numbered_adapter(adap); ++ if (err < 0) { ++ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err); ++ goto out_free_irq; ++ } ++ ++ bus_hz = clk_get_rate(bi->clk); ++ cdiv = bus_hz / baudrate; ++ if (cdiv > 0xffff) { ++ cdiv = 0xffff; ++ baudrate = bus_hz / cdiv; ++ } ++ ++ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", ++ pdev->id, (unsigned long)regs->start, irq, baudrate); ++ ++ return 0; ++ ++out_free_irq: ++ free_irq(bi->irq, bi); ++out_iounmap: ++ iounmap(bi->base); ++out_free_bi: ++ kfree(bi); ++out_clk_put: ++ clk_put(clk); ++ return err; ++} ++ ++static int bcm2708_i2c_remove(struct platform_device *pdev) ++{ ++ struct bcm2708_i2c *bi = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ i2c_del_adapter(&bi->adapter); ++ free_irq(bi->irq, bi); ++ iounmap(bi->base); ++ clk_disable(bi->clk); ++ clk_put(bi->clk); ++ kfree(bi); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_i2c_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcm2708_i2c_probe, ++ .remove = bcm2708_i2c_remove, ++}; ++ ++// module_platform_driver(bcm2708_i2c_driver); ++ ++ ++static int __init bcm2708_i2c_init(void) ++{ ++ return platform_driver_register(&bcm2708_i2c_driver); ++} ++ ++static void __exit bcm2708_i2c_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_i2c_driver); ++} ++ ++module_init(bcm2708_i2c_init); ++module_exit(bcm2708_i2c_exit); ++ ++ ++ ++MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708"); ++MODULE_AUTHOR("Chris Boot "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index ab8dfbe..9628e75 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -86,6 +86,14 @@ config SPI_BCM2835 + is for the regular SPI controller. Slave mode operation is not also + not supported. + ++config SPI_BCM2708 ++ tristate "BCM2708 SPI controller driver (SPI0)" ++ depends on MACH_BCM2708 ++ help ++ This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This ++ driver is not compatible with the "Universal SPI Master" or the SPI slave ++ device. ++ + config SPI_BFIN5XX + tristate "SPI controller driver for ADI Blackfin5xx" + depends on BLACKFIN && !BF60x +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index d8cbf65..0dcd03d 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o + obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o + obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o + obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o ++obj-$(CONFIG_SPI_BCM2708) += spi-bcm2708.o + obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o + obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o + obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +new file mode 100644 +index 0000000..b04a57d +--- /dev/null ++++ b/drivers/spi/spi-bcm2708.c +@@ -0,0 +1,626 @@ ++/* ++ * Driver for Broadcom BCM2708 SPI Controllers ++ * ++ * Copyright (C) 2012 Chris Boot ++ * ++ * This driver is inspired by: ++ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos ++ * spi-atmel.c, Copyright (C) 2006 Atmel Corporation ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* SPI register offsets */ ++#define SPI_CS 0x00 ++#define SPI_FIFO 0x04 ++#define SPI_CLK 0x08 ++#define SPI_DLEN 0x0c ++#define SPI_LTOH 0x10 ++#define SPI_DC 0x14 ++ ++/* Bitfields in CS */ ++#define SPI_CS_LEN_LONG 0x02000000 ++#define SPI_CS_DMA_LEN 0x01000000 ++#define SPI_CS_CSPOL2 0x00800000 ++#define SPI_CS_CSPOL1 0x00400000 ++#define SPI_CS_CSPOL0 0x00200000 ++#define SPI_CS_RXF 0x00100000 ++#define SPI_CS_RXR 0x00080000 ++#define SPI_CS_TXD 0x00040000 ++#define SPI_CS_RXD 0x00020000 ++#define SPI_CS_DONE 0x00010000 ++#define SPI_CS_LEN 0x00002000 ++#define SPI_CS_REN 0x00001000 ++#define SPI_CS_ADCS 0x00000800 ++#define SPI_CS_INTR 0x00000400 ++#define SPI_CS_INTD 0x00000200 ++#define SPI_CS_DMAEN 0x00000100 ++#define SPI_CS_TA 0x00000080 ++#define SPI_CS_CSPOL 0x00000040 ++#define SPI_CS_CLEAR_RX 0x00000020 ++#define SPI_CS_CLEAR_TX 0x00000010 ++#define SPI_CS_CPOL 0x00000008 ++#define SPI_CS_CPHA 0x00000004 ++#define SPI_CS_CS_10 0x00000002 ++#define SPI_CS_CS_01 0x00000001 ++ ++#define SPI_TIMEOUT_MS 150 ++ ++#define DRV_NAME "bcm2708_spi" ++ ++struct bcm2708_spi { ++ spinlock_t lock; ++ void __iomem *base; ++ int irq; ++ struct clk *clk; ++ bool stopping; ++ ++ struct list_head queue; ++ struct workqueue_struct *workq; ++ struct work_struct work; ++ struct completion done; ++ ++ const u8 *tx_buf; ++ u8 *rx_buf; ++ int len; ++}; ++ ++struct bcm2708_spi_state { ++ u32 cs; ++ u16 cdiv; ++}; ++ ++/* ++ * This function sets the ALT mode on the SPI pins so that we can use them with ++ * the SPI hardware. ++ * ++ * FIXME: This is a hack. Use pinmux / pinctrl. ++ */ ++static void bcm2708_init_pinmode(void) ++{ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ ++ int pin; ++ u32 *gpio = ioremap(GPIO_BASE, SZ_16K); ++ ++ /* SPI is on GPIO 7..11 */ ++ for (pin = 7; pin <= 11; pin++) { ++ INP_GPIO(pin); /* set mode to GPIO input first */ ++ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */ ++ } ++ ++ iounmap(gpio); ++ ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static inline u32 bcm2708_rd(struct bcm2708_spi *bs, unsigned reg) ++{ ++ return readl(bs->base + reg); ++} ++ ++static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val) ++{ ++ writel(val, bs->base + reg); ++} ++ ++static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len) ++{ ++ u8 byte; ++ ++ while (len--) { ++ byte = bcm2708_rd(bs, SPI_FIFO); ++ if (bs->rx_buf) ++ *bs->rx_buf++ = byte; ++ } ++} ++ ++static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len) ++{ ++ u8 byte; ++ u16 val; ++ ++ if (len > bs->len) ++ len = bs->len; ++ ++ if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) { ++ /* LoSSI mode */ ++ if (unlikely(len % 2)) { ++ printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n"); ++ bs->len = 0; ++ return; ++ } ++ while (len) { ++ if (bs->tx_buf) { ++ val = *(const u16 *)bs->tx_buf; ++ bs->tx_buf += 2; ++ } else ++ val = 0; ++ bcm2708_wr(bs, SPI_FIFO, val); ++ bs->len -= 2; ++ len -= 2; ++ } ++ return; ++ } ++ ++ while (len--) { ++ byte = bs->tx_buf ? *bs->tx_buf++ : 0; ++ bcm2708_wr(bs, SPI_FIFO, byte); ++ bs->len--; ++ } ++} ++ ++static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id) ++{ ++ struct spi_master *master = dev_id; ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ u32 cs; ++ ++ spin_lock(&bs->lock); ++ ++ cs = bcm2708_rd(bs, SPI_CS); ++ ++ if (cs & SPI_CS_DONE) { ++ if (bs->len) { /* first interrupt in a transfer */ ++ /* fill the TX fifo with up to 16 bytes */ ++ bcm2708_wr_fifo(bs, 16); ++ } else { /* transfer complete */ ++ /* disable interrupts */ ++ cs &= ~(SPI_CS_INTR | SPI_CS_INTD); ++ bcm2708_wr(bs, SPI_CS, cs); ++ ++ /* drain RX FIFO */ ++ while (cs & SPI_CS_RXD) { ++ bcm2708_rd_fifo(bs, 1); ++ cs = bcm2708_rd(bs, SPI_CS); ++ } ++ ++ /* wake up our bh */ ++ complete(&bs->done); ++ } ++ } else if (cs & SPI_CS_RXR) { ++ /* read 12 bytes of data */ ++ bcm2708_rd_fifo(bs, 12); ++ ++ /* write up to 12 bytes */ ++ bcm2708_wr_fifo(bs, 12); ++ } ++ ++ spin_unlock(&bs->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcm2708_setup_state(struct spi_master *master, ++ struct device *dev, struct bcm2708_spi_state *state, ++ u32 hz, u8 csel, u8 mode, u8 bpw) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ int cdiv; ++ unsigned long bus_hz; ++ u32 cs = 0; ++ ++ bus_hz = clk_get_rate(bs->clk); ++ ++ if (hz >= bus_hz) { ++ cdiv = 2; /* bus_hz / 2 is as fast as we can go */ ++ } else if (hz) { ++ cdiv = DIV_ROUND_UP(bus_hz, hz); ++ ++ /* CDIV must be a power of 2, so round up */ ++ cdiv = roundup_pow_of_two(cdiv); ++ ++ if (cdiv > 65536) { ++ dev_dbg(dev, ++ "setup: %d Hz too slow, cdiv %u; min %ld Hz\n", ++ hz, cdiv, bus_hz / 65536); ++ return -EINVAL; ++ } else if (cdiv == 65536) { ++ cdiv = 0; ++ } else if (cdiv == 1) { ++ cdiv = 2; /* 1 gets rounded down to 0; == 65536 */ ++ } ++ } else { ++ cdiv = 0; ++ } ++ ++ switch (bpw) { ++ case 8: ++ break; ++ case 9: ++ /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */ ++ cs |= SPI_CS_LEN; ++ break; ++ default: ++ dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n", ++ bpw); ++ return -EINVAL; ++ } ++ ++ if (mode & SPI_CPOL) ++ cs |= SPI_CS_CPOL; ++ if (mode & SPI_CPHA) ++ cs |= SPI_CS_CPHA; ++ ++ if (!(mode & SPI_NO_CS)) { ++ if (mode & SPI_CS_HIGH) { ++ cs |= SPI_CS_CSPOL; ++ cs |= SPI_CS_CSPOL0 << csel; ++ } ++ ++ cs |= csel; ++ } else { ++ cs |= SPI_CS_CS_10 | SPI_CS_CS_01; ++ } ++ ++ if (state) { ++ state->cs = cs; ++ state->cdiv = cdiv; ++ dev_dbg(dev, "setup: want %d Hz; " ++ "bus_hz=%lu / cdiv=%u == %lu Hz; " ++ "mode %u: cs 0x%08X\n", ++ hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs); ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_process_transfer(struct bcm2708_spi *bs, ++ struct spi_message *msg, struct spi_transfer *xfer) ++{ ++ struct spi_device *spi = msg->spi; ++ struct bcm2708_spi_state state, *stp; ++ int ret; ++ u32 cs; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ if (xfer->bits_per_word || xfer->speed_hz) { ++ ret = bcm2708_setup_state(spi->master, &spi->dev, &state, ++ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, ++ spi->chip_select, spi->mode, ++ xfer->bits_per_word ? xfer->bits_per_word : ++ spi->bits_per_word); ++ if (ret) ++ return ret; ++ ++ stp = &state; ++ } else { ++ stp = spi->controller_state; ++ } ++ ++ reinit_completion(&bs->done); ++ bs->tx_buf = xfer->tx_buf; ++ bs->rx_buf = xfer->rx_buf; ++ bs->len = xfer->len; ++ ++ cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA; ++ ++ bcm2708_wr(bs, SPI_CLK, stp->cdiv); ++ bcm2708_wr(bs, SPI_CS, cs); ++ ++ ret = wait_for_completion_timeout(&bs->done, ++ msecs_to_jiffies(SPI_TIMEOUT_MS)); ++ if (ret == 0) { ++ dev_err(&spi->dev, "transfer timed out\n"); ++ return -ETIMEDOUT; ++ } ++ ++ if (xfer->delay_usecs) ++ udelay(xfer->delay_usecs); ++ ++ if (list_is_last(&xfer->transfer_list, &msg->transfers) || ++ xfer->cs_change) { ++ /* clear TA and interrupt flags */ ++ bcm2708_wr(bs, SPI_CS, stp->cs); ++ } ++ ++ msg->actual_length += (xfer->len - bs->len); ++ ++ return 0; ++} ++ ++static void bcm2708_work(struct work_struct *work) ++{ ++ struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work); ++ unsigned long flags; ++ struct spi_message *msg; ++ struct spi_transfer *xfer; ++ int status = 0; ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ while (!list_empty(&bs->queue)) { ++ msg = list_first_entry(&bs->queue, struct spi_message, queue); ++ list_del_init(&msg->queue); ++ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ status = bcm2708_process_transfer(bs, msg, xfer); ++ if (status) ++ break; ++ } ++ ++ msg->status = status; ++ msg->complete(msg->context); ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ } ++ spin_unlock_irqrestore(&bs->lock, flags); ++} ++ ++static int bcm2708_spi_setup(struct spi_device *spi) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); ++ struct bcm2708_spi_state *state; ++ int ret; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ if (!(spi->mode & SPI_NO_CS) && ++ (spi->chip_select > spi->master->num_chipselect)) { ++ dev_dbg(&spi->dev, ++ "setup: invalid chipselect %u (%u defined)\n", ++ spi->chip_select, spi->master->num_chipselect); ++ return -EINVAL; ++ } ++ ++ state = spi->controller_state; ++ if (!state) { ++ state = kzalloc(sizeof(*state), GFP_KERNEL); ++ if (!state) ++ return -ENOMEM; ++ ++ spi->controller_state = state; ++ } ++ ++ ret = bcm2708_setup_state(spi->master, &spi->dev, state, ++ spi->max_speed_hz, spi->chip_select, spi->mode, ++ spi->bits_per_word); ++ if (ret < 0) { ++ kfree(state); ++ spi->controller_state = NULL; ++ return ret; ++ } ++ ++ dev_dbg(&spi->dev, ++ "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n", ++ spi->chip_select, spi->max_speed_hz, spi->bits_per_word, ++ spi->mode, state->cs, state->cdiv); ++ ++ return 0; ++} ++ ++static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct bcm2708_spi *bs = spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ int ret; ++ unsigned long flags; ++ ++ if (unlikely(list_empty(&msg->transfers))) ++ return -EINVAL; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) { ++ dev_dbg(&spi->dev, "missing rx or tx buf\n"); ++ return -EINVAL; ++ } ++ ++ if (!xfer->bits_per_word || xfer->speed_hz) ++ continue; ++ ++ ret = bcm2708_setup_state(spi->master, &spi->dev, NULL, ++ xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz, ++ spi->chip_select, spi->mode, ++ xfer->bits_per_word ? xfer->bits_per_word : ++ spi->bits_per_word); ++ if (ret) ++ return ret; ++ } ++ ++ msg->status = -EINPROGRESS; ++ msg->actual_length = 0; ++ ++ spin_lock_irqsave(&bs->lock, flags); ++ list_add_tail(&msg->queue, &bs->queue); ++ queue_work(bs->workq, &bs->work); ++ spin_unlock_irqrestore(&bs->lock, flags); ++ ++ return 0; ++} ++ ++static void bcm2708_spi_cleanup(struct spi_device *spi) ++{ ++ if (spi->controller_state) { ++ kfree(spi->controller_state); ++ spi->controller_state = NULL; ++ } ++} ++ ++static int bcm2708_spi_probe(struct platform_device *pdev) ++{ ++ struct resource *regs; ++ int irq, err = -ENOMEM; ++ struct clk *clk; ++ struct spi_master *master; ++ struct bcm2708_spi *bs; ++ ++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!regs) { ++ dev_err(&pdev->dev, "could not get IO memory\n"); ++ return -ENXIO; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(&pdev->dev, "could not get IRQ\n"); ++ return irq; ++ } ++ ++ clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk)); ++ return PTR_ERR(clk); ++ } ++ ++ bcm2708_init_pinmode(); ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*bs)); ++ if (!master) { ++ dev_err(&pdev->dev, "spi_alloc_master() failed\n"); ++ goto out_clk_put; ++ } ++ ++ /* the spi->mode bits understood by this driver: */ ++ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS; ++ ++ master->bus_num = pdev->id; ++ master->num_chipselect = 3; ++ master->setup = bcm2708_spi_setup; ++ master->transfer = bcm2708_spi_transfer; ++ master->cleanup = bcm2708_spi_cleanup; ++ platform_set_drvdata(pdev, master); ++ ++ bs = spi_master_get_devdata(master); ++ ++ spin_lock_init(&bs->lock); ++ INIT_LIST_HEAD(&bs->queue); ++ init_completion(&bs->done); ++ INIT_WORK(&bs->work, bcm2708_work); ++ ++ bs->base = ioremap(regs->start, resource_size(regs)); ++ if (!bs->base) { ++ dev_err(&pdev->dev, "could not remap memory\n"); ++ goto out_master_put; ++ } ++ ++ bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev)); ++ if (!bs->workq) { ++ dev_err(&pdev->dev, "could not create workqueue\n"); ++ goto out_iounmap; ++ } ++ ++ bs->irq = irq; ++ bs->clk = clk; ++ bs->stopping = false; ++ ++ err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev), ++ master); ++ if (err) { ++ dev_err(&pdev->dev, "could not request IRQ: %d\n", err); ++ goto out_workqueue; ++ } ++ ++ /* initialise the hardware */ ++ clk_enable(clk); ++ bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); ++ ++ err = spi_register_master(master); ++ if (err) { ++ dev_err(&pdev->dev, "could not register SPI master: %d\n", err); ++ goto out_free_irq; ++ } ++ ++ dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n", ++ (unsigned long)regs->start, irq); ++ ++ return 0; ++ ++out_free_irq: ++ free_irq(bs->irq, master); ++out_workqueue: ++ destroy_workqueue(bs->workq); ++out_iounmap: ++ iounmap(bs->base); ++out_master_put: ++ spi_master_put(master); ++out_clk_put: ++ clk_put(clk); ++ return err; ++} ++ ++static int bcm2708_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct bcm2708_spi *bs = spi_master_get_devdata(master); ++ ++ /* reset the hardware and block queue progress */ ++ spin_lock_irq(&bs->lock); ++ bs->stopping = true; ++ bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); ++ spin_unlock_irq(&bs->lock); ++ ++ flush_work(&bs->work); ++ ++ clk_disable(bs->clk); ++ clk_put(bs->clk); ++ free_irq(bs->irq, master); ++ iounmap(bs->base); ++ ++ spi_unregister_master(master); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_spi_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcm2708_spi_probe, ++ .remove = bcm2708_spi_remove, ++}; ++ ++ ++static int __init bcm2708_spi_init(void) ++{ ++ return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe); ++} ++module_init(bcm2708_spi_init); ++ ++static void __exit bcm2708_spi_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_spi_driver); ++} ++module_exit(bcm2708_spi_exit); ++ ++ ++//module_platform_driver(bcm2708_spi_driver); ++ ++MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708"); ++MODULE_AUTHOR("Chris Boot "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" DRV_NAME); + +From 6942b6c513172931ea563999626c8c179257a7e5 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Wed, 30 Jan 2013 12:45:18 +0000 +Subject: [PATCH 018/104] bcm2835: add v4l2 camera device + +- Supports raw YUV capture, preview, JPEG and H264. +- Uses videobuf2 for data transfer, using dma_buf. +- Uses 3.6.10 timestamping +- Camera power based on use +- Uses immutable input mode on video encoder + +Signed-off-by: Daniel Stone +Signed-off-by: Luke Diamand + +V4L2: Fixes from 6by9 + +V4L2: Fix EV values. Add manual shutter speed control + +V4L2 EV values should be in units of 1/1000. Corrected. +Add support for V4L2_CID_EXPOSURE_ABSOLUTE which should +give manual shutter control. Requires manual exposure mode +to be selected first. + +Signed-off-by: Dave Stevenson + +V4L2: Correct JPEG Q-factor range + +Should be 1-100, not 0-100 + +Signed-off-by: Dave Stevenson + +V4L2: Fix issue of driver jamming if STREAMON failed. + +Fix issue where the driver was left in a partially enabled +state if STREAMON failed, and would then reject many IOCTLs +as it thought it was streaming. + +Signed-off-by: Dave Stevenson + +V4L2: Fix ISO controls. + +Driver was passing the index to the GPU, and not the desired +ISO value. + +Signed-off-by: Dave Stevenson + +V4L2: Add flicker avoidance controls + +Add support for V4L2_CID_POWER_LINE_FREQUENCY to set flicker +avoidance frequencies. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for frame rate control. + +Add support for frame rate (or time per frame as V4L2 +inverts it) control via s_parm. + +Signed-off-by: Dave Stevenson + +V4L2: Improve G_FBUF handling so we pass conformance + +Return some sane numbers for get framebuffer so that +we pass conformance. + +Signed-off-by: Dave Stevenson + +V4L2: Fix information advertised through g_vidfmt + +Width and height were being stored based on incorrect +values. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for inline H264 headers + +Add support for V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER +to control H264 inline headers. +Requires firmware fix to work correctly, otherwise format +has to be set to H264 before this parameter is set. + +Signed-off-by: Dave Stevenson + +V4L2: Fix JPEG timestamp issue + +JPEG images were coming through from the GPU with timestamp +of 0. Detect this and give current system time instead +of some invalid value. + +Signed-off-by: Dave Stevenson + +V4L2: Fix issue when switching down JPEG resolution. + +JPEG buffer size calculation is based on input resolution. +Input resolution was being configured after output port +format. Caused failures if switching from one JPEG resolution +to a smaller one. + +Signed-off-by: Dave Stevenson + +V4L2: Enable MJPEG encoding + +Requires GPU firmware update to support MJPEG encoder. + +Signed-off-by: Dave Stevenson + +V4L2: Correct flag settings for compressed formats + +Set flags field correctly on enum_fmt_vid_cap for compressed +image formats. + +Signed-off-by: Dave Stevenson + +V4L2: H264 profile & level ctrls, FPS control and auto exp pri + +Several control handling updates. +H264 profile and level controls. +Timeperframe/FPS reworked to add V4L2_CID_EXPOSURE_AUTO_PRIORITY to +select whether AE is allowed to override the framerate specified. + +Signed-off-by: Dave Stevenson + +V4L2: Correct BGR24 to RGB24 in format table + +Signed-off-by: Dave Stevenson + +V4L2: Add additional pixel formats. Correct colourspace + +Adds the other flavours of YUYV, and NV12. +Corrects the overlay advertised colourspace. + +Signed-off-by: Dave Stevenson + +V4L2: Drop logging msg from info to debug + +Signed-off-by: Dave Stevenson + +V4L2: Initial pass at scene modes. + +Only supports exposure mode and metering modes. + +Signed-off-by: Dave Stevenson + +V4L2: Add manual white balance control. + +Adds support for V4L2_CID_RED_BALANCE and +V4L2_CID_BLUE_BALANCE. Only has an effect if +V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE has +V4L2_WHITE_BALANCE_MANUAL selected. + +Signed-off-by: Dave Stevenson + +config: Enable V4L / MMAL driver + +V4L2: Increase the MMAL timeout to 3sec + +MJPEG codec flush is now taking longer and results +in a kernel panic if the driver has stopped waiting for +the result when it finally completes. +Increase the timeout value from 1 to 3secs. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for setting H264_I_PERIOD + +Adds support for the parameter V4L2_CID_MPEG_VIDEO_H264_I_PERIOD +to set the frequency with which I frames are produced. + +Signed-off-by: Dave Stevenson + +V4L2: Enable GPU function for removing padding from images. + +GPU can now support arbitrary strides, although may require +additional processing to achieve it. Enable this feature +so that the images delivered are the size requested. + +Signed-off-by: Dave Stevenson + +V4L2: Add support for V4L2_PIX_FMT_BGR32 + +Signed-off-by: Dave Stevenson + +V4L2: Set the colourspace to avoid odd YUV-RGB conversions + +Removes the amiguity from the conversion routines and stops +them dropping back to the SD vs HD choice of coeffs. + +Signed-off-by: Dave Stevenson + +V4L2: Make video/still threshold a run-time param + +Move the define for at what resolution the driver +switches from a video mode capture to a stills mode +capture to module parameters. + +Signed-off-by: Dave Stevenson + +V4L2: Fix incorrect pool sizing + +Signed-off-by: Dave Stevenson + +V4L2: Add option to disable enum_framesizes. + +Gstreamer's handling of a driver that advertises +V4L2_FRMSIZE_TYPE_STEPWISE to define the supported +resolutions is broken. See bug +https://bugzilla.gnome.org/show_bug.cgi?id=726521 + +Optional parameter of gst_v4l2src_is_broken added. +If non-zero, the driver claims not to support that +ioctl, and gstreamer should be happy again (it +guesses a set of defaults for itself). + +Signed-off-by: Dave Stevenson + +V4L2: Add support for more image formats + +Adds YVU420 (YV12), YVU420SP (NV21), and BGR888. + +Signed-off-by: Dave Stevenson + +V4L2: Extend range for V4L2_CID_MPEG_VIDEO_H264_I_PERIOD + +Request to extend the range from the fairly arbitrary +1000 frames (33 seconds at 30fps). Extend out to the +max range supported (int32 value). +Also allow 0, which is handled by the codec as only +send an I-frame on the first frame and never again. +There may be an exception if it detects a significant +scene change, but there's no easy way around that. + +Signed-off-by: Dave Stevenson + +bcm2835-camera: stop_streaming now has a void return + +BCM2835-V4L2: Fix compliance test failures + +VIDIOC_TRY_FMT and VIDIOC_S_FMT tests were faling due +to reporting V4L2_COLORSPACE_JPEG when the colour +format wasn't V4L2_PIX_FMT_JPEG. +Now reports V4L2_COLORSPACE_SMPTE170M for YUV formats. +--- + Documentation/video4linux/bcm2835-v4l2.txt | 60 + + drivers/media/platform/Kconfig | 2 + + drivers/media/platform/Makefile | 2 + + drivers/media/platform/bcm2835/Kconfig | 25 + + drivers/media/platform/bcm2835/Makefile | 5 + + drivers/media/platform/bcm2835/bcm2835-camera.c | 1828 +++++++++++++++++++++ + drivers/media/platform/bcm2835/bcm2835-camera.h | 126 ++ + drivers/media/platform/bcm2835/controls.c | 1322 +++++++++++++++ + drivers/media/platform/bcm2835/mmal-common.h | 52 + + drivers/media/platform/bcm2835/mmal-encodings.h | 127 ++ + drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + + drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + + drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ + drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ + drivers/media/platform/bcm2835/mmal-parameters.h | 656 ++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ + 17 files changed, 6941 insertions(+) + create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt + create mode 100644 drivers/media/platform/bcm2835/Kconfig + create mode 100644 drivers/media/platform/bcm2835/Makefile + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h + create mode 100644 drivers/media/platform/bcm2835/controls.c + create mode 100644 drivers/media/platform/bcm2835/mmal-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h + create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h + +diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt +new file mode 100644 +index 0000000..c585a8f +--- /dev/null ++++ b/Documentation/video4linux/bcm2835-v4l2.txt +@@ -0,0 +1,60 @@ ++ ++BCM2835 (aka Raspberry Pi) V4L2 driver ++====================================== ++ ++1. Copyright ++============ ++ ++Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ ++2. License ++========== ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT 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. ++ ++3. Quick Start ++============== ++ ++You need a version 1.0 or later of v4l2-ctl, available from: ++ git://git.linuxtv.org/v4l-utils.git ++ ++$ sudo modprobe bcm2835-v4l2 ++ ++Turn on the overlay: ++ ++$ v4l2-ctl --overlay=1 ++ ++Turn off the overlay: ++ ++$ v4l2-ctl --overlay=0 ++ ++Set the capture format for video: ++ ++$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 ++ ++(Note: 1088 not 1080). ++ ++Capture: ++ ++$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 ++ ++Stills capture: ++ ++$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 ++$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg ++ ++List of available formats: ++ ++$ v4l2-ctl --list-formats +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index d9b872b..2da9264 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -11,6 +11,8 @@ menuconfig V4L_PLATFORM_DRIVERS + + if V4L_PLATFORM_DRIVERS + ++source "drivers/media/platform/bcm2835/Kconfig" ++ + source "drivers/media/platform/marvell-ccic/Kconfig" + + config VIDEO_VIA_CAMERA +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index 3ec1547..d8d5927 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -2,6 +2,8 @@ + # Makefile for the video capture/playback device drivers. + # + ++obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ ++ + obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o + obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o + +diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig +new file mode 100644 +index 0000000..2cb1a68 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Kconfig +@@ -0,0 +1,25 @@ ++# Broadcom VideoCore IV v4l2 camera support ++ ++config VIDEO_BCM2835 ++ bool "Broadcom BCM2835 camera interface driver" ++ depends on VIDEO_V4L2 && (ARCH_BCM2708 || ARCH_BCM2709) ++ ---help--- ++ Say Y here to enable camera host interface devices for ++ Broadcom BCM2835 SoC. This operates over the VCHIQ interface ++ to a service running on VideoCore. ++ ++ ++if VIDEO_BCM2835 ++ ++config VIDEO_BCM2835_MMAL ++ tristate "Broadcom BM2835 MMAL camera interface driver" ++ depends on BCM2708_VCHIQ ++ select VIDEOBUF2_VMALLOC ++ ---help--- ++ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2835-v4l2.o ++ ++ ++endif # VIDEO_BM2835 +diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile +new file mode 100644 +index 0000000..f17c79c +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Makefile +@@ -0,0 +1,5 @@ ++bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o ++ ++obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o ++ ++ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +new file mode 100644 +index 0000000..eb4d831 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -0,0 +1,1828 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-encodings.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++#define BM2835_MMAL_VERSION "0.0.2" ++#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" ++#define MIN_WIDTH 16 ++#define MIN_HEIGHT 16 ++#define MAX_WIDTH 2592 ++#define MAX_HEIGHT 1944 ++#define MIN_BUFFER_SIZE (80*1024) ++ ++#define MAX_VIDEO_MODE_WIDTH 1280 ++#define MAX_VIDEO_MODE_HEIGHT 720 ++ ++MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); ++MODULE_AUTHOR("Vincent Sanders"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(BM2835_MMAL_VERSION); ++ ++int bcm2835_v4l2_debug; ++module_param_named(debug, bcm2835_v4l2_debug, int, 0644); ++MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); ++ ++int max_video_width = MAX_VIDEO_MODE_WIDTH; ++int max_video_height = MAX_VIDEO_MODE_HEIGHT; ++module_param(max_video_width, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(max_video_width, "Threshold for video mode"); ++module_param(max_video_height, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(max_video_height, "Threshold for video mode"); ++ ++/* Gstreamer bug https://bugzilla.gnome.org/show_bug.cgi?id=726521 ++ * v4l2src does bad (and actually wrong) things when the vidioc_enum_framesizes ++ * function says type V4L2_FRMSIZE_TYPE_STEPWISE, which we do by default. ++ * It's happier if we just don't say anything at all, when it then ++ * sets up a load of defaults that it thinks might work. ++ * If gst_v4l2src_is_broken is non-zero, then we remove the function from ++ * our function table list (actually switch to an alternate set, but same ++ * result). ++ */ ++int gst_v4l2src_is_broken = 0; ++module_param(gst_v4l2src_is_broken, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ++MODULE_PARM_DESC(gst_v4l2src_is_broken, "If non-zero, enable workaround for Gstreamer"); ++ ++static struct bm2835_mmal_dev *gdev; /* global device data */ ++ ++#define FPS_MIN 1 ++#define FPS_MAX 90 ++ ++/* timeperframe: min/max and default */ ++static const struct v4l2_fract ++ tpf_min = {.numerator = 1, .denominator = FPS_MAX}, ++ tpf_max = {.numerator = 1, .denominator = FPS_MIN}, ++ tpf_default = {.numerator = 1000, .denominator = 30000}; ++ ++/* video formats */ ++static struct mmal_fmt formats[] = { ++ { ++ .name = "4:2:0, packed YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_I420, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YUYV, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (LE)", ++ .fourcc = V4L2_PIX_FMT_RGB24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGR24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "JPEG", ++ .fourcc = V4L2_PIX_FMT_JPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_JPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, ++ }, ++ { ++ .name = "H264", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_H264, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ }, ++ { ++ .name = "MJPEG", ++ .fourcc = V4L2_PIX_FMT_MJPEG, ++ .flags = V4L2_FMT_FLAG_COMPRESSED, ++ .mmal = MMAL_ENCODING_MJPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ }, ++ { ++ .name = "4:2:2, packed, YVYU", ++ .fourcc = V4L2_PIX_FMT_YVYU, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YVYU, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, VYUY", ++ .fourcc = V4L2_PIX_FMT_VYUY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_VYUY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, UYVY", ++ .fourcc = V4L2_PIX_FMT_UYVY, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_UYVY, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed, NV12", ++ .fourcc = V4L2_PIX_FMT_NV12, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_RGB24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed YVU", ++ .fourcc = V4L2_PIX_FMT_YVU420, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_YV12, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:0, packed, NV21", ++ .fourcc = V4L2_PIX_FMT_NV21, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_NV21, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB32 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR32, ++ .flags = 0, ++ .mmal = MMAL_ENCODING_BGRA, ++ .depth = 32, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++}; ++ ++static struct mmal_fmt *get_format(struct v4l2_format *f) ++{ ++ struct mmal_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < ARRAY_SIZE(formats); k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == ARRAY_SIZE(formats)) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++/* ------------------------------------------------------------------ ++ Videobuf queue operations ++ ------------------------------------------------------------------*/ ++ ++static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ unsigned long size; ++ ++ /* refuse queue setup if port is not configured */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port not configured\n", __func__); ++ return -EINVAL; ++ } ++ ++ size = dev->capture.port->current_buffer.size; ++ if (size == 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port buffer size is zero\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) ++ *nbuffers = (dev->capture.port->current_buffer.num + 2); ++ ++ *nplanes = 1; ++ ++ sizes[0] = size; ++ ++ /* ++ * videobuf2-vmalloc allocator is context-less so no need to set ++ * alloc_ctxs array. ++ */ ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ unsigned long size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ BUG_ON(dev->capture.port == NULL); ++ BUG_ON(dev->capture.fmt == NULL); ++ ++ size = dev->capture.stride * dev->capture.height; ++ if (vb2_plane_size(vb, 0) < size) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline bool is_capturing(struct bm2835_mmal_dev *dev) ++{ ++ return dev->capture.camera_port == ++ &dev-> ++ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; ++} ++ ++static void buffer_cb(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, ++ struct mmal_buffer *buf, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) ++{ ++ struct bm2835_mmal_dev *dev = port->cb_ctx; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", ++ __func__, status, buf, length, mmal_flags, pts); ++ ++ if (status != 0) { ++ /* error in transfer */ ++ if (buf != NULL) { ++ /* there was a buffer with the error so return it */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ } ++ return; ++ } else if (length == 0) { ++ /* stream ended */ ++ if (buf != NULL) { ++ /* this should only ever happen if the port is ++ * disabled and there are buffers still queued ++ */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ pr_debug("Empty buffer"); ++ } else if (dev->capture.frame_count) { ++ /* grab another frame */ ++ if (is_capturing(dev)) { ++ pr_debug("Grab another frame"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } else { ++ if (dev->capture.frame_count) { ++ if (dev->capture.vc_start_timestamp != -1 && ++ pts != 0) { ++ s64 runtime_us = pts - ++ dev->capture.vc_start_timestamp; ++ u32 div = 0; ++ u32 rem = 0; ++ ++ div = ++ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); ++ buf->vb.v4l2_buf.timestamp.tv_sec = ++ dev->capture.kernel_start_ts.tv_sec - 1 + ++ div; ++ buf->vb.v4l2_buf.timestamp.tv_usec = ++ dev->capture.kernel_start_ts.tv_usec + rem; ++ ++ if (buf->vb.v4l2_buf.timestamp.tv_usec >= ++ USEC_PER_SEC) { ++ buf->vb.v4l2_buf.timestamp.tv_sec++; ++ buf->vb.v4l2_buf.timestamp.tv_usec -= ++ USEC_PER_SEC; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Convert start time %d.%06d and %llu " ++ "with offset %llu to %d.%06d\n", ++ (int)dev->capture.kernel_start_ts. ++ tv_sec, ++ (int)dev->capture.kernel_start_ts. ++ tv_usec, ++ dev->capture.vc_start_timestamp, pts, ++ (int)buf->vb.v4l2_buf.timestamp.tv_sec, ++ (int)buf->vb.v4l2_buf.timestamp. ++ tv_usec); ++ } else { ++ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); ++ } ++ ++ vb2_set_plane_payload(&buf->vb, 0, length); ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); ++ ++ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && ++ is_capturing(dev)) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Grab another frame as buffer has EOS"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } ++} ++ ++static int enable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed enabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ } ++ dev->camera_use_count++; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, "enabled camera (refcount %d)\n", ++ dev->camera_use_count); ++ return 0; ++} ++ ++static int disable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ v4l2_err(&dev->v4l2_dev, ++ "Disabled the camera when already disabled\n"); ++ return -EINVAL; ++ } ++ dev->camera_use_count--; ++ if (!dev->camera_use_count) { ++ unsigned int i = 0xFFFFFFFF; ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Disabling camera\n"); ++ ret = ++ vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed disabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]->control, ++ MMAL_PARAMETER_CAMERA_NUM, &i, ++ sizeof(i)); ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Camera refcount now %d\n", dev->camera_use_count); ++ return 0; ++} ++ ++static void buffer_queue(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); ++ int ret; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: dev:%p buf:%p\n", __func__, dev, buf); ++ ++ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); ++ buf->buffer_size = vb2_plane_size(&buf->vb, 0); ++ ++ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); ++ if (ret < 0) ++ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", ++ __func__); ++} ++ ++static int start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ int ret; ++ int parameter_size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ if (enable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); ++ return -EINVAL; ++ } ++ ++ /*init_completion(&dev->capture.frame_cmplt); */ ++ ++ /* enable frame capture */ ++ dev->capture.frame_count = 1; ++ ++ /* if the preview is not already running, wait for a few frames for AGC ++ * to settle down. ++ */ ++ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) ++ msleep(300); ++ ++ /* enable the connection from camera to encoder (if applicable) */ ++ if (dev->capture.camera_port != dev->capture.port ++ && dev->capture.camera_port) { ++ ret = vchiq_mmal_port_enable(dev->instance, ++ dev->capture.camera_port, NULL); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable encode tunnel - error %d\n", ++ ret); ++ return -1; ++ } ++ } ++ ++ /* Get VC timestamp at this point in time */ ++ parameter_size = sizeof(dev->capture.vc_start_timestamp); ++ if (vchiq_mmal_port_parameter_get(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_SYSTEM_TIME, ++ &dev->capture.vc_start_timestamp, ++ ¶meter_size)) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to get VC start time - update your VC f/w\n"); ++ ++ /* Flag to indicate just to rely on kernel timestamps */ ++ dev->capture.vc_start_timestamp = -1; ++ } else ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Start time %lld size %d\n", ++ dev->capture.vc_start_timestamp, parameter_size); ++ ++ v4l2_get_timestamp(&dev->capture.kernel_start_ts); ++ ++ /* enable the camera port */ ++ dev->capture.port->cb_ctx = dev; ++ ret = ++ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable capture port - error %d. " ++ "Disabling camera port again\n", ret); ++ ++ vchiq_mmal_port_disable(dev->instance, ++ dev->capture.camera_port); ++ if (disable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); ++ return -EINVAL; ++ } ++ return -1; ++ } ++ ++ /* capture the first frame */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static void stop_streaming(struct vb2_queue *vq) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ init_completion(&dev->capture.frame_cmplt); ++ dev->capture.frame_count = 0; ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "no capture port - stream not started?\n"); ++ return; ++ } ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); ++ ++ /* stop capturing frames */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ ++ /* wait for last frame to complete */ ++ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); ++ if (ret <= 0) ++ v4l2_err(&dev->v4l2_dev, ++ "error %d waiting for frame completion\n", ret); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling connection\n"); ++ ++ /* disable the connection from camera to encoder */ ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); ++ if (!ret && dev->capture.camera_port != dev->capture.port) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling port\n"); ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); ++ } else if (dev->capture.camera_port != dev->capture.port) { ++ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", ++ ret); ++ } ++ ++ if (disable_camera(dev) < 0) ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera\n"); ++} ++ ++static void bm2835_mmal_lock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_lock(&dev->mutex); ++} ++ ++static void bm2835_mmal_unlock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_unlock(&dev->mutex); ++} ++ ++static struct vb2_ops bm2835_mmal_video_qops = { ++ .queue_setup = queue_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .start_streaming = start_streaming, ++ .stop_streaming = stop_streaming, ++ .wait_prepare = bm2835_mmal_unlock, ++ .wait_finish = bm2835_mmal_lock, ++}; ++ ++/* ------------------------------------------------------------------ ++ IOCTL operations ++ ------------------------------------------------------------------*/ ++ ++/* overlay ioctl */ ++static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ f->flags = fmt->flags; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.win = dev->overlay; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ /* Only support one format so get the current one. */ ++ vidioc_g_fmt_vid_overlay(file, priv, f); ++ ++ /* todo: allow the size and/or offset to be changed. */ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ vidioc_try_fmt_vid_overlay(file, priv, f); ++ ++ dev->overlay = f->fmt.win; ++ ++ /* todo: program the preview port parameters */ ++ return 0; ++} ++ ++static int vidioc_overlay(struct file *file, void *f, unsigned int on) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *src; ++ struct vchiq_mmal_port *dst; ++ struct mmal_parameter_displayregion prev_config = { ++ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | ++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, ++ .layer = PREVIEW_LAYER, ++ .alpha = 255, ++ .fullscreen = 0, ++ .dest_rect = { ++ .x = dev->overlay.w.left, ++ .y = dev->overlay.w.top, ++ .width = dev->overlay.w.width, ++ .height = dev->overlay.w.height, ++ }, ++ }; ++ ++ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || ++ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) ++ return 0; /* already in requested state */ ++ ++ src = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ ++ if (!on) { ++ /* disconnect preview ports and disable component */ ++ ret = vchiq_mmal_port_disable(dev->instance, src); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel(dev->instance, src, ++ NULL); ++ if (ret >= 0) ++ ret = vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ disable_camera(dev); ++ return ret; ++ } ++ ++ /* set preview port format and connect it to output */ ++ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, src); ++ if (ret < 0) ++ goto error; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, ++ MMAL_PARAMETER_DISPLAYREGION, ++ &prev_config, sizeof(prev_config)); ++ if (ret < 0) ++ goto error; ++ ++ if (enable_camera(dev) < 0) ++ goto error; ++ ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto error; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", ++ src, dst); ++ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); ++error: ++ return ret; ++} ++ ++static int vidioc_g_fbuf(struct file *file, void *fh, ++ struct v4l2_framebuffer *a) ++{ ++ /* The video overlay must stay within the framebuffer and can't be ++ positioned independently. */ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ a->flags = V4L2_FBUF_FLAG_OVERLAY; ++ a->fmt.width = preview_port->es.video.width; ++ a->fmt.height = preview_port->es.video.height; ++ a->fmt.pixelformat = V4L2_PIX_FMT_YUV420; ++ a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; ++ a->fmt.sizeimage = (preview_port->es.video.width * ++ preview_port->es.video.height * 3)>>1; ++ a->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ ++ return 0; ++} ++ ++/* input ioctls */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ /* only a single camera input */ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ sprintf(inp->name, "Camera %u", inp->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* capture ioctls */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ u32 major; ++ u32 minor; ++ ++ vchiq_mmal_version(dev->instance, &major, &minor); ++ ++ strcpy(cap->driver, "bm2835 mmal"); ++ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", ++ major, minor); ++ ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", dev->v4l2_dev.name); ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ f->flags = fmt->flags; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.pix.width = dev->capture.width; ++ f->fmt.pix.height = dev->capture.height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; ++ f->fmt.pix.bytesperline = dev->capture.stride; ++ f->fmt.pix.sizeimage = dev->capture.buffersize; ++ ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ else if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Clipping/aligning %dx%d format %08X\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); ++ ++ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 1, ++ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 1, 0); ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth)>>3; ++ ++ /* Image buffer has to be padded to allow for alignment, even though ++ * we then remove that padding before delivering the buffer. ++ */ ++ f->fmt.pix.sizeimage = ((f->fmt.pix.height+15)&~15) * ++ (((f->fmt.pix.width+31)&~31) * mfmt->depth) >> 3; ++ ++ if ((mfmt->flags & V4L2_FMT_FLAG_COMPRESSED) && ++ f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) ++ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; ++ ++ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Now %dx%d format %08X\n", ++ f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.pixelformat); ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int mmal_setup_components(struct bm2835_mmal_dev *dev, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; ++ struct vchiq_mmal_component *encode_component = NULL; ++ struct mmal_fmt *mfmt = get_format(f); ++ ++ BUG_ON(!mfmt); ++ ++ if (dev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - disconnect previous tunnel\n"); ++ ++ /* Disconnect any previous connection */ ++ vchiq_mmal_port_connect_tunnel(dev->instance, ++ dev->capture.camera_port, NULL); ++ dev->capture.camera_port = NULL; ++ ret = vchiq_mmal_component_disable(dev->instance, ++ dev->capture. ++ encode_component); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to disable encode component %d\n", ++ ret); ++ ++ dev->capture.encode_component = NULL; ++ } ++ /* format dependant port setup */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_CAMERA: ++ /* Make a further decision on port based on resolution */ ++ if (f->fmt.pix.width <= max_video_width ++ && f->fmt.pix.height <= max_video_height) ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ else ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ break; ++ default: ++ break; ++ } ++ ++ if (!port) ++ return -EINVAL; ++ ++ if (encode_component) ++ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; ++ else ++ camera_port->format.encoding = mfmt->mmal; ++ ++ camera_port->format.encoding_variant = 0; ++ camera_port->es.video.width = f->fmt.pix.width; ++ camera_port->es.video.height = f->fmt.pix.height; ++ camera_port->es.video.crop.x = 0; ++ camera_port->es.video.crop.y = 0; ++ camera_port->es.video.crop.width = f->fmt.pix.width; ++ camera_port->es.video.crop.height = f->fmt.pix.height; ++ camera_port->es.video.frame_rate.num = 0; ++ camera_port->es.video.frame_rate.den = 1; ++ camera_port->es.video.color_space = MMAL_COLOR_SPACE_JPEG_JFIF; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); ++ ++ if (!ret ++ && camera_port == ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]) { ++ bool overlay_enabled = ++ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ /* Preview and encode ports need to match on resolution */ ++ if (overlay_enabled) { ++ /* Need to disable the overlay before we can update ++ * the resolution ++ */ ++ ret = ++ vchiq_mmal_port_disable(dev->instance, ++ preview_port); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ NULL); ++ } ++ preview_port->es.video.width = f->fmt.pix.width; ++ preview_port->es.video.height = f->fmt.pix.height; ++ preview_port->es.video.crop.x = 0; ++ preview_port->es.video.crop.y = 0; ++ preview_port->es.video.crop.width = f->fmt.pix.width; ++ preview_port->es.video.crop.height = f->fmt.pix.height; ++ preview_port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ preview_port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; ++ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); ++ if (overlay_enabled) { ++ ret = vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, ++ preview_port, ++ NULL); ++ } ++ } ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format %dx%d %08X\n", __func__, ++ f->fmt.pix.width, f->fmt.pix.height, ++ f->fmt.pix.pixelformat); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ if (encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - set up encode comp\n"); ++ ++ /* configure buffering */ ++ camera_port->current_buffer.size = ++ camera_port->recommended_buffer.size; ++ camera_port->current_buffer.num = ++ camera_port->recommended_buffer.num; ++ ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ camera_port, ++ &encode_component->input[0]); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to create connection\n", ++ __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ port->es.video.width = f->fmt.pix.width; ++ port->es.video.height = f->fmt.pix.height; ++ port->es.video.crop.x = 0; ++ port->es.video.crop.y = 0; ++ port->es.video.crop.width = f->fmt.pix.width; ++ port->es.video.crop.height = f->fmt.pix.height; ++ port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; ++ ++ port->format.encoding = mfmt->mmal; ++ port->format.encoding_variant = 0; ++ /* Set any encoding specific parameters */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ port->format.bitrate = ++ dev->capture.encode_bitrate; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ /* Could set EXIF parameters here */ ++ break; ++ default: ++ break; ++ } ++ ret = vchiq_mmal_port_set_format(dev->instance, ++ port); ++ if (ret) ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to set format %dx%d fmt %08X\n", ++ __func__, ++ f->fmt.pix.width, ++ f->fmt.pix.height, ++ f->fmt.pix.pixelformat ++ ); ++ } ++ ++ if (!ret) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ encode_component); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s Failed to enable encode components\n", ++ __func__); ++ } ++ } ++ if (!ret) { ++ /* configure buffering */ ++ port->current_buffer.num = 1; ++ port->current_buffer.size = ++ f->fmt.pix.sizeimage; ++ if (port->format.encoding == ++ MMAL_ENCODING_JPEG) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "JPG - buf size now %d was %d\n", ++ f->fmt.pix.sizeimage, ++ port->current_buffer.size); ++ port->current_buffer.size = ++ (f->fmt.pix.sizeimage < ++ (100 << 10)) ++ ? (100 << 10) : f->fmt.pix. ++ sizeimage; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "vid_cap - cur_buf.size set to %d\n", ++ f->fmt.pix.sizeimage); ++ port->current_buffer.alignment = 0; ++ } ++ } else { ++ /* configure buffering */ ++ camera_port->current_buffer.num = 1; ++ camera_port->current_buffer.size = f->fmt.pix.sizeimage; ++ camera_port->current_buffer.alignment = 0; ++ } ++ ++ if (!ret) { ++ dev->capture.fmt = mfmt; ++ dev->capture.stride = f->fmt.pix.bytesperline; ++ dev->capture.width = camera_port->es.video.crop.width; ++ dev->capture.height = camera_port->es.video.crop.height; ++ dev->capture.buffersize = port->current_buffer.size; ++ ++ /* select port for capture */ ++ dev->capture.port = port; ++ dev->capture.camera_port = camera_port; ++ dev->capture.encode_component = encode_component; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "Set dev->capture.fmt %08X, %dx%d, stride %d, size %d", ++ port->format.encoding, ++ dev->capture.width, dev->capture.height, ++ dev->capture.stride, dev->capture.buffersize); ++ } ++ } ++ ++ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ ++ return ret; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ /* try the format to set valid parameters */ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); ++ return ret; ++ } ++ ++ /* if a capture is running refuse to set format */ ++ if (vb2_is_busy(&dev->capture.vb_vidq)) { ++ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ /* If the format is unsupported v4l2 says we should switch to ++ * a supported one and not return an error. */ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ ret = mmal_setup_components(dev, f); ++ if (ret != 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: failed to setup mmal components: %d\n", ++ __func__, ret); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++int vidioc_enum_framesizes(struct file *file, void *fh, ++ struct v4l2_frmsizeenum *fsize) ++{ ++ static const struct v4l2_frmsize_stepwise sizes = { ++ MIN_WIDTH, MAX_WIDTH, 2, ++ MIN_HEIGHT, MAX_HEIGHT, 2 ++ }; ++ int i; ++ ++ if (fsize->index) ++ return -EINVAL; ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fsize->pixel_format) ++ break; ++ if (i == ARRAY_SIZE(formats)) ++ return -EINVAL; ++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; ++ fsize->stepwise = sizes; ++ return 0; ++} ++ ++/* timeperframe is arbitrary and continous */ ++static int vidioc_enum_frameintervals(struct file *file, void *priv, ++ struct v4l2_frmivalenum *fival) ++{ ++ int i; ++ ++ if (fival->index) ++ return -EINVAL; ++ ++ for (i = 0; i < ARRAY_SIZE(formats); i++) ++ if (formats[i].fourcc == fival->pixel_format) ++ break; ++ if (i == ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ /* regarding width & height - we support any within range */ ++ if (fival->width < MIN_WIDTH || fival->width > MAX_WIDTH || ++ fival->height < MIN_HEIGHT || fival->height > MAX_HEIGHT) ++ return -EINVAL; ++ ++ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ ++ /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ ++ fival->stepwise.min = tpf_min; ++ fival->stepwise.max = tpf_max; ++ fival->stepwise.step = (struct v4l2_fract) {1, 1}; ++ ++ return 0; ++} ++ ++static int vidioc_g_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ parm->parm.capture.timeperframe = dev->capture.timeperframe; ++ parm->parm.capture.readbuffers = 1; ++ return 0; ++} ++ ++#define FRACT_CMP(a, OP, b) \ ++ ((u64)(a).numerator * (b).denominator OP \ ++ (u64)(b).numerator * (a).denominator) ++ ++static int vidioc_s_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct v4l2_fract tpf; ++ struct mmal_parameter_rational fps_param; ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ tpf = parm->parm.capture.timeperframe; ++ ++ /* tpf: {*, 0} resets timing; clip to [min, max]*/ ++ tpf = tpf.denominator ? tpf : tpf_default; ++ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; ++ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; ++ ++ dev->capture.timeperframe = tpf; ++ parm->parm.capture.timeperframe = tpf; ++ parm->parm.capture.readbuffers = 1; ++ ++ fps_param.num = 0; /* Select variable fps, and then use ++ * FPS_RANGE to select the actual limits. ++ */ ++ fps_param.den = 1; ++ set_framerate_params(dev); ++ ++ return 0; ++} ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_enum_framesizes = vidioc_enum_framesizes, ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops_gstreamer = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ /* Remove this function ptr to fix gstreamer bug ++ .vidioc_enum_framesizes = vidioc_enum_framesizes, */ ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ------------------------------------------------------------------ ++ Driver init/finalise ++ ------------------------------------------------------------------*/ ++ ++static const struct v4l2_file_operations camera0_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = vb2_fop_release, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ ++ .mmap = vb2_fop_mmap, ++}; ++ ++static struct video_device vdev_template = { ++ .name = "camera0", ++ .fops = &camera0_fops, ++ .ioctl_ops = &camera0_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++static int set_camera_parameters(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *camera) ++{ ++ int ret; ++ struct mmal_parameter_camera_config cam_config = { ++ .max_stills_w = MAX_WIDTH, ++ .max_stills_h = MAX_HEIGHT, ++ .stills_yuv422 = 1, ++ .one_shot_stills = 1, ++ .max_preview_video_w = (max_video_width > 1920) ? ++ max_video_width : 1920, ++ .max_preview_video_h = (max_video_height > 1088) ? ++ max_video_height : 1088, ++ .num_preview_video_frames = 3, ++ .stills_capture_circular_buffer_height = 0, ++ .fast_preview_resume = 0, ++ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC ++ }; ++ ++ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, ++ MMAL_PARAMETER_CAMERA_CONFIG, ++ &cam_config, sizeof(cam_config)); ++ return ret; ++} ++ ++/* MMAL instance and component init */ ++static int __init mmal_init(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ struct mmal_es_format *format; ++ u32 bool_true = 1; ++ ++ ret = vchiq_mmal_init(&dev->instance); ++ if (ret < 0) ++ return ret; ++ ++ /* get the camera component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", ++ &dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_mmal; ++ ++ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < ++ MMAL_CAMERA_PORT_COUNT) { ++ ret = -EINVAL; ++ goto unreg_camera; ++ } ++ ++ ret = set_camera_parameters(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO], ++ MMAL_PARAMETER_NO_IMAGE_PADDING, ++ &bool_true, sizeof(bool_true)); ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ ++ format->es->video.width = 2592; ++ format->es->video.height = 1944; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 2592; ++ format->es->video.crop.height = 1944; ++ format->es->video.frame_rate.num = 0; /* Rely on fps_range */ ++ format->es->video.frame_rate.den = 1; ++ ++ dev->capture.width = format->es->video.width; ++ dev->capture.height = format->es->video.height; ++ dev->capture.fmt = &formats[0]; ++ dev->capture.encode_component = NULL; ++ dev->capture.timeperframe = tpf_default; ++ dev->capture.enc_profile = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH; ++ dev->capture.enc_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_0; ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE], ++ MMAL_PARAMETER_NO_IMAGE_PADDING, ++ &bool_true, sizeof(bool_true)); ++ ++ /* get the preview component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.video_render", ++ &dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { ++ ret = -EINVAL; ++ pr_debug("too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); ++ goto unreg_preview; ++ } ++ ++ /* get the image encoder component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.image_encode", ++ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ if (ret < 0) ++ goto unreg_preview; ++ ++ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, ++ 1); ++ goto unreg_image_encoder; ++ } ++ ++ /* get the video encoder component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", ++ &dev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ if (ret < 0) ++ goto unreg_image_encoder; ++ ++ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, ++ 1); ++ goto unreg_vid_encoder; ++ } ++ ++ { ++ struct vchiq_mmal_port *encoder_port = ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ encoder_port->format.encoding = MMAL_ENCODING_H264; ++ ret = vchiq_mmal_port_set_format(dev->instance, ++ encoder_port); ++ } ++ ++ { ++ unsigned int enable = 1; ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ &enable, sizeof(enable)); ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ &enable, ++ sizeof(enable)); ++ } ++ ret = bm2835_mmal_set_all_camera_controls(dev); ++ if (ret < 0) ++ goto unreg_vid_encoder; ++ ++ return 0; ++ ++unreg_vid_encoder: ++ pr_err("Cleanup: Destroy video encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++unreg_image_encoder: ++ pr_err("Cleanup: Destroy image encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++unreg_preview: ++ pr_err("Cleanup: Destroy video render\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++unreg_camera: ++ pr_err("Cleanup: Destroy camera\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ ++unreg_mmal: ++ vchiq_mmal_finalise(dev->instance); ++ return ret; ++} ++ ++static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, ++ struct video_device *vfd) ++{ ++ int ret; ++ ++ *vfd = vdev_template; ++ if (gst_v4l2src_is_broken) { ++ v4l2_info(&dev->v4l2_dev, ++ "Work-around for gstreamer issue is active.\n"); ++ vfd->ioctl_ops = &camera0_ioctl_ops_gstreamer; ++ } ++ ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ ++ vfd->lock = &dev->mutex; ++ ++ vfd->queue = &dev->capture.vb_vidq; ++ ++ /* video device needs to be able to access instance data */ ++ video_set_drvdata(vfd, dev); ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_info(vfd->v4l2_dev, ++ "V4L2 device registered as %s - stills mode > %dx%d\n", ++ video_device_node_name(vfd), max_video_width, max_video_height); ++ ++ return 0; ++} ++ ++static struct v4l2_format default_v4l2_format = { ++ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, ++ .fmt.pix.width = 1024, ++ .fmt.pix.bytesperline = 1024, ++ .fmt.pix.height = 768, ++ .fmt.pix.sizeimage = 1024*768, ++}; ++ ++static int __init bm2835_mmal_init(void) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev; ++ struct vb2_queue *q; ++ ++ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ /* setup device defaults */ ++ dev->overlay.w.left = 150; ++ dev->overlay.w.top = 50; ++ dev->overlay.w.width = 1024; ++ dev->overlay.w.height = 768; ++ dev->overlay.clipcount = 0; ++ dev->overlay.field = V4L2_FIELD_NONE; ++ ++ dev->capture.fmt = &formats[3]; /* JPEG */ ++ ++ /* v4l device registration */ ++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), ++ "%s", BM2835_MMAL_MODULE_NAME); ++ ret = v4l2_device_register(NULL, &dev->v4l2_dev); ++ if (ret) ++ goto free_dev; ++ ++ /* setup v4l controls */ ++ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); ++ if (ret < 0) ++ goto unreg_dev; ++ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; ++ ++ /* mmal init */ ++ ret = mmal_init(dev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* initialize queue */ ++ q = &dev->capture.vb_vidq; ++ memset(q, 0, sizeof(*q)); ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; ++ q->drv_priv = dev; ++ q->buf_struct_size = sizeof(struct mmal_buffer); ++ q->ops = &bm2835_mmal_video_qops; ++ q->mem_ops = &vb2_vmalloc_memops; ++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ret = vb2_queue_init(q); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ ++ mutex_init(&dev->mutex); ++ ++ /* initialise video devices */ ++ ret = bm2835_mmal_init_device(dev, &dev->vdev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* Really want to call vidioc_s_fmt_vid_cap with the default ++ * format, but currently the APIs don't join up. ++ */ ++ ret = mmal_setup_components(dev, &default_v4l2_format); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: could not setup components\n", __func__); ++ goto unreg_dev; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Broadcom 2835 MMAL video capture ver %s loaded.\n", ++ BM2835_MMAL_VERSION); ++ ++ gdev = dev; ++ return 0; ++ ++unreg_dev: ++ v4l2_ctrl_handler_free(&dev->ctrl_handler); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++free_dev: ++ kfree(dev); ++ ++ v4l2_err(&dev->v4l2_dev, ++ "%s: error %d while loading driver\n", ++ BM2835_MMAL_MODULE_NAME, ret); ++ ++ return ret; ++} ++ ++static void __exit bm2835_mmal_exit(void) ++{ ++ if (!gdev) ++ return; ++ ++ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", ++ video_device_node_name(&gdev->vdev)); ++ ++ video_unregister_device(&gdev->vdev); ++ ++ if (gdev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, ++ "mmal_exit - disconnect tunnel\n"); ++ vchiq_mmal_port_connect_tunnel(gdev->instance, ++ gdev->capture.camera_port, NULL); ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->capture.encode_component); ++ } ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_finalise(gdev->instance); ++ ++ v4l2_ctrl_handler_free(&gdev->ctrl_handler); ++ ++ v4l2_device_unregister(&gdev->v4l2_dev); ++ ++ kfree(gdev); ++} ++ ++module_init(bm2835_mmal_init); ++module_exit(bm2835_mmal_exit); +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +new file mode 100644 +index 0000000..7fe9f65 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -0,0 +1,126 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * core driver device ++ */ ++ ++#define V4L2_CTRL_COUNT 28 /* number of v4l controls */ ++ ++enum { ++ MMAL_COMPONENT_CAMERA = 0, ++ MMAL_COMPONENT_PREVIEW, ++ MMAL_COMPONENT_IMAGE_ENCODE, ++ MMAL_COMPONENT_VIDEO_ENCODE, ++ MMAL_COMPONENT_COUNT ++}; ++ ++enum { ++ MMAL_CAMERA_PORT_PREVIEW = 0, ++ MMAL_CAMERA_PORT_VIDEO, ++ MMAL_CAMERA_PORT_CAPTURE, ++ MMAL_CAMERA_PORT_COUNT ++}; ++ ++#define PREVIEW_LAYER 2 ++ ++extern int bcm2835_v4l2_debug; ++ ++struct bm2835_mmal_dev { ++ /* v4l2 devices */ ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct mutex mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; ++ enum v4l2_scene_mode scene_mode; ++ struct mmal_colourfx colourfx; ++ int hflip; ++ int vflip; ++ int red_gain; ++ int blue_gain; ++ enum mmal_parameter_exposuremode exposure_mode_user; ++ enum v4l2_exposure_auto_type exposure_mode_v4l2_user; ++ /* active exposure mode may differ if selected via a scene mode */ ++ enum mmal_parameter_exposuremode exposure_mode_active; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++ unsigned int manual_shutter_speed; ++ bool exp_auto_priority; ++ ++ /* allocated mmal instance and components */ ++ struct vchiq_mmal_instance *instance; ++ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; ++ int camera_use_count; ++ ++ struct v4l2_window overlay; ++ ++ struct { ++ unsigned int width; /* width */ ++ unsigned int height; /* height */ ++ unsigned int stride; /* stride */ ++ unsigned int buffersize; /* buffer size with padding */ ++ struct mmal_fmt *fmt; ++ struct v4l2_fract timeperframe; ++ ++ /* H264 encode bitrate */ ++ int encode_bitrate; ++ /* H264 bitrate mode. CBR/VBR */ ++ int encode_bitrate_mode; ++ /* H264 profile */ ++ enum v4l2_mpeg_video_h264_profile enc_profile; ++ /* H264 level */ ++ enum v4l2_mpeg_video_h264_level enc_level; ++ /* JPEG Q-factor */ ++ int q_factor; ++ ++ struct vb2_queue vb_vidq; ++ ++ /* VC start timestamp for streaming */ ++ s64 vc_start_timestamp; ++ /* Kernel start timestamp for streaming */ ++ struct timeval kernel_start_ts; ++ ++ struct vchiq_mmal_port *port; /* port being used for capture */ ++ /* camera port being used for capture */ ++ struct vchiq_mmal_port *camera_port; ++ /* component being used for encode */ ++ struct vchiq_mmal_component *encode_component; ++ /* number of frames remaining which driver should capture */ ++ unsigned int frame_count; ++ /* last frame completion */ ++ struct completion frame_cmplt; ++ ++ } capture; ++ ++}; ++ ++int bm2835_mmal_init_controls( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl); ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); ++int set_framerate_params(struct bm2835_mmal_dev *dev); ++ ++/* Debug helpers */ ++ ++#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ ++{ \ ++ v4l2_dbg(level, debug, dev, \ ++"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ ++ desc == NULL ? "" : desc, \ ++ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ ++ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ ++ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ ++} +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +new file mode 100644 +index 0000000..3017b94 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -0,0 +1,1322 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -4.0 to +4.0. ++ * MMAL values are in 1/6th increments so the MMAL range is -24 to +24. ++ * V4L2 docs say value "is expressed in terms of EV, drivers should interpret ++ * the values as 0.001 EV units, where the value 1000 stands for +1 EV." ++ * V4L2 is limited to a max of 32 values in a menu, so count in 1/3rds from ++ * -4 to +4 ++ */ ++static const s64 ev_bias_qmenu[] = { ++ -4000, -3667, -3333, ++ -3000, -2667, -2333, ++ -2000, -1667, -1333, ++ -1000, -667, -333, ++ 0, 333, 667, ++ 1000, 1333, 1667, ++ 2000, 2333, 2667, ++ 3000, 3333, 3667, ++ 4000 ++}; ++ ++/* Supported ISO values ++ * ISOO = auto ISO ++ */ ++static const s64 iso_qmenu[] = { ++ 0, 100, 200, 400, 800, ++}; ++ ++static const s64 mains_freq_qmenu[] = { ++ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO ++}; ++ ++/* Supported video encode modes */ ++static const s64 bitrate_mode_qmenu[] = { ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ++}; ++ ++enum bm2835_mmal_ctrl_type { ++ MMAL_CONTROL_TYPE_STD, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ MMAL_CONTROL_TYPE_INT_MENU, ++ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ ++}; ++ ++struct bm2835_mmal_v4l2_ctrl; ++ ++typedef int(bm2835_mmal_v4l2_ctrl_cb)( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); ++ ++struct bm2835_mmal_v4l2_ctrl { ++ u32 id; /* v4l2 control identifier */ ++ enum bm2835_mmal_ctrl_type type; ++ /* control minimum value or ++ * mask for MMAL_CONTROL_TYPE_STD_MENU */ ++ s32 min; ++ s32 max; /* maximum value of control */ ++ s32 def; /* default value of control */ ++ s32 step; /* step size of the control */ ++ const s64 *imenu; /* integer menu array */ ++ u32 mmal_id; /* mmal parameter id */ ++ bm2835_mmal_v4l2_ctrl_cb *setter; ++ bool ignore_errors; ++}; ++ ++struct v4l2_to_mmal_effects_setting { ++ u32 v4l2_effect; ++ u32 mmal_effect; ++ s32 col_fx_enable; ++ s32 col_fx_fixed_cbcr; ++ u32 u; ++ u32 v; ++ u32 num_effect_params; ++ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; ++ ++static const struct v4l2_to_mmal_effects_setting ++ v4l2_to_mmal_effects_values[] = { ++ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, ++ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, ++ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, ++ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } ++}; ++ ++struct v4l2_mmal_scene_config { ++ enum v4l2_scene_mode v4l2_scene; ++ enum mmal_parameter_exposuremode exposure_mode; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++}; ++ ++static const struct v4l2_mmal_scene_config scene_configs[] = { ++ /* V4L2_SCENE_MODE_NONE automatically added */ ++ { ++ V4L2_SCENE_MODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE ++ }, ++ { ++ V4L2_SCENE_MODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE ++ }, ++}; ++ ++/* control handlers*/ ++ ++static int ctrl_set_rational(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct mmal_parameter_rational rational_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ rational_value.num = ctrl->val; ++ rational_value.den = 100; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &rational_value, ++ sizeof(rational_value)); ++} ++ ++static int ctrl_set_value(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_value_menu(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ if (ctrl->val > mmal_ctrl->max || ctrl->val < mmal_ctrl->min) ++ return 1; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = mmal_ctrl->imenu[ctrl->val]; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ s32 s32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ s32_value = (ctrl->val-12)*2; /* Convert from index to 1/6ths */ ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &s32_value, sizeof(s32_value)); ++} ++ ++static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ u32_value = ((ctrl->val % 360) / 90) * 90; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++} ++ ++static int ctrl_set_flip(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ if (ctrl->id == V4L2_CID_HFLIP) ++ dev->hflip = ctrl->val; ++ else ++ dev->vflip = ctrl->val; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ if (dev->hflip && dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_BOTH; ++ else if (dev->hflip) ++ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; ++ else if (dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_VERTICAL; ++ else ++ u32_value = MMAL_PARAM_MIRROR_NONE; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++ ++} ++ ++static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode_user; ++ u32 shutter_speed = 0; ++ struct vchiq_mmal_port *control; ++ int ret = 0; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (mmal_ctrl->mmal_id == MMAL_PARAMETER_SHUTTER_SPEED) { ++ /* V4L2 is in 100usec increments. ++ * MMAL is 1usec. ++ */ ++ dev->manual_shutter_speed = ctrl->val * 100; ++ } else if (mmal_ctrl->mmal_id == MMAL_PARAMETER_EXPOSURE_MODE) { ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_AUTO: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_AUTO; ++ break; ++ ++ case V4L2_EXPOSURE_MANUAL: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF; ++ break; ++ } ++ dev->exposure_mode_user = exp_mode; ++ dev->exposure_mode_v4l2_user = ctrl->val; ++ } else if (mmal_ctrl->id == V4L2_CID_EXPOSURE_AUTO_PRIORITY) { ++ dev->exp_auto_priority = ctrl->val; ++ } ++ ++ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { ++ if (exp_mode == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exp_mode, ++ sizeof(u32)); ++ dev->exposure_mode_active = exp_mode; ++ } ++ /* exposure_dynamic_framerate (V4L2_CID_EXPOSURE_AUTO_PRIORITY) should ++ * always apply irrespective of scene mode. ++ */ ++ ret += set_framerate_params(dev); ++ ++ return ret; ++} ++ ++static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_METERING_AVERAGE: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_SPOT: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; ++ break; ++ ++ /* todo matrix weighting not added to Linux API till 3.9 ++ case V4L2_EXPOSURE_METERING_MATRIX: ++ dev->metering_mode = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; ++ break; ++ */ ++ ++ } ++ ++ if (dev->scene_mode == V4L2_SCENE_MODE_NONE) { ++ struct vchiq_mmal_port *control; ++ u32 u32_value = dev->metering_mode; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ } else ++ return 0; ++} ++ ++static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ u32_value = MMAL_PARAM_FLICKERAVOID_OFF; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_50HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_60HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ u32_value = MMAL_PARAM_FLICKERAVOID_AUTO; ++ break; ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_WHITE_BALANCE_MANUAL: ++ u32_value = MMAL_PARAM_AWBMODE_OFF; ++ break; ++ ++ case V4L2_WHITE_BALANCE_AUTO: ++ u32_value = MMAL_PARAM_AWBMODE_AUTO; ++ break; ++ ++ case V4L2_WHITE_BALANCE_INCANDESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT_H: ++ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; ++ break; ++ ++ case V4L2_WHITE_BALANCE_HORIZON: ++ u32_value = MMAL_PARAM_AWBMODE_HORIZON; ++ break; ++ ++ case V4L2_WHITE_BALANCE_DAYLIGHT: ++ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLASH: ++ u32_value = MMAL_PARAM_AWBMODE_FLASH; ++ break; ++ ++ case V4L2_WHITE_BALANCE_CLOUDY: ++ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; ++ break; ++ ++ case V4L2_WHITE_BALANCE_SHADE: ++ u32_value = MMAL_PARAM_AWBMODE_SHADE; ++ break; ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_gains(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_awbgains gains; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (ctrl->id == V4L2_CID_RED_BALANCE) ++ dev->red_gain = ctrl->val; ++ else if (ctrl->id == V4L2_CID_BLUE_BALANCE) ++ dev->blue_gain = ctrl->val; ++ ++ gains.r_gain.num = dev->red_gain; ++ gains.b_gain.num = dev->blue_gain; ++ gains.r_gain.den = gains.b_gain.den = 1000; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &gains, sizeof(gains)); ++} ++ ++static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ int i, j; ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_imagefx_parameters imagefx; ++ ++ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { ++ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { ++ ++ imagefx.effect = ++ v4l2_to_mmal_effects_values[i].mmal_effect; ++ imagefx.num_effect_params = ++ v4l2_to_mmal_effects_values[i].num_effect_params; ++ ++ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) ++ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; ++ ++ for (j = 0; j < imagefx.num_effect_params; j++) ++ imagefx.effect_parameter[j] = ++ v4l2_to_mmal_effects_values[i].effect_params[j]; ++ ++ dev->colourfx.enable = ++ v4l2_to_mmal_effects_values[i].col_fx_enable; ++ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { ++ dev->colourfx.u = ++ v4l2_to_mmal_effects_values[i].u; ++ dev->colourfx.v = ++ v4l2_to_mmal_effects_values[i].v; ++ } ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ &imagefx, sizeof(imagefx)); ++ if (ret) ++ goto exit; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ } ++ } ++ ++exit: ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, ++ dev->colourfx.enable ? "true" : "false", ++ dev->colourfx.u, dev->colourfx.v, ++ ret, (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; ++ dev->colourfx.enable = ctrl->val & 0xff; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", ++ __func__, mmal_ctrl, ctrl->id, ctrl->val, ret, ++ (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ struct vchiq_mmal_port *encoder_out; ++ ++ dev->capture.encode_bitrate = ctrl->val; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &ctrl->val, sizeof(ctrl->val)); ++ ret = 0; ++ return ret; ++} ++ ++static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 bitrate_mode; ++ struct vchiq_mmal_port *encoder_out; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ dev->capture.encode_bitrate_mode = ctrl->val; ++ switch (ctrl->val) { ++ default: ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; ++ break; ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; ++ break; ++ } ++ ++ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &bitrate_mode, ++ sizeof(bitrate_mode)); ++ return 0; ++} ++ ++static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *jpeg_out; ++ ++ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_video_encode_param_output(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *vid_enc_ctl; ++ ++ vid_enc_ctl = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, vid_enc_ctl, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_video_encode_profile_level(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct mmal_parameter_video_profile param; ++ int ret = 0; ++ ++ if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_PROFILE) { ++ switch (ctrl->val) { ++ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: ++ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: ++ dev->capture.enc_profile = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ } else if (ctrl->id == V4L2_CID_MPEG_VIDEO_H264_LEVEL) { ++ switch (ctrl->val) { ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: ++ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: ++ dev->capture.enc_level = ctrl->val; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ } ++ ++ if (!ret) { ++ switch (dev->capture.enc_profile) { ++ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE: ++ param.profile = MMAL_VIDEO_PROFILE_H264_BASELINE; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: ++ param.profile = ++ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: ++ param.profile = MMAL_VIDEO_PROFILE_H264_MAIN; ++ break; ++ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH: ++ param.profile = MMAL_VIDEO_PROFILE_H264_HIGH; ++ break; ++ default: ++ /* Should never get here */ ++ break; ++ } ++ ++ switch (dev->capture.enc_level) { ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_1; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1B: ++ param.level = MMAL_VIDEO_LEVEL_H264_1b; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_11; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_12; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_1_3: ++ param.level = MMAL_VIDEO_LEVEL_H264_13; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_2; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_21; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_2_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_22; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_3; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_1: ++ param.level = MMAL_VIDEO_LEVEL_H264_31; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_3_2: ++ param.level = MMAL_VIDEO_LEVEL_H264_32; ++ break; ++ case V4L2_MPEG_VIDEO_H264_LEVEL_4_0: ++ param.level = MMAL_VIDEO_LEVEL_H264_4; ++ break; ++ default: ++ /* Should never get here */ ++ break; ++ } ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0], ++ mmal_ctrl->mmal_id, ++ ¶m, sizeof(param)); ++ } ++ return ret; ++} ++ ++static int ctrl_set_scene_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = 0; ++ int shutter_speed; ++ struct vchiq_mmal_port *control; ++ ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "scene mode selected %d, was %d\n", ctrl->val, ++ dev->scene_mode); ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ if (ctrl->val == dev->scene_mode) ++ return 0; ++ ++ if (ctrl->val == V4L2_SCENE_MODE_NONE) { ++ /* Restore all user selections */ ++ dev->scene_mode = V4L2_SCENE_MODE_NONE; ++ ++ if (dev->exposure_mode_user == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ else ++ shutter_speed = 0; ++ ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", ++ __func__, shutter_speed, dev->exposure_mode_user, ++ dev->metering_mode); ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &dev->exposure_mode_user, ++ sizeof(u32)); ++ dev->exposure_mode_active = dev->exposure_mode_user; ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &dev->metering_mode, ++ sizeof(u32)); ++ ret += set_framerate_params(dev); ++ } else { ++ /* Set up scene mode */ ++ int i; ++ const struct v4l2_mmal_scene_config *scene = NULL; ++ int shutter_speed; ++ enum mmal_parameter_exposuremode exposure_mode; ++ enum mmal_parameter_exposuremeteringmode metering_mode; ++ ++ for (i = 0; i < ARRAY_SIZE(scene_configs); i++) { ++ if (scene_configs[i].v4l2_scene == ++ ctrl->val) { ++ scene = &scene_configs[i]; ++ break; ++ } ++ } ++ if (i >= ARRAY_SIZE(scene_configs)) ++ return -EINVAL; ++ ++ /* Set all the values */ ++ dev->scene_mode = ctrl->val; ++ ++ if (scene->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ else ++ shutter_speed = 0; ++ exposure_mode = scene->exposure_mode; ++ metering_mode = scene->metering_mode; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: scene mode none: shut_speed %d, exp_mode %d, metering %d\n", ++ __func__, shutter_speed, exposure_mode, metering_mode); ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, ++ sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exposure_mode, ++ sizeof(u32)); ++ dev->exposure_mode_active = exposure_mode; ++ ret += vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exposure_mode, ++ sizeof(u32)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &metering_mode, ++ sizeof(u32)); ++ ret += set_framerate_params(dev); ++ } ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: Setting scene to %d, ret=%d\n", ++ __func__, ctrl->val, ret); ++ ret = -EINVAL; ++ } ++ return 0; ++} ++ ++static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct bm2835_mmal_dev *dev = ++ container_of(ctrl->handler, struct bm2835_mmal_dev, ++ ctrl_handler); ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; ++ int ret; ++ ++ if ((mmal_ctrl == NULL) || ++ (mmal_ctrl->id != ctrl->id) || ++ (mmal_ctrl->setter == NULL)) { ++ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); ++ return -EINVAL; ++ } ++ ++ ret = mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++ if (ret) ++ pr_warn("ctrl id:%d/MMAL param %08X- returned ret %d\n", ++ ctrl->id, mmal_ctrl->mmal_id, ret); ++ if (mmal_ctrl->ignore_errors) ++ ret = 0; ++ return ret; ++} ++ ++static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { ++ .s_ctrl = bm2835_mmal_s_ctrl, ++}; ++ ++ ++ ++static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { ++ { ++ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SATURATION, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SHARPNESS, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_CONTRAST, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, ++ 0, 100, 50, 1, NULL, ++ MMAL_PARAMETER_BRIGHTNESS, ++ &ctrl_set_rational, ++ false ++ }, ++ { ++ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, ++ MMAL_PARAMETER_ISO, ++ &ctrl_set_value_menu, ++ false ++ }, ++ { ++ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_STABILISATION, ++ &ctrl_set_value, ++ false ++ }, ++/* { ++ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL ++ }, */ ++ { ++ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &ctrl_set_exposure, ++ false ++ }, ++/* todo this needs mixing in with set exposure ++ { ++ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ }, ++ */ ++ { ++ V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, ++ /* Units of 100usecs */ ++ 1, 1*1000*10, 100*10, 1, NULL, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &ctrl_set_exposure, ++ false ++ }, ++ { ++ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, ++ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, ++ MMAL_PARAMETER_EXPOSURE_COMP, ++ &ctrl_set_value_ev, ++ false ++ }, ++ { ++ V4L2_CID_EXPOSURE_AUTO_PRIORITY, MMAL_CONTROL_TYPE_STD, ++ 0, 1, ++ 0, 1, NULL, ++ 0, /* Dummy MMAL ID as it gets mapped into FPS range*/ ++ &ctrl_set_exposure, ++ false ++ }, ++ { ++ V4L2_CID_EXPOSURE_METERING, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &ctrl_set_metering_mode, ++ false ++ }, ++ { ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x3ff, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, ++ MMAL_PARAMETER_AWB_MODE, ++ &ctrl_set_awb_mode, ++ false ++ }, ++ { ++ V4L2_CID_RED_BALANCE, MMAL_CONTROL_TYPE_STD, ++ 1, 7999, 1000, 1, NULL, ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ &ctrl_set_awb_gains, ++ false ++ }, ++ { ++ V4L2_CID_BLUE_BALANCE, MMAL_CONTROL_TYPE_STD, ++ 1, 7999, 1000, 1, NULL, ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, ++ &ctrl_set_awb_gains, ++ false ++ }, ++ { ++ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, 15, V4L2_COLORFX_NONE, 0, NULL, ++ MMAL_PARAMETER_IMAGE_EFFECT, ++ &ctrl_set_image_effect, ++ false ++ }, ++ { ++ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, ++ 0, 0xffff, 0x8080, 1, NULL, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &ctrl_set_colfx, ++ false ++ }, ++ { ++ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, ++ 0, 360, 0, 90, NULL, ++ MMAL_PARAMETER_ROTATION, ++ &ctrl_set_rotate, ++ false ++ }, ++ { ++ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false ++ }, ++ { ++ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, ++ 0, 0, bitrate_mode_qmenu, ++ MMAL_PARAMETER_RATECONTROL, ++ &ctrl_set_bitrate_mode, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, ++ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ &ctrl_set_bitrate, ++ false ++ }, ++ { ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, ++ 1, 100, ++ 30, 1, NULL, ++ MMAL_PARAMETER_JPEG_Q_FACTOR, ++ &ctrl_set_image_encode_output, ++ false ++ }, ++ { ++ V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(mains_freq_qmenu) - 1, ++ 1, 1, NULL, ++ MMAL_PARAMETER_FLICKER_AVOID, ++ &ctrl_set_flicker_avoidance, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, MMAL_CONTROL_TYPE_STD, ++ 0, 1, ++ 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, ++ &ctrl_set_video_encode_param_output, ++ true /* Errors ignored as requires latest firmware to work */ ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_H264_PROFILE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~((1<ctrls[c]) && (v4l2_ctrls[c].setter)) { ++ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], ++ &v4l2_ctrls[c]); ++ if (!v4l2_ctrls[c].ignore_errors && ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Failed when setting default values for ctrl %d\n", ++ c); ++ break; ++ } ++ } ++ } ++ return ret; ++} ++ ++int set_framerate_params(struct bm2835_mmal_dev *dev) ++{ ++ struct mmal_parameter_fps_range fps_range; ++ int ret; ++ ++ if ((dev->exposure_mode_active != MMAL_PARAM_EXPOSUREMODE_OFF) && ++ (dev->exp_auto_priority)) { ++ /* Variable FPS. Define min FPS as 1fps. ++ * Max as max defined FPS. ++ */ ++ fps_range.fps_low.num = 1; ++ fps_range.fps_low.den = 1; ++ fps_range.fps_high.num = dev->capture.timeperframe.denominator; ++ fps_range.fps_high.den = dev->capture.timeperframe.numerator; ++ } else { ++ /* Fixed FPS - set min and max to be the same */ ++ fps_range.fps_low.num = fps_range.fps_high.num = ++ dev->capture.timeperframe.denominator; ++ fps_range.fps_low.den = fps_range.fps_high.den = ++ dev->capture.timeperframe.numerator; ++ } ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Set fps range to %d/%d to %d/%d\n", ++ fps_range.fps_low.num, ++ fps_range.fps_low.den, ++ fps_range.fps_high.num, ++ fps_range.fps_high.den ++ ); ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE], ++ MMAL_PARAMETER_FPS_RANGE, ++ &fps_range, sizeof(fps_range)); ++ if (ret) ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Failed to set fps ret %d\n", ++ ret); ++ ++ return ret; ++ ++} ++ ++int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl) ++{ ++ int c; ++ const struct bm2835_mmal_v4l2_ctrl *ctrl; ++ ++ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_STD: ++ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->min, ctrl->max, ctrl->step, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ { ++ int mask = ctrl->min; ++ ++ if (ctrl->id == V4L2_CID_SCENE_MODE) { ++ /* Special handling to work out the mask ++ * value based on the scene_configs array ++ * at runtime. Reduces the chance of ++ * mismatches. ++ */ ++ int i; ++ mask = 1<ctrls[c] = v4l2_ctrl_new_std_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, mask, ctrl->def); ++ break; ++ } ++ ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->def, ctrl->imenu); ++ break; ++ ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ /* skip this entry when constructing controls */ ++ continue; ++ } ++ ++ if (hdl->error) ++ break; ++ ++ dev->ctrls[c]->priv = (void *)ctrl; ++ } ++ ++ if (hdl->error) { ++ pr_err("error adding control %d/%d id 0x%x\n", c, ++ V4L2_CTRL_COUNT, ctrl->id); ++ return hdl->error; ++ } ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ v4l2_ctrl_auto_cluster(ctrl->min, ++ &dev->ctrls[c+1], ++ ctrl->max, ++ ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD: ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ break; ++ } ++ ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h +new file mode 100644 +index 0000000..35698c8 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-common.h +@@ -0,0 +1,52 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL structures ++ * ++ */ ++ ++#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) ++#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') ++ ++/** Special value signalling that time is not known */ ++#define MMAL_TIME_UNKNOWN (1LL<<63) ++ ++/* mapping between v4l and mmal video modes */ ++struct mmal_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ int flags; /* v4l2 flags field */ ++ u32 mmal; ++ int depth; ++ u32 mmal_component; /* MMAL component index to be used to encode */ ++}; ++ ++/* buffer for one video frame */ ++struct mmal_buffer { ++ /* v4l buffer data -- must be first */ ++ struct vb2_buffer vb; ++ ++ /* list of buffers available */ ++ struct list_head list; ++ ++ void *buffer; /* buffer pointer */ ++ unsigned long buffer_size; /* size of allocated buffer */ ++}; ++ ++/* */ ++struct mmal_colourfx { ++ s32 enable; ++ u32 u; ++ u32 v; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h +new file mode 100644 +index 0000000..024d620 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-encodings.h +@@ -0,0 +1,127 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++#ifndef MMAL_ENCODINGS_H ++#define MMAL_ENCODINGS_H ++ ++#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') ++#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') ++#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') ++#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') ++#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') ++#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') ++#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') ++#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') ++#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') ++#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') ++#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') ++#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') ++#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') ++#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') ++#define MMAL_ENCODING_MJPEG MMAL_FOURCC('M', 'J', 'P', 'G') ++ ++#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') ++#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') ++#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') ++#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') ++#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') ++#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') ++ ++#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') ++#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') ++#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') ++#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') ++#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') ++#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') ++#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') ++#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') ++#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') ++#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') ++#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') ++#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') ++#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') ++#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') ++#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') ++#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') ++#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') ++#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') ++#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') ++#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') ++#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') ++ ++/** SAND Video (YUVUV128) format, native format understood by VideoCore. ++ * This format is *not* opaque - if requested you will receive full frames ++ * of YUV_UV video. ++ */ ++#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') ++ ++/** VideoCore opaque image format, image handles are returned to ++ * the host but not the actual image data. ++ */ ++#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') ++ ++/** An EGL image handle ++ */ ++#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') ++ ++/* }@ */ ++ ++/** \name Pre-defined audio encodings */ ++/* @{ */ ++#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') ++#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') ++#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') ++#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') ++#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') ++#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') ++ ++/* Pre-defined H264 encoding variants */ ++ ++/** ISO 14496-10 Annex B byte stream format */ ++#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 ++/** ISO 14496-15 AVC stream format */ ++#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') ++/** Implicitly delineated NAL units without emulation prevention */ ++#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') ++ ++ ++/** \defgroup MmalColorSpace List of pre-defined video color spaces ++ * This defines a list of common color spaces. This list isn't exhaustive and ++ * is only provided as a convenience to avoid clients having to use FourCC ++ * codes directly. However components are allowed to define and use their own ++ * FourCC codes. ++ */ ++/* @{ */ ++ ++/** Unknown color space */ ++#define MMAL_COLOR_SPACE_UNKNOWN 0 ++/** ITU-R BT.601-5 [SDTV] */ ++#define MMAL_COLOR_SPACE_ITUR_BT601 MMAL_FOURCC('Y', '6', '0', '1') ++/** ITU-R BT.709-3 [HDTV] */ ++#define MMAL_COLOR_SPACE_ITUR_BT709 MMAL_FOURCC('Y', '7', '0', '9') ++/** JPEG JFIF */ ++#define MMAL_COLOR_SPACE_JPEG_JFIF MMAL_FOURCC('Y', 'J', 'F', 'I') ++/** Title 47 Code of Federal Regulations (2003) 73.682 (a) (20) */ ++#define MMAL_COLOR_SPACE_FCC MMAL_FOURCC('Y', 'F', 'C', 'C') ++/** Society of Motion Picture and Television Engineers 240M (1999) */ ++#define MMAL_COLOR_SPACE_SMPTE240M MMAL_FOURCC('Y', '2', '4', '0') ++/** ITU-R BT.470-2 System M */ ++#define MMAL_COLOR_SPACE_BT470_2_M MMAL_FOURCC('Y', '_', '_', 'M') ++/** ITU-R BT.470-2 System BG */ ++#define MMAL_COLOR_SPACE_BT470_2_BG MMAL_FOURCC('Y', '_', 'B', 'G') ++/** JPEG JFIF, but with 16..255 luma */ ++#define MMAL_COLOR_SPACE_JFIF_Y16_255 MMAL_FOURCC('Y', 'Y', '1', '6') ++/* @} MmalColorSpace List */ ++ ++#endif /* MMAL_ENCODINGS_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h +new file mode 100644 +index 0000000..66e8a6e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-common.h +@@ -0,0 +1,50 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_COMMON_H ++#define MMAL_MSG_COMMON_H ++ ++enum mmal_msg_status { ++ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ ++ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ ++ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ ++ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ ++ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ ++ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ ++ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ ++ MMAL_MSG_STATUS_EIO, /**< I/O error */ ++ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ ++ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ ++ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ ++ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ ++ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ ++ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ ++ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ ++ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ ++}; ++ ++struct mmal_rect { ++ s32 x; /**< x coordinate (from left) */ ++ s32 y; /**< y coordinate (from top) */ ++ s32 width; /**< width */ ++ s32 height; /**< height */ ++}; ++ ++struct mmal_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++#endif /* MMAL_MSG_COMMON_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h +new file mode 100644 +index 0000000..123d86e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-format.h +@@ -0,0 +1,81 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_FORMAT_H ++#define MMAL_MSG_FORMAT_H ++ ++#include "mmal-msg-common.h" ++ ++/* MMAL_ES_FORMAT_T */ ++ ++ ++struct mmal_audio_format { ++ u32 channels; /**< Number of audio channels */ ++ u32 sample_rate; /**< Sample rate */ ++ ++ u32 bits_per_sample; /**< Bits per sample */ ++ u32 block_align; /**< Size of a block of data */ ++}; ++ ++struct mmal_video_format { ++ u32 width; /**< Width of frame in pixels */ ++ u32 height; /**< Height of frame in rows of pixels */ ++ struct mmal_rect crop; /**< Visible region of the frame */ ++ struct mmal_rational frame_rate; /**< Frame rate */ ++ struct mmal_rational par; /**< Pixel aspect ratio */ ++ ++ /* FourCC specifying the color space of the video stream. See the ++ * \ref MmalColorSpace "pre-defined color spaces" for some examples. ++ */ ++ u32 color_space; ++}; ++ ++struct mmal_subpicture_format { ++ u32 x_offset; ++ u32 y_offset; ++}; ++ ++union mmal_es_specific_format { ++ struct mmal_audio_format audio; ++ struct mmal_video_format video; ++ struct mmal_subpicture_format subpicture; ++}; ++ ++/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ ++struct mmal_es_format { ++ u32 type; /* enum mmal_es_type */ ++ ++ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ ++ u32 encoding_variant; /* FourCC specifying the specific ++ * encoding variant of the elementary ++ * stream. ++ */ ++ ++ union mmal_es_specific_format *es; /* TODO: pointers in ++ * message serialisation?!? ++ */ ++ /* Type specific ++ * information for the ++ * elementary stream ++ */ ++ ++ u32 bitrate; /**< Bitrate in bits per second */ ++ u32 flags; /**< Flags describing properties of the elementary stream. */ ++ ++ u32 extradata_size; /**< Size of the codec specific data */ ++ u8 *extradata; /**< Codec specific data */ ++}; ++ ++#endif /* MMAL_MSG_FORMAT_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h +new file mode 100644 +index 0000000..a55c1ea +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-port.h +@@ -0,0 +1,107 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* MMAL_PORT_TYPE_T */ ++enum mmal_port_type { ++ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ ++ MMAL_PORT_TYPE_CONTROL, /**< Control port */ ++ MMAL_PORT_TYPE_INPUT, /**< Input port */ ++ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ ++ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ ++}; ++ ++/** The port is pass-through and doesn't need buffer headers allocated */ ++#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 ++/** The port wants to allocate the buffer payloads. ++ * This signals a preference that payload allocation should be done ++ * on this port for efficiency reasons. */ ++#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 ++/** The port supports format change events. ++ * This applies to input ports and is used to let the client know ++ * whether the port supports being reconfigured via a format ++ * change event (i.e. without having to disable the port). */ ++#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 ++ ++/* mmal port structure (MMAL_PORT_T) ++ * ++ * most elements are informational only, the pointer values for ++ * interogation messages are generally provided as additional ++ * strucures within the message. When used to set values only teh ++ * buffer_num, buffer_size and userdata parameters are writable. ++ */ ++struct mmal_port { ++ void *priv; /* Private member used by the framework */ ++ const char *name; /* Port name. Used for debugging purposes (RO) */ ++ ++ u32 type; /* Type of the port (RO) enum mmal_port_type */ ++ u16 index; /* Index of the port in its type list (RO) */ ++ u16 index_all; /* Index of the port in the list of all ports (RO) */ ++ ++ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ ++ struct mmal_es_format *format; /* Format of the elementary stream */ ++ ++ u32 buffer_num_min; /* Minimum number of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_size_min; /* Minimum size of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_alignment_min; /* Minimum alignment requirement for ++ * the buffers (RO). A value of ++ * zero means no special alignment ++ * requirements. This is set by the ++ * component. ++ */ ++ ++ u32 buffer_num_recommended; /* Number of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_size_recommended; /* Size of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_num; /* Actual number of buffers the port will use. ++ * This is set by the client. ++ */ ++ ++ u32 buffer_size; /* Actual maximum size of the buffers that ++ * will be sent to the port. This is set by ++ * the client. ++ */ ++ ++ void *component; /* Component this port belongs to (Read Only) */ ++ ++ void *userdata; /* Field reserved for use by the client */ ++ ++ u32 capabilities; /* Flags describing the capabilities of a ++ * port (RO). Bitwise combination of \ref ++ * portcapabilities "Port capabilities" ++ * values. ++ */ ++ ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h +new file mode 100644 +index 0000000..67b1076 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg.h +@@ -0,0 +1,404 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* all the data structures which serialise the MMAL protocol. note ++ * these are directly mapped onto the recived message data. ++ * ++ * BEWARE: They seem to *assume* pointers are u32 and that there is no ++ * structure padding! ++ * ++ * NOTE: this implementation uses kernel types to ensure sizes. Rather ++ * than assigning values to enums to force their size the ++ * implementation uses fixed size types and not the enums (though the ++ * comments have the actual enum type ++ */ ++ ++#define VC_MMAL_VER 15 ++#define VC_MMAL_MIN_VER 10 ++#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") ++ ++/* max total message size is 512 bytes */ ++#define MMAL_MSG_MAX_SIZE 512 ++/* with six 32bit header elements max payload is therefore 488 bytes */ ++#define MMAL_MSG_MAX_PAYLOAD 488 ++ ++#include "mmal-msg-common.h" ++#include "mmal-msg-format.h" ++#include "mmal-msg-port.h" ++ ++enum mmal_msg_type { ++ MMAL_MSG_TYPE_QUIT = 1, ++ MMAL_MSG_TYPE_SERVICE_CLOSED, ++ MMAL_MSG_TYPE_GET_VERSION, ++ MMAL_MSG_TYPE_COMPONENT_CREATE, ++ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ ++ MMAL_MSG_TYPE_COMPONENT_ENABLE, ++ MMAL_MSG_TYPE_COMPONENT_DISABLE, ++ MMAL_MSG_TYPE_PORT_INFO_GET, ++ MMAL_MSG_TYPE_PORT_INFO_SET, ++ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST, ++ MMAL_MSG_TYPE_BUFFER_TO_HOST, ++ MMAL_MSG_TYPE_GET_STATS, ++ MMAL_MSG_TYPE_PORT_PARAMETER_SET, ++ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ ++ MMAL_MSG_TYPE_EVENT_TO_HOST, ++ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, ++ MMAL_MSG_TYPE_CONSUME_MEM, ++ MMAL_MSG_TYPE_LMK, /* 20 */ ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, ++ MMAL_MSG_TYPE_DRM_GET_LHS32, ++ MMAL_MSG_TYPE_DRM_GET_TIME, ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, ++ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ ++ MMAL_MSG_TYPE_HOST_LOG, ++ MMAL_MSG_TYPE_MSG_LAST ++}; ++ ++/* port action request messages differ depending on the action type */ ++enum mmal_msg_port_action_type { ++ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ ++}; ++ ++struct mmal_msg_header { ++ u32 magic; ++ u32 type; /** enum mmal_msg_type */ ++ ++ /* Opaque handle to the control service */ ++ struct mmal_control_service *control_service; ++ ++ struct mmal_msg_context *context; /** a u32 per message context */ ++ u32 status; /** The status of the vchiq operation */ ++ u32 padding; ++}; ++ ++/* Send from VC to host to report version */ ++struct mmal_msg_version { ++ u32 flags; ++ u32 major; ++ u32 minor; ++ u32 minimum; ++}; ++ ++/* request to VC to create component */ ++struct mmal_msg_component_create { ++ void *client_component; /* component context */ ++ char name[128]; ++ u32 pid; /* For debug */ ++}; ++ ++/* reply from VC to component creation request */ ++struct mmal_msg_component_create_reply { ++ u32 status; /** enum mmal_msg_status - how does this differ to ++ * the one in the header? ++ */ ++ u32 component_handle; /* VideoCore handle for component */ ++ u32 input_num; /* Number of input ports */ ++ u32 output_num; /* Number of output ports */ ++ u32 clock_num; /* Number of clock ports */ ++}; ++ ++/* request to VC to destroy a component */ ++struct mmal_msg_component_destroy { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_destroy_reply { ++ u32 status; /** The component destruction status */ ++}; ++ ++ ++/* request and reply to VC to enable a component */ ++struct mmal_msg_component_enable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_enable_reply { ++ u32 status; /** The component enable status */ ++}; ++ ++ ++/* request and reply to VC to disable a component */ ++struct mmal_msg_component_disable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_disable_reply { ++ u32 status; /** The component disable status */ ++}; ++ ++/* request to VC to get port information */ ++struct mmal_msg_port_info_get { ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port index to query */ ++}; ++ ++/* reply from VC to get port info request */ ++struct mmal_msg_port_info_get_reply { ++ u32 status; /** enum mmal_msg_status */ ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; /* elementry stream format */ ++ union mmal_es_specific_format es; /* es type specific data */ ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ ++}; ++ ++/* request to VC to set port information */ ++struct mmal_msg_port_info_set { ++ u32 component_handle; ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++/* reply from VC to port info set request */ ++struct mmal_msg_port_info_set_reply { ++ u32 status; ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++ ++/* port action requests that take a mmal_port as a parameter */ ++struct mmal_msg_port_action_port { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ struct mmal_port port; ++}; ++ ++/* port action requests that take handles as a parameter */ ++struct mmal_msg_port_action_handle { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ u32 connect_component_handle; ++ u32 connect_port_handle; ++}; ++ ++struct mmal_msg_port_action_reply { ++ u32 status; /** The port action operation status */ ++}; ++ ++ ++ ++ ++/* MMAL buffer transfer */ ++ ++/** Size of space reserved in a buffer message for short messages. */ ++#define MMAL_VC_SHORT_DATA 128 ++ ++/** Signals that the current payload is the end of the stream of data */ ++#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) ++/** Signals that the start of the current payload starts a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) ++/** Signals that the end of the current payload ends a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) ++/** Signals that the current payload contains only complete frames (>1) */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME \ ++ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) ++/** Signals that the current payload is a keyframe (i.e. self decodable) */ ++#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) ++/** Signals a discontinuity in the stream of data (e.g. after a seek). ++ * Can be used for instance by a decoder to reset its state */ ++#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) ++/** Signals a buffer containing some kind of config data for the component ++ * (e.g. codec config data) */ ++#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) ++/** Signals an encrypted payload */ ++#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) ++/** Signals a buffer containing side information */ ++#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) ++/** Signals a buffer which is the snapshot/postview image from a stills ++ * capture ++ */ ++#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) ++/** Signals a buffer which contains data known to be corrupted */ ++#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) ++/** Signals that a buffer failed to be transmitted */ ++#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) ++ ++struct mmal_driver_buffer { ++ u32 magic; ++ u32 component_handle; ++ u32 port_handle; ++ void *client_context; ++}; ++ ++/* buffer header */ ++struct mmal_buffer_header { ++ struct mmal_buffer_header *next; /* next header */ ++ void *priv; /* framework private data */ ++ u32 cmd; ++ void *data; ++ u32 alloc_size; ++ u32 length; ++ u32 offset; ++ u32 flags; ++ s64 pts; ++ s64 dts; ++ void *type; ++ void *user_data; ++}; ++ ++struct mmal_buffer_header_type_specific { ++ union { ++ struct { ++ u32 planes; ++ u32 offset[4]; ++ u32 pitch[4]; ++ u32 flags; ++ } video; ++ } u; ++}; ++ ++struct mmal_msg_buffer_from_host { ++ /* The front 32 bytes of the buffer header are copied ++ * back to us in the reply to allow for context. This ++ * area is used to store two mmal_driver_buffer structures to ++ * allow for multiple concurrent service users. ++ */ ++ /* control data */ ++ struct mmal_driver_buffer drvbuf; ++ ++ /* referenced control data for passthrough buffer management */ ++ struct mmal_driver_buffer drvbuf_ref; ++ struct mmal_buffer_header buffer_header; /* buffer header itself */ ++ struct mmal_buffer_header_type_specific buffer_header_type_specific; ++ s32 is_zero_copy; ++ s32 has_reference; ++ ++ /** allows short data to be xfered in control message */ ++ u32 payload_in_message; ++ u8 short_data[MMAL_VC_SHORT_DATA]; ++}; ++ ++ ++/* port parameter setting */ ++ ++#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 ++ ++struct mmal_msg_port_parameter_set { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++struct mmal_msg_port_parameter_set_reply { ++ u32 status; /** enum mmal_msg_status todo: how does this ++ * differ to the one in the header? ++ */ ++}; ++ ++/* port parameter getting */ ++ ++struct mmal_msg_port_parameter_get { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++}; ++ ++struct mmal_msg_port_parameter_get_reply { ++ u32 status; /* Status of mmal_port_parameter_get call */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++/* event messages */ ++#define MMAL_WORKER_EVENT_SPACE 256 ++ ++struct mmal_msg_event_to_host { ++ void *client_component; /* component context */ ++ ++ u32 port_type; ++ u32 port_num; ++ ++ u32 cmd; ++ u32 length; ++ u8 data[MMAL_WORKER_EVENT_SPACE]; ++ struct mmal_buffer_header *delayed_buffer; ++}; ++ ++/* all mmal messages are serialised through this structure */ ++struct mmal_msg { ++ /* header */ ++ struct mmal_msg_header h; ++ /* payload */ ++ union { ++ struct mmal_msg_version version; ++ ++ struct mmal_msg_component_create component_create; ++ struct mmal_msg_component_create_reply component_create_reply; ++ ++ struct mmal_msg_component_destroy component_destroy; ++ struct mmal_msg_component_destroy_reply component_destroy_reply; ++ ++ struct mmal_msg_component_enable component_enable; ++ struct mmal_msg_component_enable_reply component_enable_reply; ++ ++ struct mmal_msg_component_disable component_disable; ++ struct mmal_msg_component_disable_reply component_disable_reply; ++ ++ struct mmal_msg_port_info_get port_info_get; ++ struct mmal_msg_port_info_get_reply port_info_get_reply; ++ ++ struct mmal_msg_port_info_set port_info_set; ++ struct mmal_msg_port_info_set_reply port_info_set_reply; ++ ++ struct mmal_msg_port_action_port port_action_port; ++ struct mmal_msg_port_action_handle port_action_handle; ++ struct mmal_msg_port_action_reply port_action_reply; ++ ++ struct mmal_msg_buffer_from_host buffer_from_host; ++ ++ struct mmal_msg_port_parameter_set port_parameter_set; ++ struct mmal_msg_port_parameter_set_reply ++ port_parameter_set_reply; ++ struct mmal_msg_port_parameter_get ++ port_parameter_get; ++ struct mmal_msg_port_parameter_get_reply ++ port_parameter_get_reply; ++ ++ struct mmal_msg_event_to_host event_to_host; ++ ++ u8 payload[MMAL_MSG_MAX_PAYLOAD]; ++ } u; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +new file mode 100644 +index 0000000..aa0fd18 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -0,0 +1,656 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* common parameters */ ++ ++/** @name Parameter groups ++ * Parameters are divided into groups, and then allocated sequentially within ++ * a group using an enum. ++ * @{ ++ */ ++ ++/** Common parameter ID group, used with many types of component. */ ++#define MMAL_PARAMETER_GROUP_COMMON (0<<16) ++/** Camera-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) ++/** Video-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) ++/** Audio-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) ++/** Clock-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) ++/** Miracast-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) ++ ++/* Common parameters */ ++enum mmal_parameter_common_type { ++ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ ++ = MMAL_PARAMETER_GROUP_COMMON, ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ ++ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ ++ ++ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ ++ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, ++ ++ /** MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_COPY, ++ ++ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ ++ MMAL_PARAMETER_BUFFER_REQUIREMENTS, ++ ++ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ ++ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ ++ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ ++ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ ++ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ ++ MMAL_PARAMETER_SYSTEM_TIME, /**< MMAL_PARAMETER_UINT64_T */ ++ MMAL_PARAMETER_NO_IMAGE_PADDING /**< MMAL_PARAMETER_BOOLEAN_T */ ++}; ++ ++/* camera parameters */ ++ ++enum mmal_parameter_camera_type { ++ /* 0 */ ++ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ ++ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION ++ = MMAL_PARAMETER_GROUP_CAMERA, ++ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ ++ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ ++ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ ++ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ ++ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ ++ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ ++ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ ++ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ ++ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ ++ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ ++ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ ++ ++ /* 0x10 */ ++ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ ++ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ ++ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ ++ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ ++ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ ++ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ ++ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ ++ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ ++ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ ++ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /* 0x20 */ ++ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ ++ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ ++ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ ++ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ ++ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ ++ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ ++ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ ++ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ ++ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ ++ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ ++ /* 0x30 */ ++ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_MIN_ISO, ++ ++ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ ++ MMAL_PARAMETER_CAMERA_USE_CASE, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_STATS_PASS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ENABLE_REGISTER_FILE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ ++ MMAL_PARAMETER_CONFIGFILE_REGISTERS, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ ++ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, ++ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ ++ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ ++ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ ++ /* 0x40 */ ++ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SHUTTER_SPEED, /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CUSTOM_AWB_GAINS, /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ ++}; ++ ++struct mmal_parameter_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++enum mmal_parameter_camera_config_timestamp_mode { ++ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ ++ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value ++ * for the frame timestamp ++ */ ++ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp ++ * but subtract the ++ * timestamp of the first ++ * frame sent to give a ++ * zero based timestamp. ++ */ ++}; ++ ++struct mmal_parameter_fps_range { ++ /**< Low end of the permitted framerate range */ ++ struct mmal_parameter_rational fps_low; ++ /**< High end of the permitted framerate range */ ++ struct mmal_parameter_rational fps_high; ++}; ++ ++ ++/* camera configuration parameter */ ++struct mmal_parameter_camera_config { ++ /* Parameters for setting up the image pools */ ++ u32 max_stills_w; /* Max size of stills capture */ ++ u32 max_stills_h; ++ u32 stills_yuv422; /* Allow YUV422 stills capture */ ++ u32 one_shot_stills; /* Continuous or one shot stills captures. */ ++ ++ u32 max_preview_video_w; /* Max size of the preview or video ++ * capture frames ++ */ ++ u32 max_preview_video_h; ++ u32 num_preview_video_frames; ++ ++ /** Sets the height of the circular buffer for stills capture. */ ++ u32 stills_capture_circular_buffer_height; ++ ++ /** Allows preview/encode to resume as fast as possible after the stills ++ * input frame has been received, and then processes the still frame in ++ * the background whilst preview/encode has resumed. ++ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. ++ */ ++ u32 fast_preview_resume; ++ ++ /** Selects algorithm for timestamping frames if ++ * there is no clock component connected. ++ * enum mmal_parameter_camera_config_timestamp_mode ++ */ ++ s32 use_stc_timestamp; ++}; ++ ++ ++enum mmal_parameter_exposuremode { ++ MMAL_PARAM_EXPOSUREMODE_OFF, ++ MMAL_PARAM_EXPOSUREMODE_AUTO, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, ++ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SNOW, ++ MMAL_PARAM_EXPOSUREMODE_BEACH, ++ MMAL_PARAM_EXPOSUREMODE_VERYLONG, ++ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, ++ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, ++ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, ++}; ++ ++enum mmal_parameter_exposuremeteringmode { ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, ++}; ++ ++enum mmal_parameter_awbmode { ++ MMAL_PARAM_AWBMODE_OFF, ++ MMAL_PARAM_AWBMODE_AUTO, ++ MMAL_PARAM_AWBMODE_SUNLIGHT, ++ MMAL_PARAM_AWBMODE_CLOUDY, ++ MMAL_PARAM_AWBMODE_SHADE, ++ MMAL_PARAM_AWBMODE_TUNGSTEN, ++ MMAL_PARAM_AWBMODE_FLUORESCENT, ++ MMAL_PARAM_AWBMODE_INCANDESCENT, ++ MMAL_PARAM_AWBMODE_FLASH, ++ MMAL_PARAM_AWBMODE_HORIZON, ++}; ++ ++enum mmal_parameter_imagefx { ++ MMAL_PARAM_IMAGEFX_NONE, ++ MMAL_PARAM_IMAGEFX_NEGATIVE, ++ MMAL_PARAM_IMAGEFX_SOLARIZE, ++ MMAL_PARAM_IMAGEFX_POSTERIZE, ++ MMAL_PARAM_IMAGEFX_WHITEBOARD, ++ MMAL_PARAM_IMAGEFX_BLACKBOARD, ++ MMAL_PARAM_IMAGEFX_SKETCH, ++ MMAL_PARAM_IMAGEFX_DENOISE, ++ MMAL_PARAM_IMAGEFX_EMBOSS, ++ MMAL_PARAM_IMAGEFX_OILPAINT, ++ MMAL_PARAM_IMAGEFX_HATCH, ++ MMAL_PARAM_IMAGEFX_GPEN, ++ MMAL_PARAM_IMAGEFX_PASTEL, ++ MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ MMAL_PARAM_IMAGEFX_FILM, ++ MMAL_PARAM_IMAGEFX_BLUR, ++ MMAL_PARAM_IMAGEFX_SATURATION, ++ MMAL_PARAM_IMAGEFX_COLOURSWAP, ++ MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ MMAL_PARAM_IMAGEFX_POSTERISE, ++ MMAL_PARAM_IMAGEFX_COLOURPOINT, ++ MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ MMAL_PARAM_IMAGEFX_CARTOON, ++}; ++ ++enum MMAL_PARAM_FLICKERAVOID_T { ++ MMAL_PARAM_FLICKERAVOID_OFF, ++ MMAL_PARAM_FLICKERAVOID_AUTO, ++ MMAL_PARAM_FLICKERAVOID_50HZ, ++ MMAL_PARAM_FLICKERAVOID_60HZ, ++ MMAL_PARAM_FLICKERAVOID_MAX = 0x7FFFFFFF ++}; ++ ++struct mmal_parameter_awbgains { ++ struct mmal_parameter_rational r_gain; /**< Red gain */ ++ struct mmal_parameter_rational b_gain; /**< Blue gain */ ++}; ++ ++/** Manner of video rate control */ ++enum mmal_parameter_rate_control_mode { ++ MMAL_VIDEO_RATECONTROL_DEFAULT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE, ++ MMAL_VIDEO_RATECONTROL_CONSTANT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, ++ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES ++}; ++ ++enum mmal_video_profile { ++ MMAL_VIDEO_PROFILE_H263_BASELINE, ++ MMAL_VIDEO_PROFILE_H263_H320CODING, ++ MMAL_VIDEO_PROFILE_H263_BACKWARDCOMPATIBLE, ++ MMAL_VIDEO_PROFILE_H263_ISWV2, ++ MMAL_VIDEO_PROFILE_H263_ISWV3, ++ MMAL_VIDEO_PROFILE_H263_HIGHCOMPRESSION, ++ MMAL_VIDEO_PROFILE_H263_INTERNET, ++ MMAL_VIDEO_PROFILE_H263_INTERLACE, ++ MMAL_VIDEO_PROFILE_H263_HIGHLATENCY, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLESCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_CORE, ++ MMAL_VIDEO_PROFILE_MP4V_MAIN, ++ MMAL_VIDEO_PROFILE_MP4V_NBIT, ++ MMAL_VIDEO_PROFILE_MP4V_SCALABLETEXTURE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFACE, ++ MMAL_VIDEO_PROFILE_MP4V_SIMPLEFBA, ++ MMAL_VIDEO_PROFILE_MP4V_BASICANIMATED, ++ MMAL_VIDEO_PROFILE_MP4V_HYBRID, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDREALTIME, ++ MMAL_VIDEO_PROFILE_MP4V_CORESCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCODING, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDCORE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSCALABLE, ++ MMAL_VIDEO_PROFILE_MP4V_ADVANCEDSIMPLE, ++ MMAL_VIDEO_PROFILE_H264_BASELINE, ++ MMAL_VIDEO_PROFILE_H264_MAIN, ++ MMAL_VIDEO_PROFILE_H264_EXTENDED, ++ MMAL_VIDEO_PROFILE_H264_HIGH, ++ MMAL_VIDEO_PROFILE_H264_HIGH10, ++ MMAL_VIDEO_PROFILE_H264_HIGH422, ++ MMAL_VIDEO_PROFILE_H264_HIGH444, ++ MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, ++ MMAL_VIDEO_PROFILE_DUMMY = 0x7FFFFFFF ++}; ++ ++enum mmal_video_level { ++ MMAL_VIDEO_LEVEL_H263_10, ++ MMAL_VIDEO_LEVEL_H263_20, ++ MMAL_VIDEO_LEVEL_H263_30, ++ MMAL_VIDEO_LEVEL_H263_40, ++ MMAL_VIDEO_LEVEL_H263_45, ++ MMAL_VIDEO_LEVEL_H263_50, ++ MMAL_VIDEO_LEVEL_H263_60, ++ MMAL_VIDEO_LEVEL_H263_70, ++ MMAL_VIDEO_LEVEL_MP4V_0, ++ MMAL_VIDEO_LEVEL_MP4V_0b, ++ MMAL_VIDEO_LEVEL_MP4V_1, ++ MMAL_VIDEO_LEVEL_MP4V_2, ++ MMAL_VIDEO_LEVEL_MP4V_3, ++ MMAL_VIDEO_LEVEL_MP4V_4, ++ MMAL_VIDEO_LEVEL_MP4V_4a, ++ MMAL_VIDEO_LEVEL_MP4V_5, ++ MMAL_VIDEO_LEVEL_MP4V_6, ++ MMAL_VIDEO_LEVEL_H264_1, ++ MMAL_VIDEO_LEVEL_H264_1b, ++ MMAL_VIDEO_LEVEL_H264_11, ++ MMAL_VIDEO_LEVEL_H264_12, ++ MMAL_VIDEO_LEVEL_H264_13, ++ MMAL_VIDEO_LEVEL_H264_2, ++ MMAL_VIDEO_LEVEL_H264_21, ++ MMAL_VIDEO_LEVEL_H264_22, ++ MMAL_VIDEO_LEVEL_H264_3, ++ MMAL_VIDEO_LEVEL_H264_31, ++ MMAL_VIDEO_LEVEL_H264_32, ++ MMAL_VIDEO_LEVEL_H264_4, ++ MMAL_VIDEO_LEVEL_H264_41, ++ MMAL_VIDEO_LEVEL_H264_42, ++ MMAL_VIDEO_LEVEL_H264_5, ++ MMAL_VIDEO_LEVEL_H264_51, ++ MMAL_VIDEO_LEVEL_DUMMY = 0x7FFFFFFF ++}; ++ ++struct mmal_parameter_video_profile { ++ enum mmal_video_profile profile; ++ enum mmal_video_level level; ++}; ++ ++/* video parameters */ ++ ++enum mmal_parameter_video_type { ++ /** @ref MMAL_DISPLAYREGION_T */ ++ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_SUPPORTED_PROFILES, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_PROFILE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_INTRAPERIOD, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ ++ MMAL_PARAMETER_RATECONTROL, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ ++ MMAL_PARAMETER_NALUNITFORMAT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Setting the value to zero resets to the default (one slice per frame). ++ */ ++ MMAL_PARAMETER_MB_ROWS_PER_SLICE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ ++ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ ++ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, ++ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ ++ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ ++ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, ++ ++ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_VERT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, ++ ++ /* H264 specific parameters */ ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, ++ ++ /** @ref MMAL_PARAMETER_BYTES_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_VCL_HRD_PARAMETERS, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER ++}; ++ ++/** Valid mirror modes */ ++enum mmal_parameter_mirror { ++ MMAL_PARAM_MIRROR_NONE, ++ MMAL_PARAM_MIRROR_VERTICAL, ++ MMAL_PARAM_MIRROR_HORIZONTAL, ++ MMAL_PARAM_MIRROR_BOTH, ++}; ++ ++enum mmal_parameter_displaytransform { ++ MMAL_DISPLAY_ROT0 = 0, ++ MMAL_DISPLAY_MIRROR_ROT0 = 1, ++ MMAL_DISPLAY_MIRROR_ROT180 = 2, ++ MMAL_DISPLAY_ROT180 = 3, ++ MMAL_DISPLAY_MIRROR_ROT90 = 4, ++ MMAL_DISPLAY_ROT270 = 5, ++ MMAL_DISPLAY_ROT90 = 6, ++ MMAL_DISPLAY_MIRROR_ROT270 = 7, ++}; ++ ++enum mmal_parameter_displaymode { ++ MMAL_DISPLAY_MODE_FILL = 0, ++ MMAL_DISPLAY_MODE_LETTERBOX = 1, ++}; ++ ++enum mmal_parameter_displayset { ++ MMAL_DISPLAY_SET_NONE = 0, ++ MMAL_DISPLAY_SET_NUM = 1, ++ MMAL_DISPLAY_SET_FULLSCREEN = 2, ++ MMAL_DISPLAY_SET_TRANSFORM = 4, ++ MMAL_DISPLAY_SET_DEST_RECT = 8, ++ MMAL_DISPLAY_SET_SRC_RECT = 0x10, ++ MMAL_DISPLAY_SET_MODE = 0x20, ++ MMAL_DISPLAY_SET_PIXEL = 0x40, ++ MMAL_DISPLAY_SET_NOASPECT = 0x80, ++ MMAL_DISPLAY_SET_LAYER = 0x100, ++ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, ++ MMAL_DISPLAY_SET_ALPHA = 0x400, ++}; ++ ++struct mmal_parameter_displayregion { ++ /** Bitfield that indicates which fields are set and should be ++ * used. All other fields will maintain their current value. ++ * \ref MMAL_DISPLAYSET_T defines the bits that can be ++ * combined. ++ */ ++ u32 set; ++ ++ /** Describes the display output device, with 0 typically ++ * being a directly connected LCD display. The actual values ++ * will depend on the hardware. Code using hard-wired numbers ++ * (e.g. 2) is certain to fail. ++ */ ++ ++ u32 display_num; ++ /** Indicates that we are using the full device screen area, ++ * rather than a window of the display. If zero, then ++ * dest_rect is used to specify a region of the display to ++ * use. ++ */ ++ ++ s32 fullscreen; ++ /** Indicates any rotation or flipping used to map frames onto ++ * the natural display orientation. ++ */ ++ u32 transform; /* enum mmal_parameter_displaytransform */ ++ ++ /** Where to display the frame within the screen, if ++ * fullscreen is zero. ++ */ ++ struct vchiq_mmal_rect dest_rect; ++ ++ /** Indicates which area of the frame to display. If all ++ * values are zero, the whole frame will be used. ++ */ ++ struct vchiq_mmal_rect src_rect; ++ ++ /** If set to non-zero, indicates that any display scaling ++ * should disregard the aspect ratio of the frame region being ++ * displayed. ++ */ ++ s32 noaspect; ++ ++ /** Indicates how the image should be scaled to fit the ++ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates ++ * that the image should fill the screen by potentially ++ * cropping the frames. Setting \code mode \endcode to \code ++ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the ++ * source region should be displayed and black bars added if ++ * necessary. ++ */ ++ u32 mode; /* enum mmal_parameter_displaymode */ ++ ++ /** If non-zero, defines the width of a source pixel relative ++ * to \code pixel_y \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_x; ++ ++ /** If non-zero, defines the height of a source pixel relative ++ * to \code pixel_x \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_y; ++ ++ /** Sets the relative depth of the images, with greater values ++ * being in front of smaller values. ++ */ ++ u32 layer; ++ ++ /** Set to non-zero to ensure copy protection is used on ++ * output. ++ */ ++ s32 copyprotect_required; ++ ++ /** Level of opacity of the layer, where zero is fully ++ * transparent and 255 is fully opaque. ++ */ ++ u32 alpha; ++}; ++ ++#define MMAL_MAX_IMAGEFX_PARAMETERS 5 ++ ++struct mmal_parameter_imagefx_parameters { ++ enum mmal_parameter_imagefx effect; ++ u32 num_effect_params; ++ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c +new file mode 100644 +index 0000000..76f249e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.c +@@ -0,0 +1,1916 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * V4L2 driver MMAL vchiq interface code ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++ ++#define USE_VCHIQ_ARM ++#include "interface/vchi/vchi.h" ++ ++/* maximum number of components supported */ ++#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++ ++/*#define FULL_MSG_DUMP 1*/ ++ ++#ifdef DEBUG ++static const char *const msg_type_names[] = { ++ "UNKNOWN", ++ "QUIT", ++ "SERVICE_CLOSED", ++ "GET_VERSION", ++ "COMPONENT_CREATE", ++ "COMPONENT_DESTROY", ++ "COMPONENT_ENABLE", ++ "COMPONENT_DISABLE", ++ "PORT_INFO_GET", ++ "PORT_INFO_SET", ++ "PORT_ACTION", ++ "BUFFER_FROM_HOST", ++ "BUFFER_TO_HOST", ++ "GET_STATS", ++ "PORT_PARAMETER_SET", ++ "PORT_PARAMETER_GET", ++ "EVENT_TO_HOST", ++ "GET_CORE_STATS_FOR_PORT", ++ "OPAQUE_ALLOCATOR", ++ "CONSUME_MEM", ++ "LMK", ++ "OPAQUE_ALLOCATOR_DESC", ++ "DRM_GET_LHS32", ++ "DRM_GET_TIME", ++ "BUFFER_FROM_HOST_ZEROLEN", ++ "PORT_FLUSH", ++ "HOST_LOG", ++}; ++#endif ++ ++static const char *const port_action_type_names[] = { ++ "UNKNOWN", ++ "ENABLE", ++ "DISABLE", ++ "FLUSH", ++ "CONNECT", ++ "DISCONNECT", ++ "SET_REQUIREMENTS", ++}; ++ ++#if defined(DEBUG) ++#if defined(FULL_MSG_DUMP) ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ ++ do { \ ++ pr_debug(TITLE" type:%s(%d) length:%d\n", \ ++ msg_type_names[(MSG)->h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ print_hex_dump(KERN_DEBUG, "<h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ } ++#endif ++#else ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) ++#endif ++ ++/* normal message context */ ++struct mmal_msg_context { ++ union { ++ struct { ++ /* work struct for defered callback - must come first */ ++ struct work_struct work; ++ /* mmal instance */ ++ struct vchiq_mmal_instance *instance; ++ /* mmal port */ ++ struct vchiq_mmal_port *port; ++ /* actual buffer used to store bulk reply */ ++ struct mmal_buffer *buffer; ++ /* amount of buffer used */ ++ unsigned long buffer_used; ++ /* MMAL buffer flags */ ++ u32 mmal_flags; ++ /* Presentation and Decode timestamps */ ++ s64 pts; ++ s64 dts; ++ ++ int status; /* context status */ ++ ++ } bulk; /* bulk data */ ++ ++ struct { ++ /* message handle to release */ ++ VCHI_HELD_MSG_T msg_handle; ++ /* pointer to received message */ ++ struct mmal_msg *msg; ++ /* received message length */ ++ u32 msg_len; ++ /* completion upon reply */ ++ struct completion cmplt; ++ } sync; /* synchronous response */ ++ } u; ++ ++}; ++ ++struct vchiq_mmal_instance { ++ VCHI_SERVICE_HANDLE_T handle; ++ ++ /* ensure serialised access to service */ ++ struct mutex vchiq_mutex; ++ ++ /* ensure serialised access to bulk operations */ ++ struct mutex bulk_mutex; ++ ++ /* vmalloc page to receive scratch bulk xfers into */ ++ void *bulk_scratch; ++ ++ /* component to use next */ ++ int component_idx; ++ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; ++}; ++ ++static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance ++ *instance) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ /* todo: should this be allocated from a pool to avoid kmalloc */ ++ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); ++ memset(msg_context, 0, sizeof(*msg_context)); ++ ++ return msg_context; ++} ++ ++static void release_msg_context(struct mmal_msg_context *msg_context) ++{ ++ kfree(msg_context); ++} ++ ++/* deals with receipt of event to host message */ ++static void event_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ pr_debug("unhandled event\n"); ++ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", ++ msg->u.event_to_host.client_component, ++ msg->u.event_to_host.port_type, ++ msg->u.event_to_host.port_num, ++ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++} ++ ++/* workqueue scheduled callback ++ * ++ * we do this because it is important we do not call any other vchiq ++ * sync calls from witin the message delivery thread ++ */ ++static void buffer_work_cb(struct work_struct *work) ++{ ++ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; ++ ++ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port, ++ msg_context->u.bulk.status, ++ msg_context->u.bulk.buffer, ++ msg_context->u.bulk.buffer_used, ++ msg_context->u.bulk.mmal_flags, ++ msg_context->u.bulk.dts, ++ msg_context->u.bulk.pts); ++ ++ /* release message context */ ++ release_msg_context(msg_context); ++} ++ ++/* enqueue a bulk receive for a given message context */ ++static int bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long rd_len; ++ unsigned long flags = 0; ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ rd_len = msg->u.buffer_from_host.buffer_header.length; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to submit bulk receive\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (underflow ++ * handling) and there is no obvious way to deal with ++ * this - how is the mmal servie going to react when ++ * we fail to do the xfer and reschedule a buffer when ++ * it arrives? perhaps a starved flag to indicate a ++ * waiting bulk receive? ++ */ ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ /* ensure we do not overrun the available buffer */ ++ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { ++ rd_len = msg_context->u.bulk.buffer->buffer_size; ++ pr_warn("short read as not enough receive buffer space\n"); ++ /* todo: is this the correct response, what happens to ++ * the rest of the message data? ++ */ ++ } ++ ++ /* store length */ ++ msg_context->u.bulk.buffer_used = rd_len; ++ msg_context->u.bulk.mmal_flags = ++ msg->u.buffer_from_host.buffer_header.flags; ++ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; ++ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; ++ ++ // only need to flush L1 cache here, as VCHIQ takes care of the L2 ++ // cache. ++ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ret = vchi_bulk_queue_receive(instance->handle, ++ msg_context->u.bulk.buffer->buffer, ++ /* Actual receive needs to be a multiple ++ * of 4 bytes ++ */ ++ (rd_len + 3) & ~3, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* enque a dummy bulk receive for a given message context */ ++static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ /* zero length indicates this was a dummy transfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_bulk_queue_receive(instance->handle, ++ instance->bulk_scratch, ++ 8, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* data in message, memcpy from packet into output buffer */ ++static int inline_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long flags = 0; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to receive inline\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (with ++ * underflow handling) and there is no obvious way to ++ * deal with this. Less bad than the bulk case as we ++ * can just drop this on the floor but...unhelpful ++ */ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ memcpy(msg_context->u.bulk.buffer->buffer, ++ msg->u.buffer_from_host.short_data, ++ msg->u.buffer_from_host.payload_in_message); ++ ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ ++ return 0; ++} ++ ++/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ ++static int ++buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++{ ++ struct mmal_msg_context *msg_context; ++ struct mmal_msg m; ++ int ret; ++ ++ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); ++ ++ /* bulk mutex stops other bulk operations while we ++ * have a receive in progress ++ */ ++ if (mutex_lock_interruptible(&instance->bulk_mutex)) ++ return -EINTR; ++ ++ /* get context */ ++ msg_context = get_msg_context(instance); ++ if (msg_context == NULL) ++ return -ENOMEM; ++ ++ /* store bulk message context for when data arrives */ ++ msg_context->u.bulk.instance = instance; ++ msg_context->u.bulk.port = port; ++ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* initialise work structure ready to schedule callback */ ++ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); ++ ++ /* prep the buffer from host message */ ++ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ ++ ++ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; ++ m.h.magic = MMAL_MAGIC; ++ m.h.context = msg_context; ++ m.h.status = 0; ++ ++ /* drvbuf is our private data passed back */ ++ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; ++ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; ++ m.u.buffer_from_host.drvbuf.port_handle = port->handle; ++ m.u.buffer_from_host.drvbuf.client_context = msg_context; ++ ++ /* buffer header */ ++ m.u.buffer_from_host.buffer_header.cmd = 0; ++ m.u.buffer_from_host.buffer_header.data = buf->buffer; ++ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; ++ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ ++ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ ++ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ ++ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; ++ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; ++ ++ /* clear buffer type sepecific data */ ++ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, ++ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); ++ ++ /* no payload in message */ ++ m.u.buffer_from_host.payload_in_message = 0; ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, &m, ++ sizeof(struct mmal_msg_header) + ++ sizeof(m.u.buffer_from_host), ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (ret != 0) { ++ release_msg_context(msg_context); ++ /* todo: is this correct error value? */ ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return ret; ++} ++ ++/* submit a buffer to the mmal sevice ++ * ++ * the buffer_from_host uses size data from the ports next available ++ * mmal_buffer and deals with there being no buffer available by ++ * incrementing the underflow for later ++ */ ++static int port_buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_buffer *buf; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return -EINVAL; ++ ++ /* peek buffer from queue */ ++ spin_lock_irqsave(&port->slock, flags); ++ if (list_empty(&port->buffers)) { ++ port->buffer_underflow++; ++ spin_unlock_irqrestore(&port->slock, flags); ++ return -ENOSPC; ++ } ++ ++ buf = list_entry(port->buffers.next, struct mmal_buffer, list); ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* issue buffer to mmal service */ ++ ret = buffer_from_host(instance, port, buf); ++ if (ret) { ++ pr_err("adding buffer header failed\n"); ++ /* todo: how should this be dealt with */ ++ } ++ ++ return ret; ++} ++ ++/* deals with receipt of buffer to host message */ ++static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", ++ instance, msg, msg_len); ++ ++ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { ++ msg_context = msg->u.buffer_from_host.drvbuf.client_context; ++ } else { ++ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); ++ return; ++ } ++ ++ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { ++ /* message reception had an error */ ++ pr_warn("error %d in reply\n", msg->h.status); ++ ++ msg_context->u.bulk.status = msg->h.status; ++ ++ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { ++ /* empty buffer */ ++ if (msg->u.buffer_from_host.buffer_header.flags & ++ MMAL_BUFFER_HEADER_FLAG_EOS) { ++ msg_context->u.bulk.status = ++ dummy_bulk_receive(instance, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ } else { ++ /* do callback with empty buffer - not EOS though */ ++ msg_context->u.bulk.status = 0; ++ msg_context->u.bulk.buffer_used = 0; ++ } ++ } else if (msg->u.buffer_from_host.payload_in_message == 0) { ++ /* data is not in message, queue a bulk receive */ ++ msg_context->u.bulk.status = ++ bulk_receive(instance, msg, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ ++ /* failed to submit buffer, this will end badly */ ++ pr_err("error %d on bulk submission\n", ++ msg_context->u.bulk.status); ++ ++ } else if (msg->u.buffer_from_host.payload_in_message <= ++ MMAL_VC_SHORT_DATA) { ++ /* data payload within message */ ++ msg_context->u.bulk.status = inline_receive(instance, msg, ++ msg_context); ++ } else { ++ pr_err("message with invalid short payload\n"); ++ ++ /* signal error */ ++ msg_context->u.bulk.status = -EINVAL; ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ } ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(instance, msg_context->u.bulk.port); ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_receive_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = 0; ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_abort_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); ++ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = -EINTR; ++ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++/* incoming event service callback */ ++static void service_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *bulk_ctx) ++{ ++ struct vchiq_mmal_instance *instance = param; ++ int status; ++ u32 msg_len; ++ struct mmal_msg *msg; ++ VCHI_HELD_MSG_T msg_handle; ++ ++ if (!instance) { ++ pr_err("Message callback passed NULL instance\n"); ++ return; ++ } ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ status = vchi_msg_hold(instance->handle, (void **)&msg, ++ &msg_len, VCHI_FLAGS_NONE, &msg_handle); ++ if (status) { ++ pr_err("Unable to dequeue a message (%d)\n", status); ++ break; ++ } ++ ++ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ ++ /* handling is different for buffer messages */ ++ switch (msg->h.type) { ++ ++ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ case MMAL_MSG_TYPE_EVENT_TO_HOST: ++ event_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ ++ break; ++ ++ case MMAL_MSG_TYPE_BUFFER_TO_HOST: ++ buffer_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ default: ++ /* messages dependant on header context to complete */ ++ ++ /* todo: the msg.context really ought to be sanity ++ * checked before we just use it, afaict it comes back ++ * and is used raw from the videocore. Perhaps it ++ * should be verified the address lies in the kernel ++ * address space. ++ */ ++ if (msg->h.context == NULL) { ++ pr_err("received message context was null!\n"); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ } ++ ++ /* fill in context values */ ++ msg->h.context->u.sync.msg_handle = msg_handle; ++ msg->h.context->u.sync.msg = msg; ++ msg->h.context->u.sync.msg_len = msg_len; ++ ++ /* todo: should this check (completion_done() ++ * == 1) for no one waiting? or do we need a ++ * flag to tell us the completion has been ++ * interrupted so we can free the message and ++ * its context. This probably also solves the ++ * message arriving after interruption todo ++ * below ++ */ ++ ++ /* complete message so caller knows it happened */ ++ complete(&msg->h.context->u.sync.cmplt); ++ break; ++ } ++ ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVED: ++ bulk_receive_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: ++ bulk_abort_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ /* TODO: consider if this requires action if received when ++ * driver is not explicitly closing the service ++ */ ++ break; ++ ++ default: ++ pr_err("Received unhandled message reason %d\n", reason); ++ break; ++ } ++} ++ ++static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ unsigned int payload_len, ++ struct mmal_msg **msg_out, ++ VCHI_HELD_MSG_T *msg_handle_out) ++{ ++ struct mmal_msg_context msg_context; ++ int ret; ++ ++ /* payload size must not cause message to exceed max size */ ++ if (payload_len > ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { ++ pr_err("payload length %d exceeds max:%d\n", payload_len, ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); ++ return -EINVAL; ++ } ++ ++ init_completion(&msg_context.u.sync.cmplt); ++ ++ msg->h.magic = MMAL_MAGIC; ++ msg->h.context = &msg_context; ++ msg->h.status = 0; ++ ++ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), ++ ">>> sync message"); ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, ++ msg, ++ sizeof(struct mmal_msg_header) + payload_len, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret) { ++ pr_err("error %d queuing message\n", ret); ++ return ret; ++ } ++ ++ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, 3*HZ); ++ if (ret <= 0) { ++ pr_err("error %d waiting for sync completion\n", ret); ++ if (ret == 0) ++ ret = -ETIME; ++ /* todo: what happens if the message arrives after aborting */ ++ return ret; ++ } ++ ++ *msg_out = msg_context.u.sync.msg; ++ *msg_handle_out = msg_context.u.sync.msg_handle; ++ ++ return 0; ++} ++ ++static void dump_port_info(struct vchiq_mmal_port *port) ++{ ++ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); ++ ++ pr_debug("buffer minimum num:%d size:%d align:%d\n", ++ port->minimum_buffer.num, ++ port->minimum_buffer.size, port->minimum_buffer.alignment); ++ ++ pr_debug("buffer recommended num:%d size:%d align:%d\n", ++ port->recommended_buffer.num, ++ port->recommended_buffer.size, ++ port->recommended_buffer.alignment); ++ ++ pr_debug("buffer current values num:%d size:%d align:%d\n", ++ port->current_buffer.num, ++ port->current_buffer.size, port->current_buffer.alignment); ++ ++ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", ++ port->format.type, ++ port->format.encoding, port->format.encoding_variant); ++ ++ pr_debug(" bitrate:%d flags:0x%x\n", ++ port->format.bitrate, port->format.flags); ++ ++ if (port->format.type == MMAL_ES_TYPE_VIDEO) { ++ pr_debug ++ ("es video format: width:%d height:%d colourspace:0x%x\n", ++ port->es.video.width, port->es.video.height, ++ port->es.video.color_space); ++ ++ pr_debug(" : crop xywh %d,%d,%d,%d\n", ++ port->es.video.crop.x, ++ port->es.video.crop.y, ++ port->es.video.crop.width, port->es.video.crop.height); ++ pr_debug(" : framerate %d/%d aspect %d/%d\n", ++ port->es.video.frame_rate.num, ++ port->es.video.frame_rate.den, ++ port->es.video.par.num, port->es.video.par.den); ++ } ++} ++ ++static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) ++{ ++ ++ /* todo do readonly fields need setting at all? */ ++ p->type = port->type; ++ p->index = port->index; ++ p->index_all = 0; ++ p->is_enabled = port->enabled; ++ p->buffer_num_min = port->minimum_buffer.num; ++ p->buffer_size_min = port->minimum_buffer.size; ++ p->buffer_alignment_min = port->minimum_buffer.alignment; ++ p->buffer_num_recommended = port->recommended_buffer.num; ++ p->buffer_size_recommended = port->recommended_buffer.size; ++ ++ /* only three writable fields in a port */ ++ p->buffer_num = port->current_buffer.num; ++ p->buffer_size = port->current_buffer.size; ++ p->userdata = port; ++} ++ ++static int port_info_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ pr_debug("setting port info port %p\n", port); ++ if (!port) ++ return -1; ++ dump_port_info(port); ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; ++ ++ m.u.port_info_set.component_handle = port->component->handle; ++ m.u.port_info_set.port_type = port->type; ++ m.u.port_info_set.port_index = port->index; ++ ++ port_to_mmal_msg(port, &m.u.port_info_set.port); ++ ++ /* elementry stream format setup */ ++ m.u.port_info_set.format.type = port->format.type; ++ m.u.port_info_set.format.encoding = port->format.encoding; ++ m.u.port_info_set.format.encoding_variant = ++ port->format.encoding_variant; ++ m.u.port_info_set.format.bitrate = port->format.bitrate; ++ m.u.port_info_set.format.flags = port->format.flags; ++ ++ memcpy(&m.u.port_info_set.es, &port->es, ++ sizeof(union mmal_es_specific_format)); ++ ++ m.u.port_info_set.format.extradata_size = port->format.extradata_size; ++ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, ++ port->format.extradata_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_set), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, ++ port->component->handle, port->handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++ ++} ++ ++/* use port info get message to retrive port information */ ++static int port_info_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* port info time */ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; ++ m.u.port_info_get.component_handle = port->component->handle; ++ m.u.port_info_get.port_type = port->type; ++ m.u.port_info_get.index = port->index; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) ++ port->enabled = false; ++ else ++ port->enabled = true; ++ ++ /* copy the values out of the message */ ++ port->handle = rmsg->u.port_info_get_reply.port_handle; ++ ++ /* port type and index cached to use on port info set becuase ++ * it does not use a port handle ++ */ ++ port->type = rmsg->u.port_info_get_reply.port_type; ++ port->index = rmsg->u.port_info_get_reply.port_index; ++ ++ port->minimum_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_min; ++ port->minimum_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size_min; ++ port->minimum_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ ++ port->recommended_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ port->recommended_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_recommended; ++ ++ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; ++ port->current_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size; ++ ++ /* stream format */ ++ port->format.type = rmsg->u.port_info_get_reply.format.type; ++ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; ++ port->format.encoding_variant = ++ rmsg->u.port_info_get_reply.format.encoding_variant; ++ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; ++ port->format.flags = rmsg->u.port_info_get_reply.format.flags; ++ ++ /* elementry stream format */ ++ memcpy(&port->es, ++ &rmsg->u.port_info_get_reply.es, ++ sizeof(union mmal_es_specific_format)); ++ port->format.es = &port->es; ++ ++ port->format.extradata_size = ++ rmsg->u.port_info_get_reply.format.extradata_size; ++ memcpy(port->format.extradata, ++ rmsg->u.port_info_get_reply.extradata, ++ port->format.extradata_size); ++ ++ pr_debug("received port info\n"); ++ dump_port_info(port); ++ ++release_msg: ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", ++ __func__, ret, port->component->handle, port->handle); ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* create comonent on vc */ ++static int create_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component, ++ const char *name) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* build component create message */ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; ++ m.u.component_create.client_component = component; ++ strncpy(m.u.component_create.name, name, ++ sizeof(m.u.component_create.name)); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_create), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_create_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ /* a valid component response received */ ++ component->handle = rmsg->u.component_create_reply.component_handle; ++ component->inputs = rmsg->u.component_create_reply.input_num; ++ component->outputs = rmsg->u.component_create_reply.output_num; ++ component->clocks = rmsg->u.component_create_reply.clock_num; ++ ++ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", ++ component->handle, ++ component->inputs, component->outputs, component->clocks); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* destroys a component on vc */ ++static int destroy_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; ++ m.u.component_destroy.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_destroy), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_destroy_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* enable a component on vc */ ++static int enable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; ++ m.u.component_enable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_enable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_enable_reply.status; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disable a component on vc */ ++static int disable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; ++ m.u.component_disable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_disable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_disable_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* get version of mmal implementation */ ++static int get_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_GET_VERSION; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.version), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ *major_out = rmsg->u.version.major; ++ *minor_out = rmsg->u.version.minor; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with a port as a parameter */ ++static int port_action_port(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ m.u.port_action_port.component_handle = port->component->handle; ++ m.u.port_action_port.port_handle = port->handle; ++ m.u.port_action_port.action = action_type; ++ ++ port_to_mmal_msg(port, &m.u.port_action_port.port); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_port), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], action_type); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with handles as parameters */ ++static int port_action_handle(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type, ++ u32 connect_component_handle, ++ u32 connect_port_handle) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ ++ m.u.port_action_handle.component_handle = port->component->handle; ++ m.u.port_action_handle.port_handle = port->handle; ++ m.u.port_action_handle.action = action_type; ++ ++ m.u.port_action_handle.connect_component_handle = ++ connect_component_handle; ++ m.u.port_action_handle.connect_port_handle = connect_port_handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_handle), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ ++ " connect component:0x%x connect port:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], ++ action_type, connect_component_handle, connect_port_handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; ++ ++ m.u.port_parameter_set.component_handle = port->component->handle; ++ m.u.port_parameter_set.port_handle = port->handle; ++ m.u.port_parameter_set.id = parameter_id; ++ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; ++ memcpy(&m.u.port_parameter_set.value, value, value_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ (4 * sizeof(u32)) + value_size, ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_set_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 *value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; ++ ++ m.u.port_parameter_get.component_handle = port->component->handle; ++ m.u.port_parameter_get.port_handle = port->handle; ++ m.u.port_parameter_get.id = parameter_id; ++ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(struct ++ mmal_msg_port_parameter_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { ++ /* got an unexpected message type in reply */ ++ pr_err("Incorrect reply type %d\n", rmsg->h.type); ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_get_reply.status; ++ if (ret) { ++ /* Copy only as much as we have space for ++ * but report true size of parameter ++ */ ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ *value_size); ++ *value_size = rmsg->u.port_parameter_get_reply.size; ++ } else ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ rmsg->u.port_parameter_get_reply.size); ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disables a port and drains buffers from it */ ++static int port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct list_head *q, *buf_head; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return 0; ++ ++ port->enabled = false; ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); ++ if (ret == 0) { ++ ++ /* drain all queued buffers on port */ ++ spin_lock_irqsave(&port->slock, flags); ++ ++ list_for_each_safe(buf_head, q, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ list_del(buf_head); ++ if (port->buffer_cb) ++ port->buffer_cb(instance, ++ port, 0, mmalbuf, 0, 0, ++ MMAL_TIME_UNKNOWN, ++ MMAL_TIME_UNKNOWN); ++ } ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ ret = port_info_get(instance, port); ++ } ++ ++ return ret; ++} ++ ++/* enable a port */ ++static int port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ unsigned int hdr_count; ++ struct list_head *buf_head; ++ int ret; ++ ++ if (port->enabled) ++ return 0; ++ ++ /* ensure there are enough buffers queued to cover the buffer headers */ ++ if (port->buffer_cb != NULL) { ++ hdr_count = 0; ++ list_for_each(buf_head, &port->buffers) { ++ hdr_count++; ++ } ++ if (hdr_count < port->current_buffer.num) ++ return -ENOSPC; ++ } ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); ++ if (ret) ++ goto done; ++ ++ port->enabled = true; ++ ++ if (port->buffer_cb) { ++ /* send buffer headers to videocore */ ++ hdr_count = 1; ++ list_for_each(buf_head, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ ret = buffer_from_host(instance, port, mmalbuf); ++ if (ret) ++ goto done; ++ ++ hdr_count++; ++ if (hdr_count > port->current_buffer.num) ++ break; ++ } ++ } ++ ++ ret = port_info_get(instance, port); ++ ++done: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------ ++ * Exported API ++ *------------------------------------------------------------------*/ ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_info_set(instance, port); ++ if (ret) ++ goto release_unlock; ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, port); ++ ++release_unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++ ++} ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_set(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 *value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_get(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* enable a port ++ * ++ * enables a port and queues buffers for satisfying callbacks if we ++ * provide a callback handler ++ */ ++int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* already enabled - noop */ ++ if (port->enabled) { ++ ret = 0; ++ goto unlock; ++ } ++ ++ port->buffer_cb = buffer_cb; ++ ++ ret = port_enable(instance, port); ++ ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!port->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = port_disable(instance, port); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ports will be connected in a tunneled manner so data buffers ++ * are not handled by client. ++ */ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* disconnect ports if connected */ ++ if (src->connected != NULL) { ++ ret = port_disable(instance, src); ++ if (ret) { ++ pr_err("failed disabling src port(%d)\n", ret); ++ goto release_unlock; ++ } ++ ++ /* do not need to disable the destination port as they ++ * are connected and it is done automatically ++ */ ++ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, ++ src->connected->component->handle, ++ src->connected->handle); ++ if (ret < 0) { ++ pr_err("failed disconnecting src port\n"); ++ goto release_unlock; ++ } ++ src->connected->enabled = false; ++ src->connected = NULL; ++ } ++ ++ if (dst == NULL) { ++ /* do not make new connection */ ++ ret = 0; ++ pr_debug("not making new connection\n"); ++ goto release_unlock; ++ } ++ ++ /* copy src port format to dst */ ++ dst->format.encoding = src->format.encoding; ++ dst->es.video.width = src->es.video.width; ++ dst->es.video.height = src->es.video.height; ++ dst->es.video.crop.x = src->es.video.crop.x; ++ dst->es.video.crop.y = src->es.video.crop.y; ++ dst->es.video.crop.width = src->es.video.crop.width; ++ dst->es.video.crop.height = src->es.video.crop.height; ++ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; ++ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ ++ /* set new format */ ++ ret = port_info_set(instance, dst); ++ if (ret) { ++ pr_debug("setting port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, dst); ++ if (ret) { ++ pr_debug("read back port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* connect two ports together */ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, ++ dst->component->handle, dst->handle); ++ if (ret < 0) { ++ pr_debug("connecting port %d:%d to %d:%d failed\n", ++ src->component->handle, src->handle, ++ dst->component->handle, dst->handle); ++ goto release_unlock; ++ } ++ src->connected = dst; ++ ++release_unlock: ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buffer) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->slock, flags); ++ list_add_tail(&buffer->list, &port->buffers); ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* the port previously underflowed because it was missing a ++ * mmal_buffer which has just been added, submit that buffer ++ * to the mmal service. ++ */ ++ if (port->buffer_underflow) { ++ port_buffer_from_host(instance, port); ++ port->buffer_underflow--; ++ } ++ ++ return 0; ++} ++ ++/* Initialise a mmal component and its ports ++ * ++ */ ++int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out) ++{ ++ int ret; ++ int idx; /* port index */ ++ struct vchiq_mmal_component *component; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { ++ ret = -EINVAL; /* todo is this correct error? */ ++ goto unlock; ++ } ++ ++ component = &instance->component[instance->component_idx]; ++ ++ ret = create_component(instance, component, name); ++ if (ret < 0) ++ goto unlock; ++ ++ /* ports info needs gathering */ ++ component->control.type = MMAL_PORT_TYPE_CONTROL; ++ component->control.index = 0; ++ component->control.component = component; ++ spin_lock_init(&component->control.slock); ++ INIT_LIST_HEAD(&component->control.buffers); ++ ret = port_info_get(instance, &component->control); ++ if (ret < 0) ++ goto release_component; ++ ++ for (idx = 0; idx < component->inputs; idx++) { ++ component->input[idx].type = MMAL_PORT_TYPE_INPUT; ++ component->input[idx].index = idx; ++ component->input[idx].component = component; ++ spin_lock_init(&component->input[idx].slock); ++ INIT_LIST_HEAD(&component->input[idx].buffers); ++ ret = port_info_get(instance, &component->input[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->outputs; idx++) { ++ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; ++ component->output[idx].index = idx; ++ component->output[idx].component = component; ++ spin_lock_init(&component->output[idx].slock); ++ INIT_LIST_HEAD(&component->output[idx].buffers); ++ ret = port_info_get(instance, &component->output[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->clocks; idx++) { ++ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; ++ component->clock[idx].index = idx; ++ component->clock[idx].component = component; ++ spin_lock_init(&component->clock[idx].slock); ++ INIT_LIST_HEAD(&component->clock[idx].buffers); ++ ret = port_info_get(instance, &component->clock[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ instance->component_idx++; ++ ++ *component_out = component; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return 0; ++ ++release_component: ++ destroy_component(instance, component); ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be destroyed ++ */ ++int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) ++ ret = disable_component(instance, component); ++ ++ ret = destroy_component(instance, component); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = enable_component(instance, component); ++ if (ret == 0) ++ component->enabled = true; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = disable_component(instance, component); ++ if (ret == 0) ++ component->enabled = false; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = get_version(instance, major_out, minor_out); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) ++{ ++ int status = 0; ++ ++ if (instance == NULL) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ vchi_service_use(instance->handle); ++ ++ status = vchi_service_close(instance->handle); ++ if (status != 0) ++ pr_err("mmal-vchiq: VCHIQ close failed"); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ vfree(instance->bulk_scratch); ++ ++ kfree(instance); ++ ++ return status; ++} ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++{ ++ int status; ++ struct vchiq_mmal_instance *instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ static VCHI_INSTANCE_T vchi_instance; ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), ++ VC_MMAL_SERVER_NAME, ++ vchi_connection, ++ 0, /* rx fifo size (unused) */ ++ 0, /* tx fifo size (unused) */ ++ service_callback, ++ NULL, /* service callback parameter */ ++ 1, /* unaligned bulk receives */ ++ 1, /* unaligned bulk transmits */ ++ 0 /* want crc check on bulk transfers */ ++ }; ++ ++ /* compile time checks to ensure structure size as they are ++ * directly (de)serialised from memory. ++ */ ++ ++ /* ensure the header structure has packed to the correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ ++ /* ensure message structure does not exceed maximum length */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); ++ ++ /* mmal port struct is correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); ++ ++ /* create a vchi instance */ ++ status = vchi_initialise(&vchi_instance); ++ if (status) { ++ pr_err("Failed to initialise VCHI instance (status=%d)\n", ++ status); ++ return -EIO; ++ } ++ ++ status = vchi_connect(NULL, 0, vchi_instance); ++ if (status) { ++ pr_err("Failed to connect VCHI instance (status=%d)\n", status); ++ return -EIO; ++ } ++ ++ instance = kmalloc(sizeof(*instance), GFP_KERNEL); ++ memset(instance, 0, sizeof(*instance)); ++ ++ mutex_init(&instance->vchiq_mutex); ++ mutex_init(&instance->bulk_mutex); ++ ++ instance->bulk_scratch = vmalloc(PAGE_SIZE); ++ ++ params.callback_param = instance; ++ ++ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); ++ if (status) { ++ pr_err("Failed to open VCHI service connection (status=%d)\n", ++ status); ++ goto err_close_services; ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ *out_instance = instance; ++ ++ return 0; ++ ++err_close_services: ++ ++ vchi_service_close(instance->handle); ++ vfree(instance->bulk_scratch); ++ kfree(instance); ++ return -ENODEV; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h +new file mode 100644 +index 0000000..9d1d11e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.h +@@ -0,0 +1,178 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL interface to VCHIQ message passing ++ */ ++ ++#ifndef MMAL_VCHIQ_H ++#define MMAL_VCHIQ_H ++ ++#include "mmal-msg-format.h" ++ ++#define MAX_PORT_COUNT 4 ++ ++/* Maximum size of the format extradata. */ ++#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 ++ ++struct vchiq_mmal_instance; ++ ++enum vchiq_mmal_es_type { ++ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ ++ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ ++ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ ++ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ ++ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ ++}; ++ ++/* rectangle, used lots so it gets its own struct */ ++struct vchiq_mmal_rect { ++ s32 x; ++ s32 y; ++ s32 width; ++ s32 height; ++}; ++ ++struct vchiq_mmal_port_buffer { ++ unsigned int num; /* number of buffers */ ++ u32 size; /* size of buffers */ ++ u32 alignment; /* alignment of buffers */ ++}; ++ ++struct vchiq_mmal_port; ++ ++typedef void (*vchiq_mmal_buffer_cb)( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, struct mmal_buffer *buffer, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); ++ ++struct vchiq_mmal_port { ++ bool enabled; ++ u32 handle; ++ u32 type; /* port type, cached to use on port info set */ ++ u32 index; /* port index, cached to use on port info set */ ++ ++ /* component port belongs to, allows simple deref */ ++ struct vchiq_mmal_component *component; ++ ++ struct vchiq_mmal_port *connected; /* port conencted to */ ++ ++ /* buffer info */ ++ struct vchiq_mmal_port_buffer minimum_buffer; ++ struct vchiq_mmal_port_buffer recommended_buffer; ++ struct vchiq_mmal_port_buffer current_buffer; ++ ++ /* stream format */ ++ struct mmal_es_format format; ++ /* elementry stream format */ ++ union mmal_es_specific_format es; ++ ++ /* data buffers to fill */ ++ struct list_head buffers; ++ /* lock to serialise adding and removing buffers from list */ ++ spinlock_t slock; ++ /* count of how many buffer header refils have failed because ++ * there was no buffer to satisfy them ++ */ ++ int buffer_underflow; ++ /* callback on buffer completion */ ++ vchiq_mmal_buffer_cb buffer_cb; ++ /* callback context */ ++ void *cb_ctx; ++}; ++ ++struct vchiq_mmal_component { ++ bool enabled; ++ u32 handle; /* VideoCore handle for component */ ++ u32 inputs; /* Number of input ports */ ++ u32 outputs; /* Number of output ports */ ++ u32 clocks; /* Number of clock ports */ ++ struct vchiq_mmal_port control; /* control port */ ++ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ ++ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ ++ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ ++}; ++ ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); ++ ++/* Initialise a mmal component and its ports ++* ++*/ ++int vchiq_mmal_component_init( ++ struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out); ++ ++int vchiq_mmal_component_finalise( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_disable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++ ++ ++/* enable a mmal port ++ * ++ * enables a port and if a buffer callback provided enque buffer ++ * headers as apropriate for the port. ++ */ ++int vchiq_mmal_port_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb); ++ ++/* disable a port ++ * ++ * disable a port will dequeue any pending buffers ++ */ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 value_size); ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 *value_size); ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst); ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, ++ u32 *minor_out); ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buf); ++ ++#endif /* MMAL_VCHIQ_H */ + +From a386866d41f3d5603dc84a4763284e7510b72333 Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 6 Jul 2014 12:07:25 +0200 +Subject: [PATCH 019/104] spi-bcm2708: Prepare for Common Clock Framework + migration + +As part of migrating to use the Common Clock Framework, replace clk_enable() +with clk_prepare_enable() and clk_disable() with clk_disable_unprepare(). +This does not affect behaviour under the current clock implementation. + +Also add a missing clk_disable_unprepare() in the probe error path. + +Signed-off-by: Noralf Tronnes +--- + drivers/spi/spi-bcm2708.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +index b04a57d..349d21f 100644 +--- a/drivers/spi/spi-bcm2708.c ++++ b/drivers/spi/spi-bcm2708.c +@@ -545,7 +545,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + } + + /* initialise the hardware */ +- clk_enable(clk); ++ clk_prepare_enable(clk); + bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX); + + err = spi_register_master(master); +@@ -561,6 +561,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + + out_free_irq: + free_irq(bs->irq, master); ++ clk_disable_unprepare(bs->clk); + out_workqueue: + destroy_workqueue(bs->workq); + out_iounmap: +@@ -585,7 +586,7 @@ static int bcm2708_spi_remove(struct platform_device *pdev) + + flush_work(&bs->work); + +- clk_disable(bs->clk); ++ clk_disable_unprepare(bs->clk); + clk_put(bs->clk); + free_irq(bs->irq, master); + iounmap(bs->base); + +From 3d32fd6b5984a34d576228ef9f1c7f1c3d2002cf Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 6 Jul 2014 12:09:30 +0200 +Subject: [PATCH 020/104] BCM2708: Migrate to the Common Clock Framework + +As part of moving towards using Device Tree, the Common Clock Framework +has to be used instead of the BCM2708 clock implementation. + +Selecting COMMON_CLK removes the need to set CLKDEV_LOOKUP and HAVE_CLK explicitly. + +CONFIG_ARCH_BCM2708_CHIPIT #ifdef's are removed. They are no longer in use. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/Kconfig | 3 +- + arch/arm/mach-bcm2708/Makefile | 2 +- + arch/arm/mach-bcm2708/bcm2708.c | 79 +++++++++++++++++------------------------ + arch/arm/mach-bcm2708/clock.c | 61 ------------------------------- + arch/arm/mach-bcm2708/clock.h | 24 ------------- + 5 files changed, 34 insertions(+), 135 deletions(-) + delete mode 100644 arch/arm/mach-bcm2708/clock.c + delete mode 100644 arch/arm/mach-bcm2708/clock.h + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 9fcd395..c495965 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -373,11 +373,10 @@ config ARCH_BCM2708 + bool "Broadcom BCM2708 family" + select CPU_V6 + select ARM_AMBA +- select HAVE_CLK + select HAVE_SCHED_CLOCK + select NEED_MACH_GPIO_H + select NEED_MACH_MEMORY_H +- select CLKDEV_LOOKUP ++ select COMMON_CLK + select ARCH_HAS_CPUFREQ + select GENERIC_CLOCKEVENTS + select ARM_ERRATA_411920 +diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile +index a722f3f..21e3521 100644 +--- a/arch/arm/mach-bcm2708/Makefile ++++ b/arch/arm/mach-bcm2708/Makefile +@@ -2,6 +2,6 @@ + # Makefile for the linux kernel. + # + +-obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o ++obj-$(CONFIG_MACH_BCM2708) += bcm2708.o armctrl.o vcio.o power.o dma.o + obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o + obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 752dd46..080e260 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -27,6 +27,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -57,7 +59,6 @@ + + #include "bcm2708.h" + #include "armctrl.h" +-#include "clock.h" + + #ifdef CONFIG_BCM_VC_CMA + #include +@@ -78,7 +79,7 @@ + + /* command line parameters */ + static unsigned boardrev, serial; +-static unsigned uart_clock; ++static unsigned uart_clock = UART0_CLOCK; + static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; +@@ -188,51 +189,39 @@ static void __init bcm2708_clocksource_init(void) + } + } + ++struct clk __init *bcm2708_clk_register(const char *name, unsigned long fixed_rate) ++{ ++ struct clk *clk; + +-/* +- * These are fixed clocks. +- */ +-static struct clk ref24_clk = { +- .rate = UART0_CLOCK, /* The UART is clocked at 3MHz via APB_CLK */ +-}; ++ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, ++ fixed_rate); ++ if (IS_ERR(clk)) ++ pr_err("%s not registered\n", name); + +-static struct clk osc_clk = { +-#ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 27000000, +-#else +- .rate = 500000000, /* ARM clock is set from the VideoCore booter */ +-#endif +-}; ++ return clk; ++} + +-/* warning - the USB needs a clock > 34MHz */ ++void __init bcm2708_register_clkdev(struct clk *clk, const char *name) ++{ ++ int ret; + +-static struct clk sdhost_clk = { +-#ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 4000000, /* 4MHz */ +-#else +- .rate = 250000000, /* 250MHz */ +-#endif +-}; ++ ret = clk_register_clkdev(clk, NULL, name); ++ if (ret) ++ pr_err("%s alias not registered\n", name); ++} + +-static struct clk_lookup lookups[] = { +- { /* UART0 */ +- .dev_id = "dev:f1", +- .clk = &ref24_clk, +- }, +- { /* USB */ +- .dev_id = "bcm2708_usb", +- .clk = &osc_clk, +- }, { /* SPI */ +- .dev_id = "bcm2708_spi.0", +- .clk = &sdhost_clk, +- }, { /* BSC0 */ +- .dev_id = "bcm2708_i2c.0", +- .clk = &sdhost_clk, +- }, { /* BSC1 */ +- .dev_id = "bcm2708_i2c.1", +- .clk = &sdhost_clk, +- } +-}; ++void __init bcm2708_init_clocks(void) ++{ ++ struct clk *clk; ++ ++ clk = bcm2708_clk_register("uart0_clk", uart_clock); ++ bcm2708_register_clkdev(clk, "dev:f1"); ++ ++ clk = bcm2708_clk_register("sdhost_clk", 250000000); ++ bcm2708_register_clkdev(clk, "bcm2708_spi.0"); ++ bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); ++ bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); ++} + + #define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } + #define UART0_DMA { 15, 14 } +@@ -685,11 +674,7 @@ void __init bcm2708_init(void) + printk("bcm2708.uart_clock = %d\n", uart_clock); + pm_power_off = bcm2708_power_off; + +- if (uart_clock) +- lookups[0].clk->rate = uart_clock; +- +- for (i = 0; i < ARRAY_SIZE(lookups); i++) +- clkdev_add(&lookups[i]); ++ bcm2708_init_clocks(); + + bcm_register_device(&bcm2708_dmaman_device); + bcm_register_device(&bcm2708_dmaengine_device); +diff --git a/arch/arm/mach-bcm2708/clock.c b/arch/arm/mach-bcm2708/clock.c +deleted file mode 100644 +index 4fc556e..0000000 +--- a/arch/arm/mach-bcm2708/clock.c ++++ /dev/null +@@ -1,61 +0,0 @@ +-/* +- * linux/arch/arm/mach-bcm2708/clock.c +- * +- * Copyright (C) 2010 Broadcom +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "clock.h" +- +-int clk_enable(struct clk *clk) +-{ +- return 0; +-} +-EXPORT_SYMBOL(clk_enable); +- +-void clk_disable(struct clk *clk) +-{ +-} +-EXPORT_SYMBOL(clk_disable); +- +-unsigned long clk_get_rate(struct clk *clk) +-{ +- return clk->rate; +-} +-EXPORT_SYMBOL(clk_get_rate); +- +-long clk_round_rate(struct clk *clk, unsigned long rate) +-{ +- return clk->rate; +-} +-EXPORT_SYMBOL(clk_round_rate); +- +-int clk_set_rate(struct clk *clk, unsigned long rate) +-{ +- return -EIO; +-} +-EXPORT_SYMBOL(clk_set_rate); +diff --git a/arch/arm/mach-bcm2708/clock.h b/arch/arm/mach-bcm2708/clock.h +deleted file mode 100644 +index 5f9d725..0000000 +--- a/arch/arm/mach-bcm2708/clock.h ++++ /dev/null +@@ -1,24 +0,0 @@ +-/* +- * linux/arch/arm/mach-bcm2708/clock.h +- * +- * Copyright (C) 2010 Broadcom +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation; either version 2 of the License, or +- * (at your option) any later version. +- * +- * This program is distributed in the hope that it will be useful, +- * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- */ +-struct module; +- +-struct clk { +- unsigned long rate; +-}; + +From fc0495fc9d5db39fc59407514d6fb2ca1ec060ef Mon Sep 17 00:00:00 2001 +From: notro +Date: Wed, 9 Jul 2014 14:46:08 +0200 +Subject: [PATCH 021/104] BCM2708: Add core Device Tree support + +Add the bare minimum needed to boot BCM2708 from a Device Tree. + +Signed-off-by: Noralf Tronnes + +BCM2708: DT: change 'axi' nodename to 'soc' + +Change DT node named 'axi' to 'soc' so it matches ARCH_BCM2835. +The VC4 bootloader fills in certain properties in the 'axi' subtree, +but since this is part of an upstreaming effort, the name is changed. + +Signed-off-by: Noralf Tronnes notro@tronnes.org + +BCM2708_DT: Correct length of the peripheral space +--- + arch/arm/boot/dts/Makefile | 21 ++++ + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 81 +++++++++++++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 81 +++++++++++++++ + arch/arm/boot/dts/bcm2708.dtsi | 86 ++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 165 +++++++++++++++++++++++++++++++ + arch/arm/mach-bcm2708/Kconfig | 8 ++ + arch/arm/mach-bcm2708/bcm2708.c | 45 ++++++++- + 7 files changed, 484 insertions(+), 3 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-plus.dts + create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b.dts + create mode 100644 arch/arm/boot/dts/bcm2708.dtsi + create mode 100644 arch/arm/boot/dts/bcm2709.dtsi + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index a1c776b..c86833d 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -1,5 +1,17 @@ + ifeq ($(CONFIG_OF),y) + ++dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb ++dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb ++dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb ++ ++# Raspberry Pi ++ifeq ($(CONFIG_BCM2708_DT),y) ++ RPI_DT_OVERLAYS=y ++endif ++ifeq ($(CONFIG_BCM2709_DT),y) ++ RPI_DT_OVERLAYS=y ++endif ++ + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb + # Keep at91 dtb files sorted alphabetically for each SoC +@@ -645,7 +657,16 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \ + mt6592-evb.dtb \ + mt8127-moose.dtb \ + mt8135-evbp1.dtb ++ ++targets += dtbs dtbs_install ++targets += $(dtb-y) ++ + endif + + always := $(dtb-y) + clean-files := *.dtb ++ ++# Enable fixups to support overlays on BCM2708 platforms ++ifeq ($(RPI_DT_OVERLAYS),y) ++ DTC_FLAGS ?= -@ ++endif +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +new file mode 100644 +index 0000000..983c23f +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++ ++/include/ "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B+"; ++ ++ aliases { ++ spi0 = &spi0; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2s = &i2s; ++ gpio = &gpio; ++ sound = &sound; ++ }; ++ ++ sound: sound { ++ }; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <7 8 9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; ++ }; ++ ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { ++ brcm,pins = <18 19 20 21>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ ++ spidev@0{ ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++ ++ spidev@1{ ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++}; ++ ++&i2c0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +new file mode 100644 +index 0000000..d8c6d15 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -0,0 +1,81 @@ ++/dts-v1/; ++ ++/include/ "bcm2708.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "Raspberry Pi Model B"; ++ ++ aliases { ++ spi0 = &spi0; ++ i2c0 = &i2c0; ++ i2c1 = &i2c1; ++ i2s = &i2s; ++ gpio = &gpio; ++ sound = &sound; ++ }; ++ ++ sound: sound { ++ }; ++}; ++ ++&gpio { ++ spi0_pins: spi0_pins { ++ brcm,pins = <7 8 9 10 11>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++ ++ i2c0_pins: i2c0 { ++ brcm,pins = <0 1>; ++ brcm,function = <4>; ++ }; ++ ++ i2c1_pins: i2c1 { ++ brcm,pins = <2 3>; ++ brcm,function = <4>; ++ }; ++ ++ i2s_pins: i2s { ++ brcm,pins = <28 29 30 31>; ++ brcm,function = <4>; /* alt0 */ ++ }; ++}; ++ ++&spi0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&spi0_pins>; ++ ++ spidev@0{ ++ compatible = "spidev"; ++ reg = <0>; /* CE0 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++ ++ spidev@1{ ++ compatible = "spidev"; ++ reg = <1>; /* CE1 */ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ spi-max-frequency = <500000>; ++ }; ++}; ++ ++&i2c0 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c0_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2c1_pins>; ++ clock-frequency = <100000>; ++}; ++ ++&i2s { ++ #sound-dai-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&i2s_pins>; ++}; +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +new file mode 100644 +index 0000000..96b7311 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -0,0 +1,86 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ model = "BCM2708"; ++ ++ interrupt-parent = <&intc>; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x7e000000 0x20000000 0x01000000>; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +new file mode 100644 +index 0000000..15c7e65 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -0,0 +1,165 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ compatible = "brcm,bcm2709"; ++ model = "BCM2709"; ++ ++ interrupt-parent = <&intc>; ++ ++ chosen { ++ /* No padding required - the boot loader can do that. */ ++ bootargs = ""; ++ }; ++ ++ soc: soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0x7e000000 0x3f000000 0x01000000>; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ leds: leds { ++ compatible = "gpio-leds"; ++ ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ }; ++ }; ++ ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupts = <3 9>; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; ++ }; ++ ++ timer { ++ compatible = "arm,armv7-timer"; ++ clock-frequency = <19200000>; ++ interrupts = <3 0>, // PHYS_SECURE_PPI ++ <3 1>, // PHYS_NONSECURE_PPI ++ <3 3>, // VIRT_PPI ++ <3 2>; // HYP_PPI ++ always-on; ++ }; ++ ++ cpus: cpus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ v7_cpu0: cpu@0 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf00>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu1: cpu@1 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf01>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu2: cpu@2 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf02>; ++ clock-frequency = <800000000>; ++ }; ++ ++ v7_cpu3: cpu@3 { ++ device_type = "cpu"; ++ compatible = "arm,cortex-a7"; ++ reg = <0xf03>; ++ clock-frequency = <800000000>; ++ }; ++ }; ++ ++ __overrides__ { ++ arm_freq = <&v7_cpu0>, "clock-frequency:0", ++ <&v7_cpu1>, "clock-frequency:0", ++ <&v7_cpu2>, "clock-frequency:0", ++ <&v7_cpu3>, "clock-frequency:0"; ++ }; ++}; +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index e151ed4..182e7ba 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -9,6 +9,14 @@ config MACH_BCM2708 + help + Include support for the Broadcom(R) BCM2708 platform. + ++config BCM2708_DT ++ bool "BCM2708 Device Tree support" ++ depends on MACH_BCM2708 ++ default n ++ select USE_OF ++ help ++ Enable Device Tree support for BCM2708 ++ + config BCM2708_GPIO + bool "BCM2708 gpio support" + depends on MACH_BCM2708 +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 080e260..496cbcd 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -33,7 +33,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include + #include +@@ -84,6 +86,8 @@ static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; + ++static unsigned use_dt = 0; ++ + static void __init bcm2708_init_led(void); + + void __init bcm2708_init_irq(void) +@@ -599,6 +603,16 @@ int __init bcm_register_device(struct platform_device *pdev) + return ret; + } + ++/* ++ * Use these macros for platform and i2c devices that are present in the ++ * Device Tree. This way the devices are only added on non-DT systems. ++ */ ++#define bcm_register_device_dt(pdev) \ ++ if (!use_dt) bcm_register_device(pdev) ++ ++#define i2c_register_board_info_dt(busnum, info, n) \ ++ if (!use_dt) i2c_register_board_info(busnum, info, n) ++ + int calc_rsts(int partition) + { + return PM_PASSWORD | +@@ -664,6 +678,24 @@ static void bcm2708_power_off(void) + } + } + ++#ifdef CONFIG_OF ++static void __init bcm2708_dt_init(void) ++{ ++ int ret; ++ ++ of_clk_init(NULL); ++ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); ++ if (ret) { ++ pr_err("of_platform_populate failed: %d\n", ret); ++ /* Proceed as if CONFIG_OF was not defined */ ++ } else { ++ use_dt = 1; ++ } ++} ++#else ++static void __init bcm2708_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ + void __init bcm2708_init(void) + { + int i; +@@ -675,6 +707,7 @@ void __init bcm2708_init(void) + pm_power_off = bcm2708_power_off; + + bcm2708_init_clocks(); ++ bcm2708_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); + bcm_register_device(&bcm2708_dmaengine_device); +@@ -842,9 +875,9 @@ static struct platform_device bcm2708_led_device = { + + static void __init bcm2708_init_led(void) + { +- bcm2708_leds[0].gpio = disk_led_gpio; +- bcm2708_leds[0].active_low = disk_led_active_low; +- platform_device_register(&bcm2708_led_device); ++ bcm2708_leds[0].gpio = disk_led_gpio; ++ bcm2708_leds[0].active_low = disk_led_active_low; ++ bcm_register_device_dt(&bcm2708_led_device); + } + #else + static inline void bcm2708_init_led(void) +@@ -869,6 +902,11 @@ static void __init board_reserve(void) + #endif + } + ++static const char * const bcm2708_compat[] = { ++ "brcm,bcm2708", ++ NULL ++}; ++ + MACHINE_START(BCM2708, "BCM2708") + /* Maintainer: Broadcom Europe Ltd. */ + .map_io = bcm2708_map_io, +@@ -878,6 +916,7 @@ MACHINE_START(BCM2708, "BCM2708") + .init_early = bcm2708_init_early, + .reserve = board_reserve, + .restart = bcm2708_restart, ++ .dt_compat = bcm2708_compat, + MACHINE_END + + module_param(boardrev, uint, 0644); + +From 3899231316f50d34a0843fc025ef541ebf5b1c1c Mon Sep 17 00:00:00 2001 +From: Siarhei Siamashka +Date: Mon, 17 Jun 2013 13:32:11 +0300 +Subject: [PATCH 022/104] fbdev: add FBIOCOPYAREA ioctl + +Based on the patch authored by Ali Gholami Rudi at + https://lkml.org/lkml/2009/7/13/153 + +Provide an ioctl for userspace applications, but only if this operation +is hardware accelerated (otherwide it does not make any sense). + +Signed-off-by: Siarhei Siamashka +--- + drivers/video/fbdev/core/fbmem.c | 30 ++++++++++++++++++++++++++++++ + include/uapi/linux/fb.h | 5 +++++ + 2 files changed, 35 insertions(+) + +diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c +index 0705d88..771992a 100644 +--- a/drivers/video/fbdev/core/fbmem.c ++++ b/drivers/video/fbdev/core/fbmem.c +@@ -1084,6 +1084,25 @@ fb_blank(struct fb_info *info, int blank) + } + EXPORT_SYMBOL(fb_blank); + ++static int fb_copyarea_user(struct fb_info *info, ++ struct fb_copyarea *copy) ++{ ++ int ret = 0; ++ if (!lock_fb_info(info)) ++ return -ENODEV; ++ if (copy->dx + copy->width > info->var.xres || ++ copy->sx + copy->width > info->var.xres || ++ copy->dy + copy->height > info->var.yres || ++ copy->sy + copy->height > info->var.yres) { ++ ret = -EINVAL; ++ goto out; ++ } ++ info->fbops->fb_copyarea(info, copy); ++out: ++ unlock_fb_info(info); ++ return ret; ++} ++ + static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) + { +@@ -1094,6 +1113,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + struct fb_event event; ++ struct fb_copyarea copy; + void __user *argp = (void __user *)arg; + long ret = 0; + +@@ -1211,6 +1231,15 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unlock_fb_info(info); + console_unlock(); + break; ++ case FBIOCOPYAREA: ++ if (info->flags & FBINFO_HWACCEL_COPYAREA) { ++ /* only provide this ioctl if it is accelerated */ ++ if (copy_from_user(©, argp, sizeof(copy))) ++ return -EFAULT; ++ ret = fb_copyarea_user(info, ©); ++ break; ++ } ++ /* fall through */ + default: + if (!lock_fb_info(info)) + return -ENODEV; +@@ -1365,6 +1394,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, + case FBIOPAN_DISPLAY: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: ++ case FBIOCOPYAREA: + arg = (unsigned long) compat_ptr(arg); + case FBIOBLANK: + ret = do_fb_ioctl(info, cmd, arg); +diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h +index fb795c3..fa72af0 100644 +--- a/include/uapi/linux/fb.h ++++ b/include/uapi/linux/fb.h +@@ -34,6 +34,11 @@ + #define FBIOPUT_MODEINFO 0x4617 + #define FBIOGET_DISPINFO 0x4618 + #define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32) ++/* ++ * HACK: use 'z' in order not to clash with any other ioctl numbers which might ++ * be concurrently added to the mainline kernel ++ */ ++#define FBIOCOPYAREA _IOW('z', 0x21, struct fb_copyarea) + + #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ + #define FB_TYPE_PLANES 1 /* Non interleaved planes */ + +From 33cab037eadd63b83b445c4036f4feda8cfeaf59 Mon Sep 17 00:00:00 2001 +From: Harm Hanemaaijer +Date: Thu, 20 Jun 2013 20:21:39 +0200 +Subject: [PATCH 025/104] Speed up console framebuffer imageblit function + +Especially on platforms with a slower CPU but a relatively high +framebuffer fill bandwidth, like current ARM devices, the existing +console monochrome imageblit function used to draw console text is +suboptimal for common pixel depths such as 16bpp and 32bpp. The existing +code is quite general and can deal with several pixel depths. By creating +special case functions for 16bpp and 32bpp, by far the most common pixel +formats used on modern systems, a significant speed-up is attained +which can be readily felt on ARM-based devices like the Raspberry Pi +and the Allwinner platform, but should help any platform using the +fb layer. + +The special case functions allow constant folding, eliminating a number +of instructions including divide operations, and allow the use of an +unrolled loop, eliminating instructions with a variable shift size, +reducing source memory access instructions, and eliminating excessive +branching. These unrolled loops also allow much better code optimization +by the C compiler. The code that selects which optimized variant is used +is also simplified, eliminating integer divide instructions. + +The speed-up, measured by timing 'cat file.txt' in the console, varies +between 40% and 70%, when testing on the Raspberry Pi and Allwinner +ARM-based platforms, depending on font size and the pixel depth, with +the greater benefit for 32bpp. + +Signed-off-by: Harm Hanemaaijer +--- + drivers/video/fbdev/core/cfbimgblt.c | 152 +++++++++++++++++++++++++++++++++-- + 1 file changed, 147 insertions(+), 5 deletions(-) + +diff --git a/drivers/video/fbdev/core/cfbimgblt.c b/drivers/video/fbdev/core/cfbimgblt.c +index a2bb276..436494f 100644 +--- a/drivers/video/fbdev/core/cfbimgblt.c ++++ b/drivers/video/fbdev/core/cfbimgblt.c +@@ -28,6 +28,11 @@ + * + * Also need to add code to deal with cards endians that are different than + * the native cpu endians. I also need to deal with MSB position in the word. ++ * Modified by Harm Hanemaaijer (fgenfb@yahoo.com) 2013: ++ * - Provide optimized versions of fast_imageblit for 16 and 32bpp that are ++ * significantly faster than the previous implementation. ++ * - Simplify the fast/slow_imageblit selection code, avoiding integer ++ * divides. + */ + #include + #include +@@ -262,6 +267,133 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info * + } + } + ++/* ++ * Optimized fast_imageblit for bpp == 16. ppw = 2, bit_mask = 3 folded ++ * into the code, main loop unrolled. ++ */ ++ ++static inline void fast_imageblit16(const struct fb_image *image, ++ struct fb_info *p, u8 __iomem * dst1, ++ u32 fgcolor, u32 bgcolor) ++{ ++ u32 fgx = fgcolor, bgx = bgcolor; ++ u32 spitch = (image->width + 7) / 8; ++ u32 end_mask, eorx; ++ const char *s = image->data, *src; ++ u32 __iomem *dst; ++ const u32 *tab = NULL; ++ int i, j, k; ++ ++ tab = fb_be_math(p) ? cfb_tab16_be : cfb_tab16_le; ++ ++ fgx <<= 16; ++ bgx <<= 16; ++ fgx |= fgcolor; ++ bgx |= bgcolor; ++ ++ eorx = fgx ^ bgx; ++ k = image->width / 2; ++ ++ for (i = image->height; i--;) { ++ dst = (u32 __iomem *) dst1; ++ src = s; ++ ++ j = k; ++ while (j >= 4) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 6) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 4) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 2) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[bits & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ src++; ++ j -= 4; ++ } ++ if (j != 0) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 6) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ if (j >= 2) { ++ end_mask = tab[(bits >> 4) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ if (j == 3) { ++ end_mask = tab[(bits >> 2) & 3]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst); ++ } ++ } ++ } ++ dst1 += p->fix.line_length; ++ s += spitch; ++ } ++} ++ ++/* ++ * Optimized fast_imageblit for bpp == 32. ppw = 1, bit_mask = 1 folded ++ * into the code, main loop unrolled. ++ */ ++ ++static inline void fast_imageblit32(const struct fb_image *image, ++ struct fb_info *p, u8 __iomem * dst1, ++ u32 fgcolor, u32 bgcolor) ++{ ++ u32 fgx = fgcolor, bgx = bgcolor; ++ u32 spitch = (image->width + 7) / 8; ++ u32 end_mask, eorx; ++ const char *s = image->data, *src; ++ u32 __iomem *dst; ++ const u32 *tab = NULL; ++ int i, j, k; ++ ++ tab = cfb_tab32; ++ ++ eorx = fgx ^ bgx; ++ k = image->width; ++ ++ for (i = image->height; i--;) { ++ dst = (u32 __iomem *) dst1; ++ src = s; ++ ++ j = k; ++ while (j >= 8) { ++ u8 bits = *src; ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 6) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 5) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 4) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 3) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 2) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[(bits >> 1) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ end_mask = tab[bits & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ src++; ++ j -= 8; ++ } ++ if (j != 0) { ++ u32 bits = (u32) * src; ++ while (j > 1) { ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst++); ++ bits <<= 1; ++ j--; ++ } ++ end_mask = tab[(bits >> 7) & 1]; ++ FB_WRITEL((end_mask & eorx) ^ bgx, dst); ++ } ++ dst1 += p->fix.line_length; ++ s += spitch; ++ } ++} ++ + void cfb_imageblit(struct fb_info *p, const struct fb_image *image) + { + u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0; +@@ -294,11 +426,21 @@ void cfb_imageblit(struct fb_info *p, const struct fb_image *image) + bgcolor = image->bg_color; + } + +- if (32 % bpp == 0 && !start_index && !pitch_index && +- ((width & (32/bpp-1)) == 0) && +- bpp >= 8 && bpp <= 32) +- fast_imageblit(image, p, dst1, fgcolor, bgcolor); +- else ++ if (!start_index && !pitch_index) { ++ if (bpp == 32) ++ fast_imageblit32(image, p, dst1, fgcolor, ++ bgcolor); ++ else if (bpp == 16 && (width & 1) == 0) ++ fast_imageblit16(image, p, dst1, fgcolor, ++ bgcolor); ++ else if (bpp == 8 && (width & 3) == 0) ++ fast_imageblit(image, p, dst1, fgcolor, ++ bgcolor); ++ else ++ slow_imageblit(image, p, dst1, fgcolor, ++ bgcolor, ++ start_index, pitch_index); ++ } else + slow_imageblit(image, p, dst1, fgcolor, bgcolor, + start_index, pitch_index); + } else + +From 4791febd5ba2ec59bdfc92fa0dc3a73119853871 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Tue, 26 Mar 2013 17:26:38 +0000 +Subject: [PATCH 026/104] Allow mac address to be set in smsc95xx + +Signed-off-by: popcornmix +--- + drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 56 insertions(+) + +diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c +index 26423ad..e29a323 100644 +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -59,6 +59,7 @@ + #define SUSPEND_SUSPEND3 (0x08) + #define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ + SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) ++#define MAC_ADDR_LEN (6) + + struct smsc95xx_priv { + u32 mac_cr; +@@ -74,6 +75,10 @@ static bool turbo_mode = true; + module_param(turbo_mode, bool, 0644); + MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); + ++static char *macaddr = ":"; ++module_param(macaddr, charp, 0); ++MODULE_PARM_DESC(macaddr, "MAC address"); ++ + static int __must_check __smsc95xx_read_reg(struct usbnet *dev, u32 index, + u32 *data, int in_pm) + { +@@ -763,8 +768,59 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + } + ++/* Check the macaddr module parameter for a MAC address */ ++static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac) ++{ ++ int i, j, got_num, num; ++ u8 mtbl[MAC_ADDR_LEN]; ++ ++ if (macaddr[0] == ':') ++ return 0; ++ ++ i = 0; ++ j = 0; ++ num = 0; ++ got_num = 0; ++ while (j < MAC_ADDR_LEN) { ++ if (macaddr[i] && macaddr[i] != ':') { ++ got_num++; ++ if ('0' <= macaddr[i] && macaddr[i] <= '9') ++ num = num * 16 + macaddr[i] - '0'; ++ else if ('A' <= macaddr[i] && macaddr[i] <= 'F') ++ num = num * 16 + 10 + macaddr[i] - 'A'; ++ else if ('a' <= macaddr[i] && macaddr[i] <= 'f') ++ num = num * 16 + 10 + macaddr[i] - 'a'; ++ else ++ break; ++ i++; ++ } else if (got_num == 2) { ++ mtbl[j++] = (u8) num; ++ num = 0; ++ got_num = 0; ++ i++; ++ } else { ++ break; ++ } ++ } ++ ++ if (j == MAC_ADDR_LEN) { ++ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: " ++ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2], ++ mtbl[3], mtbl[4], mtbl[5]); ++ for (i = 0; i < MAC_ADDR_LEN; i++) ++ dev_mac[i] = mtbl[i]; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ + static void smsc95xx_init_mac_address(struct usbnet *dev) + { ++ /* Check module parameters */ ++ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) ++ return; ++ + /* try reading mac address from EEPROM */ + if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, + dev->net->dev_addr) == 0) { + +From 7958a3ae050a3d5f88132f2ae0430ecebddfff57 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 8 May 2013 11:46:50 +0100 +Subject: [PATCH 027/104] enabling the realtime clock 1-wire chip DS1307 and + 1-wire on GPIO4 (as a module) + +1-wire: Add support for configuring pin for w1-gpio kernel module +See: https://github.com/raspberrypi/linux/pull/457 + +Add bitbanging pullups, use them for w1-gpio + +Allows parasite power to work, uses module option pullup=1 + +bcm2708: Ensure 1-wire pullup is disabled by default, and expose as module parameter + +Signed-off-by: Alex J Lennon + +w1-gpio: Add gpiopin module parameter and correctly free up gpio pull-up pin, if set + +Signed-off-by: Alex J Lennon +--- + arch/arm/mach-bcm2708/bcm2708.c | 29 ++++++++++++++++++++++ + drivers/w1/masters/w1-gpio.c | 55 +++++++++++++++++++++++++++++++++++++---- + drivers/w1/w1.h | 6 +++++ + drivers/w1/w1_int.c | 14 +++++++++++ + drivers/w1/w1_io.c | 18 +++++++++++--- + 5 files changed, 114 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 496cbcd..5873f8b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -79,12 +80,19 @@ + */ + #define DMA_MASK_BITS_COMMON 32 + ++// use GPIO 4 for the one-wire GPIO pin, if enabled ++#define W1_GPIO 4 ++// ensure one-wire GPIO pullup is disabled by default ++#define W1_PULLUP -1 ++ + /* command line parameters */ + static unsigned boardrev, serial; + static unsigned uart_clock = UART0_CLOCK; + static unsigned disk_led_gpio = 16; + static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; ++static unsigned w1_gpio_pin = W1_GPIO; ++static unsigned w1_gpio_pullup = W1_PULLUP; + + static unsigned use_dt = 0; + +@@ -256,6 +264,20 @@ static struct platform_device bcm2708_dmaengine_device = { + .id = -1, + }; + ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++static struct w1_gpio_platform_data w1_gpio_pdata = { ++ .pin = W1_GPIO, ++ .ext_pullup_enable_pin = W1_PULLUP, ++ .is_open_drain = 0, ++}; ++ ++static struct platform_device w1_device = { ++ .name = "w1-gpio", ++ .id = -1, ++ .dev.platform_data = &w1_gpio_pdata, ++}; ++#endif ++ + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -715,6 +737,11 @@ void __init bcm2708_init(void) + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device(&bcm2708_gpio_device); + #endif ++#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) ++ w1_gpio_pdata.pin = w1_gpio_pin; ++ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; ++ bcm_register_device_dt(&w1_device); ++#endif + bcm_register_device(&bcm2708_systemtimer_device); + bcm_register_device(&bcm2708_fb_device); + bcm_register_device(&bcm2708_usb_device); +@@ -925,3 +952,5 @@ module_param(uart_clock, uint, 0644); + module_param(disk_led_gpio, uint, 0644); + module_param(disk_led_active_low, uint, 0644); + module_param(reboot_part, uint, 0644); ++module_param(w1_gpio_pin, uint, 0644); ++module_param(w1_gpio_pullup, uint, 0644); +diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c +index b99a932..3ad15cc 100644 +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -23,6 +23,15 @@ + #include "../w1.h" + #include "../w1_int.h" + ++static int w1_gpio_pullup = -1; ++static int w1_gpio_pullup_orig = -1; ++module_param_named(pullup, w1_gpio_pullup, int, 0); ++MODULE_PARM_DESC(pullup, "GPIO pin pullup number"); ++static int w1_gpio_pin = -1; ++static int w1_gpio_pin_orig = -1; ++module_param_named(gpiopin, w1_gpio_pin, int, 0); ++MODULE_PARM_DESC(gpiopin, "GPIO pin number"); ++ + static u8 w1_gpio_set_pullup(void *data, int delay) + { + struct w1_gpio_platform_data *pdata = data; +@@ -67,6 +76,16 @@ static u8 w1_gpio_read_bit(void *data) + return gpio_get_value(pdata->pin) ? 1 : 0; + } + ++static void w1_gpio_bitbang_pullup(void *data, u8 on) ++{ ++ struct w1_gpio_platform_data *pdata = data; ++ ++ if (on) ++ gpio_direction_output(pdata->pin, 1); ++ else ++ gpio_direction_input(pdata->pin); ++} ++ + #if defined(CONFIG_OF) + static struct of_device_id w1_gpio_dt_ids[] = { + { .compatible = "w1-gpio" }, +@@ -113,13 +132,15 @@ static int w1_gpio_probe_dt(struct platform_device *pdev) + static int w1_gpio_probe(struct platform_device *pdev) + { + struct w1_bus_master *master; +- struct w1_gpio_platform_data *pdata; ++ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data; + int err; + +- if (of_have_populated_dt()) { +- err = w1_gpio_probe_dt(pdev); +- if (err < 0) +- return err; ++ if(pdata == NULL) { ++ if (of_have_populated_dt()) { ++ err = w1_gpio_probe_dt(pdev); ++ if (err < 0) ++ return err; ++ } + } + + pdata = dev_get_platdata(&pdev->dev); +@@ -136,6 +157,19 @@ static int w1_gpio_probe(struct platform_device *pdev) + return -ENOMEM; + } + ++ w1_gpio_pin_orig = pdata->pin; ++ w1_gpio_pullup_orig = pdata->ext_pullup_enable_pin; ++ ++ if(gpio_is_valid(w1_gpio_pin)) { ++ pdata->pin = w1_gpio_pin; ++ pdata->ext_pullup_enable_pin = -1; ++ } ++ if(gpio_is_valid(w1_gpio_pullup)) { ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup; ++ } ++ ++ dev_info(&pdev->dev, "gpio pin %d, gpio pullup pin %d\n", pdata->pin, pdata->ext_pullup_enable_pin); ++ + err = devm_gpio_request(&pdev->dev, pdata->pin, "w1"); + if (err) { + dev_err(&pdev->dev, "gpio_request (pin) failed\n"); +@@ -165,6 +199,14 @@ static int w1_gpio_probe(struct platform_device *pdev) + master->set_pullup = w1_gpio_set_pullup; + } + ++ if (gpio_is_valid(w1_gpio_pullup)) { ++ if (pdata->is_open_drain) ++ printk(KERN_ERR "w1-gpio 'pullup' option " ++ "doesn't work with open drain GPIO\n"); ++ else ++ master->bitbang_pullup = w1_gpio_bitbang_pullup; ++ } ++ + err = w1_add_master_device(master); + if (err) { + dev_err(&pdev->dev, "w1_add_master device failed\n"); +@@ -195,6 +237,9 @@ static int w1_gpio_remove(struct platform_device *pdev) + + w1_remove_master_device(master); + ++ pdata->pin = w1_gpio_pin_orig; ++ pdata->ext_pullup_enable_pin = w1_gpio_pullup_orig; ++ + return 0; + } + +diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h +index 56a49ba..881d728 100644 +--- a/drivers/w1/w1.h ++++ b/drivers/w1/w1.h +@@ -171,6 +171,12 @@ struct w1_bus_master + + u8 (*set_pullup)(void *, int); + ++ /** ++ * Turns the pullup on/off in bitbanging mode, takes an on/off argument. ++ * @return -1=Error, 0=completed ++ */ ++ void (*bitbang_pullup) (void *, u8); ++ + void (*search)(void *, struct w1_master *, + u8, w1_slave_found_callback); + }; +diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c +index 47249a3..a4b4a8d 100644 +--- a/drivers/w1/w1_int.c ++++ b/drivers/w1/w1_int.c +@@ -123,6 +123,20 @@ int w1_add_master_device(struct w1_bus_master *master) + return(-EINVAL); + } + ++ /* bitbanging hardware uses bitbang_pullup, other hardware uses set_pullup ++ * and takes care of timing itself */ ++ if (!master->write_byte && !master->touch_bit && master->set_pullup) { ++ printk(KERN_ERR "w1_add_master_device: set_pullup requires " ++ "write_byte or touch_bit, disabling\n"); ++ master->set_pullup = NULL; ++ } ++ ++ if (master->set_pullup && master->bitbang_pullup) { ++ printk(KERN_ERR "w1_add_master_device: set_pullup should not " ++ "be set when bitbang_pullup is used, disabling\n"); ++ master->set_pullup = NULL; ++ } ++ + /* Lock until the device is added (or not) to w1_masters. */ + mutex_lock(&w1_mlock); + /* Search for the first available id (starting at 1). */ +diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c +index 2820924..fd0550f 100644 +--- a/drivers/w1/w1_io.c ++++ b/drivers/w1/w1_io.c +@@ -134,10 +134,22 @@ static void w1_pre_write(struct w1_master *dev) + static void w1_post_write(struct w1_master *dev) + { + if (dev->pullup_duration) { +- if (dev->enable_pullup && dev->bus_master->set_pullup) +- dev->bus_master->set_pullup(dev->bus_master->data, 0); +- else ++ if (dev->enable_pullup) { ++ if (dev->bus_master->set_pullup) { ++ dev->bus_master->set_pullup(dev-> ++ bus_master->data, ++ 0); ++ } else if (dev->bus_master->bitbang_pullup) { ++ dev->bus_master-> ++ bitbang_pullup(dev->bus_master->data, 1); ++ msleep(dev->pullup_duration); ++ dev->bus_master-> ++ bitbang_pullup(dev->bus_master->data, 0); ++ } ++ } else { + msleep(dev->pullup_duration); ++ } ++ + dev->pullup_duration = 0; + } + } + +From 198cbde7112abbdccf89a1a75e8386c8cba42de9 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Dec 2013 22:16:19 +0000 +Subject: [PATCH 029/104] config: Enable CONFIG_MEMCG, but leave it disabled + (due to memory cost). Enable with cgroup_enable=memory. + +--- + kernel/cgroup.c | 23 +++++++++++++++++++++++ + mm/memcontrol.c | 1 + + 2 files changed, 24 insertions(+) + +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index 29a7b2c..54fd2f0 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -5393,6 +5393,29 @@ static int __init cgroup_disable(char *str) + } + __setup("cgroup_disable=", cgroup_disable); + ++static int __init cgroup_enable(char *str) ++{ ++ struct cgroup_subsys *ss; ++ char *token; ++ int i; ++ ++ while ((token = strsep(&str, ",")) != NULL) { ++ if (!*token) ++ continue; ++ ++ for_each_subsys(ss, i) { ++ if (!strcmp(token, ss->name)) { ++ ss->disabled = 0; ++ printk(KERN_INFO "Enabling %s control group" ++ " subsystem\n", ss->name); ++ break; ++ } ++ } ++ } ++ return 1; ++} ++__setup("cgroup_enable=", cgroup_enable); ++ + static int __init cgroup_set_legacy_files_on_dfl(char *str) + { + printk("cgroup: using legacy files on the default hierarchy\n"); +diff --git a/mm/memcontrol.c b/mm/memcontrol.c +index b34ef4a..c2aa348 100644 +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -5391,6 +5391,7 @@ struct cgroup_subsys memory_cgrp_subsys = { + .dfl_cftypes = memory_files, + .legacy_cftypes = mem_cgroup_legacy_files, + .early_init = 0, ++ .disabled = 1, + }; + + /** + +From 121bddc28d392117231ba805dbe27e63440f90ac Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:33:38 +0100 +Subject: [PATCH 030/104] ASoC: Add support for BCM2708 + +This driver adds support for digital audio (I2S) +for the BCM2708 SoC that is used by the +Raspberry Pi. External audio codecs can be +connected to the Raspberry Pi via P5 header. + +It relies on cyclic DMA engine support for BCM2708. + +Signed-off-by: Florian Meier + +ASoC: BCM2708: Add 24 bit support + +This adds 24 bit support to the I2S driver of the BCM2708. +Besides enabling the 24 bit flags, it includes two bug fixes: + +MMAP is not supported. Claiming this leads to strange issues +when the format of driver and file do not match. + +The datasheet states that the width extension bit should be set +for widths greater than 24, but greater or equal would be correct. +This follows from the definition of the width field. + +Signed-off-by: Florian Meier + +bcm2708-i2s: Update bclk_ratio to more correct values + +Move GPIO setup to hw_params. + +This is used to stop the I2S driver from breaking +the GPIO setup for other uses of the PCM interface + +Configure GPIOs for I2S based on revision/card settings + +With RPi model B+, assignment of the I2S GPIO pins has changed. +This patch uses the board revision to auto-detect the GPIOs used +for I2S. It also allows sound card drivers to set the GPIOs that +should be used. This is especially important with the Compute +Module. + +bcm2708-i2s: Avoid leak from iomap when accessing gpio + +bcm2708: Eliminate i2s debugfs directory error + +Qualify the two regmap ranges uses by bcm2708-i2s ('-i2s' and '-clk') +to avoid the name clash when registering debugfs entries. +--- + sound/soc/bcm/Kconfig | 11 + + sound/soc/bcm/Makefile | 4 + + sound/soc/bcm/bcm2708-i2s.c | 1009 +++++++++++++++++++++++++++++++++++++++++++ + sound/soc/bcm/bcm2708-i2s.h | 35 ++ + 4 files changed, 1059 insertions(+) + create mode 100644 sound/soc/bcm/bcm2708-i2s.c + create mode 100644 sound/soc/bcm/bcm2708-i2s.h + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 6a834e1..8642296 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -7,3 +7,14 @@ config SND_BCM2835_SOC_I2S + Say Y or M if you want to add support for codecs attached to + the BCM2835 I2S interface. You will also need + to select the audio interfaces to support below. ++ ++config SND_BCM2708_SOC_I2S ++ tristate "SoC Audio support for the Broadcom BCM2708 I2S module" ++ depends on MACH_BCM2708 || MACH_BCM2709 ++ select REGMAP_MMIO ++ select SND_SOC_DMAENGINE_PCM ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ help ++ Say Y or M if you want to add support for codecs attached to ++ the BCM2708 I2S interface. You will also need ++ to select the audio interfaces to support below. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index bc816b7..f8bbe1f 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -3,3 +3,7 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o + + obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o + ++# BCM2708 Platform Support ++snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o +diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +new file mode 100644 +index 0000000..a3b65dc +--- /dev/null ++++ b/sound/soc/bcm/bcm2708-i2s.c +@@ -0,0 +1,1009 @@ ++/* ++ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * Based on ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor ++ * Vladimir Barinov, ++ * Copyright (C) 2007 MontaVista Software, Inc., ++ * ++ * OMAP ALSA SoC DAI driver using McBSP port ++ * Copyright (C) 2008 Nokia Corporation ++ * Contact: Jarkko Nikula ++ * Peter Ujfalusi ++ * ++ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver ++ * Author: Timur Tabi ++ * Copyright 2007-2010 Freescale Semiconductor, Inc. ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 "bcm2708-i2s.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Clock registers */ ++#define BCM2708_CLK_PCMCTL_REG 0x00 ++#define BCM2708_CLK_PCMDIV_REG 0x04 ++ ++/* Clock register settings */ ++#define BCM2708_CLK_PASSWD (0x5a000000) ++#define BCM2708_CLK_PASSWD_MASK (0xff000000) ++#define BCM2708_CLK_MASH(v) ((v) << 9) ++#define BCM2708_CLK_FLIP BIT(8) ++#define BCM2708_CLK_BUSY BIT(7) ++#define BCM2708_CLK_KILL BIT(5) ++#define BCM2708_CLK_ENAB BIT(4) ++#define BCM2708_CLK_SRC(v) (v) ++ ++#define BCM2708_CLK_SHIFT (12) ++#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) ++#define BCM2708_CLK_DIVF(v) (v) ++#define BCM2708_CLK_DIVF_MASK (0xFFF) ++ ++enum { ++ BCM2708_CLK_MASH_0 = 0, ++ BCM2708_CLK_MASH_1, ++ BCM2708_CLK_MASH_2, ++ BCM2708_CLK_MASH_3, ++}; ++ ++enum { ++ BCM2708_CLK_SRC_GND = 0, ++ BCM2708_CLK_SRC_OSC, ++ BCM2708_CLK_SRC_DBG0, ++ BCM2708_CLK_SRC_DBG1, ++ BCM2708_CLK_SRC_PLLA, ++ BCM2708_CLK_SRC_PLLC, ++ BCM2708_CLK_SRC_PLLD, ++ BCM2708_CLK_SRC_HDMI, ++}; ++ ++/* Most clocks are not useable (freq = 0) */ ++static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { ++ [BCM2708_CLK_SRC_GND] = 0, ++ [BCM2708_CLK_SRC_OSC] = 19200000, ++ [BCM2708_CLK_SRC_DBG0] = 0, ++ [BCM2708_CLK_SRC_DBG1] = 0, ++ [BCM2708_CLK_SRC_PLLA] = 0, ++ [BCM2708_CLK_SRC_PLLC] = 0, ++ [BCM2708_CLK_SRC_PLLD] = 500000000, ++ [BCM2708_CLK_SRC_HDMI] = 0, ++}; ++ ++/* I2S registers */ ++#define BCM2708_I2S_CS_A_REG 0x00 ++#define BCM2708_I2S_FIFO_A_REG 0x04 ++#define BCM2708_I2S_MODE_A_REG 0x08 ++#define BCM2708_I2S_RXC_A_REG 0x0c ++#define BCM2708_I2S_TXC_A_REG 0x10 ++#define BCM2708_I2S_DREQ_A_REG 0x14 ++#define BCM2708_I2S_INTEN_A_REG 0x18 ++#define BCM2708_I2S_INTSTC_A_REG 0x1c ++#define BCM2708_I2S_GRAY_REG 0x20 ++ ++/* I2S register settings */ ++#define BCM2708_I2S_STBY BIT(25) ++#define BCM2708_I2S_SYNC BIT(24) ++#define BCM2708_I2S_RXSEX BIT(23) ++#define BCM2708_I2S_RXF BIT(22) ++#define BCM2708_I2S_TXE BIT(21) ++#define BCM2708_I2S_RXD BIT(20) ++#define BCM2708_I2S_TXD BIT(19) ++#define BCM2708_I2S_RXR BIT(18) ++#define BCM2708_I2S_TXW BIT(17) ++#define BCM2708_I2S_CS_RXERR BIT(16) ++#define BCM2708_I2S_CS_TXERR BIT(15) ++#define BCM2708_I2S_RXSYNC BIT(14) ++#define BCM2708_I2S_TXSYNC BIT(13) ++#define BCM2708_I2S_DMAEN BIT(9) ++#define BCM2708_I2S_RXTHR(v) ((v) << 7) ++#define BCM2708_I2S_TXTHR(v) ((v) << 5) ++#define BCM2708_I2S_RXCLR BIT(4) ++#define BCM2708_I2S_TXCLR BIT(3) ++#define BCM2708_I2S_TXON BIT(2) ++#define BCM2708_I2S_RXON BIT(1) ++#define BCM2708_I2S_EN (1) ++ ++#define BCM2708_I2S_CLKDIS BIT(28) ++#define BCM2708_I2S_PDMN BIT(27) ++#define BCM2708_I2S_PDME BIT(26) ++#define BCM2708_I2S_FRXP BIT(25) ++#define BCM2708_I2S_FTXP BIT(24) ++#define BCM2708_I2S_CLKM BIT(23) ++#define BCM2708_I2S_CLKI BIT(22) ++#define BCM2708_I2S_FSM BIT(21) ++#define BCM2708_I2S_FSI BIT(20) ++#define BCM2708_I2S_FLEN(v) ((v) << 10) ++#define BCM2708_I2S_FSLEN(v) (v) ++ ++#define BCM2708_I2S_CHWEX BIT(15) ++#define BCM2708_I2S_CHEN BIT(14) ++#define BCM2708_I2S_CHPOS(v) ((v) << 4) ++#define BCM2708_I2S_CHWID(v) (v) ++#define BCM2708_I2S_CH1(v) ((v) << 16) ++#define BCM2708_I2S_CH2(v) (v) ++ ++#define BCM2708_I2S_TX_PANIC(v) ((v) << 24) ++#define BCM2708_I2S_RX_PANIC(v) ((v) << 16) ++#define BCM2708_I2S_TX(v) ((v) << 8) ++#define BCM2708_I2S_RX(v) (v) ++ ++#define BCM2708_I2S_INT_RXERR BIT(3) ++#define BCM2708_I2S_INT_TXERR BIT(2) ++#define BCM2708_I2S_INT_RXR BIT(1) ++#define BCM2708_I2S_INT_TXW BIT(0) ++ ++/* I2S DMA interface */ ++#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004 ++#define BCM2708_DMA_DREQ_PCM_TX 2 ++#define BCM2708_DMA_DREQ_PCM_RX 3 ++ ++/* I2S pin configuration */ ++static int bcm2708_i2s_gpio=BCM2708_I2S_GPIO_AUTO; ++ ++/* General device struct */ ++struct bcm2708_i2s_dev { ++ struct device *dev; ++ struct snd_dmaengine_dai_dma_data dma_data[2]; ++ unsigned int fmt; ++ unsigned int bclk_ratio; ++ ++ struct regmap *i2s_regmap; ++ struct regmap *clk_regmap; ++}; ++ ++void bcm2708_i2s_set_gpio(int gpio) { ++ bcm2708_i2s_gpio=gpio; ++} ++EXPORT_SYMBOL(bcm2708_i2s_set_gpio); ++ ++ ++static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev) ++{ ++ /* Start the clock if in master mode */ ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ ++ switch (master) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ case SND_SOC_DAIFMT_CBS_CFM: ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev) ++{ ++ uint32_t clkreg; ++ int timeout = 1000; ++ ++ /* Stop clock */ ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD); ++ ++ /* Wait for the BUSY flag going down */ ++ while (--timeout) { ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ if (!(clkreg & BCM2708_CLK_BUSY)) ++ break; ++ } ++ ++ if (!timeout) { ++ /* KILL the clock */ ++ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); ++ } ++} ++ ++static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev, ++ bool tx, bool rx) ++{ ++ int timeout = 1000; ++ uint32_t syncval; ++ uint32_t csreg; ++ uint32_t i2s_active_state; ++ uint32_t clkreg; ++ uint32_t clk_active_state; ++ uint32_t off; ++ uint32_t clr; ++ ++ off = tx ? BCM2708_I2S_TXON : 0; ++ off |= rx ? BCM2708_I2S_RXON : 0; ++ ++ clr = tx ? BCM2708_I2S_TXCLR : 0; ++ clr |= rx ? BCM2708_I2S_RXCLR : 0; ++ ++ /* Backup the current state */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON); ++ ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ clk_active_state = clkreg & BCM2708_CLK_ENAB; ++ ++ /* Start clock if not running */ ++ if (!clk_active_state) { ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ } ++ ++ /* Stop I2S module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0); ++ ++ /* ++ * Clear the FIFOs ++ * Requires at least 2 PCM clock cycles to take effect ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr); ++ ++ /* Wait for 2 PCM clock cycles */ ++ ++ /* ++ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back ++ * FIXME: This does not seem to work for slave mode! ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval); ++ syncval &= BCM2708_I2S_SYNC; ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_SYNC, ~syncval); ++ ++ /* Wait for the SYNC flag changing it's state */ ++ while (--timeout) { ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ if ((csreg & BCM2708_I2S_SYNC) != syncval) ++ break; ++ } ++ ++ if (!timeout) ++ dev_err(dev->dev, "I2S SYNC error!\n"); ++ ++ /* Stop clock if it was not running before */ ++ if (!clk_active_state) ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Restore I2S state */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state); ++} ++ ++static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai, ++ unsigned int fmt) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->fmt = fmt; ++ return 0; ++} ++ ++static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, ++ unsigned int ratio) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->bclk_ratio = ratio; ++ return 0; ++} ++ ++ ++static int bcm2708_i2s_set_function(unsigned offset, int function) ++{ ++ #define GPIOFSEL(x) (0x00+(x)*4) ++ void __iomem *gpio = __io_address(GPIO_BASE); ++ unsigned alt = function <= 3 ? function + 4: function == 4 ? 3 : 2; ++ unsigned gpiodir; ++ unsigned gpio_bank = offset / 10; ++ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; ++ ++ if (offset >= BCM2708_NR_GPIOS) ++ return -EINVAL; ++ ++ gpiodir = readl(gpio + GPIOFSEL(gpio_bank)); ++ gpiodir &= ~(7 << gpio_field_offset); ++ gpiodir |= alt << gpio_field_offset; ++ writel(gpiodir, gpio + GPIOFSEL(gpio_bank)); ++ return 0; ++} ++ ++static void bcm2708_i2s_setup_gpio(void) ++{ ++ /* ++ * This is the common way to handle the GPIO pins for ++ * the Raspberry Pi. ++ * TODO Better way would be to handle ++ * this in the device tree! ++ */ ++ int pin,pinconfig,startpin,alt; ++ ++ /* SPI is on different GPIOs on different boards */ ++ /* for Raspberry Pi B+, this is pin GPIO18-21, for original on 28-31 */ ++ if (bcm2708_i2s_gpio==BCM2708_I2S_GPIO_AUTO) { ++ if ((system_rev & 0xffffff) >= 0x10) { ++ /* Model B+ */ ++ pinconfig=BCM2708_I2S_GPIO_PIN18; ++ } else { ++ /* original */ ++ pinconfig=BCM2708_I2S_GPIO_PIN28; ++ } ++ } else { ++ pinconfig=bcm2708_i2s_gpio; ++ } ++ ++ if (pinconfig==BCM2708_I2S_GPIO_PIN18) { ++ startpin=18; ++ alt=BCM2708_I2S_GPIO_PIN18_ALT; ++ } else if (pinconfig==BCM2708_I2S_GPIO_PIN28) { ++ startpin=28; ++ alt=BCM2708_I2S_GPIO_PIN28_ALT; ++ } else { ++ printk(KERN_INFO "Can't configure I2S GPIOs, unknown pin mode for I2S: %i\n",pinconfig); ++ return; ++ } ++ ++ /* configure I2S pins to correct ALT mode */ ++ for (pin = startpin; pin <= startpin+3; pin++) { ++ bcm2708_i2s_set_function(pin, alt); ++ } ++} ++ ++static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ unsigned int sampling_rate = params_rate(params); ++ unsigned int data_length, data_delay, bclk_ratio; ++ unsigned int ch1pos, ch2pos, mode, format; ++ unsigned int mash = BCM2708_CLK_MASH_1; ++ unsigned int divi, divf, target_frequency; ++ int clk_src = -1; ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBS_CFM); ++ ++ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBM_CFS); ++ uint32_t csreg; ++ ++ /* ++ * If a stream is already enabled, ++ * the registers are already set properly. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ ++ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON)) ++ return 0; ++ ++ ++ bcm2708_i2s_setup_gpio(); ++ ++ /* ++ * Adjust the data length according to the format. ++ * We prefill the half frame length with an integer ++ * divider of 2400 as explained at the clock settings. ++ * Maybe it is overwritten there, if the Integer mode ++ * does not apply. ++ */ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ data_length = 16; ++ bclk_ratio = 50; ++ break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ data_length = 24; ++ bclk_ratio = 50; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ data_length = 32; ++ bclk_ratio = 100; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* If bclk_ratio already set, use that one. */ ++ if (dev->bclk_ratio) ++ bclk_ratio = dev->bclk_ratio; ++ ++ /* ++ * Clock Settings ++ * ++ * The target frequency of the bit clock is ++ * sampling rate * frame length ++ * ++ * Integer mode: ++ * Sampling rates that are multiples of 8000 kHz ++ * can be driven by the oscillator of 19.2 MHz ++ * with an integer divider as long as the frame length ++ * is an integer divider of 19200000/8000=2400 as set up above. ++ * This is no longer possible if the sampling rate ++ * is too high (e.g. 192 kHz), because the oscillator is too slow. ++ * ++ * MASH mode: ++ * For all other sampling rates, it is not possible to ++ * have an integer divider. Approximate the clock ++ * with the MASH module that induces a slight frequency ++ * variance. To minimize that it is best to have the fastest ++ * clock here. That is PLLD with 500 MHz. ++ */ ++ target_frequency = sampling_rate * bclk_ratio; ++ clk_src = BCM2708_CLK_SRC_OSC; ++ mash = BCM2708_CLK_MASH_0; ++ ++ if (bcm2708_clk_freq[clk_src] % target_frequency == 0 ++ && bit_master && frame_master) { ++ divi = bcm2708_clk_freq[clk_src] / target_frequency; ++ divf = 0; ++ } else { ++ uint64_t dividend; ++ ++ if (!dev->bclk_ratio) { ++ /* ++ * Overwrite bclk_ratio, because the ++ * above trick is not needed or can ++ * not be used. ++ */ ++ bclk_ratio = 2 * data_length; ++ } ++ ++ target_frequency = sampling_rate * bclk_ratio; ++ ++ clk_src = BCM2708_CLK_SRC_PLLD; ++ mash = BCM2708_CLK_MASH_1; ++ ++ dividend = bcm2708_clk_freq[clk_src]; ++ dividend <<= BCM2708_CLK_SHIFT; ++ do_div(dividend, target_frequency); ++ divi = dividend >> BCM2708_CLK_SHIFT; ++ divf = dividend & BCM2708_CLK_DIVF_MASK; ++ } ++ ++ /* Clock should only be set up here if CPU is clock master */ ++ if (((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS) || ++ ((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFM)) { ++ /* Set clock divider */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_DIVI(divi) ++ | BCM2708_CLK_DIVF(divf)); ++ ++ /* Setup clock, but don't start it yet */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_MASH(mash) ++ | BCM2708_CLK_SRC(clk_src)); ++ } ++ ++ /* Setup the frame format */ ++ format = BCM2708_I2S_CHEN; ++ ++ if (data_length >= 24) ++ format |= BCM2708_I2S_CHWEX; ++ ++ format |= BCM2708_I2S_CHWID((data_length-8)&0xf); ++ ++ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ data_delay = 1; ++ break; ++ default: ++ /* ++ * TODO ++ * Others are possible but are not implemented at the moment. ++ */ ++ dev_err(dev->dev, "%s:bad format\n", __func__); ++ return -EINVAL; ++ } ++ ++ ch1pos = data_delay; ++ ch2pos = bclk_ratio / 2 + data_delay; ++ ++ switch (params_channels(params)) { ++ case 2: ++ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format); ++ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos)); ++ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * Set format for both streams. ++ * We cannot set another frame length ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format); ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format); ++ ++ /* Setup the I2S mode */ ++ mode = 0; ++ ++ if (data_length <= 16) { ++ /* ++ * Use frame packed mode (2 channels per 32 bit word) ++ * We cannot set another frame length in the second stream ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP; ++ } ++ ++ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1); ++ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2); ++ ++ /* Master or slave? */ ++ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* CPU is master */ ++ break; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ /* ++ * CODEC is bit clock master ++ * CPU is frame master ++ */ ++ mode |= BCM2708_I2S_CLKM; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFM: ++ /* ++ * CODEC is frame master ++ * CPU is bit clock master ++ */ ++ mode |= BCM2708_I2S_FSM; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ /* CODEC is master */ ++ mode |= BCM2708_I2S_CLKM; ++ mode |= BCM2708_I2S_FSM; ++ break; ++ default: ++ dev_err(dev->dev, "%s:bad master\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Invert clocks? ++ * ++ * The BCM approach seems to be inverted to the classical I2S approach. ++ */ ++ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ /* None. Therefore, both for BCM */ ++ mode |= BCM2708_I2S_CLKI; ++ mode |= BCM2708_I2S_FSI; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ /* Both. Therefore, none for BCM */ ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ /* ++ * Invert only frame sync. Therefore, ++ * invert only bit clock for BCM ++ */ ++ mode |= BCM2708_I2S_CLKI; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ /* ++ * Invert only bit clock. Therefore, ++ * invert only frame sync for BCM ++ */ ++ mode |= BCM2708_I2S_FSI; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode); ++ ++ /* Setup the DMA parameters */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXTHR(1) ++ | BCM2708_I2S_TXTHR(1) ++ | BCM2708_I2S_DMAEN, 0xffffffff); ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG, ++ BCM2708_I2S_TX_PANIC(0x10) ++ | BCM2708_I2S_RX_PANIC(0x30) ++ | BCM2708_I2S_TX(0x30) ++ | BCM2708_I2S_RX(0x20), 0xffffffff); ++ ++ /* Clear FIFOs */ ++ bcm2708_i2s_clear_fifos(dev, true, true); ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t cs_reg; ++ ++ bcm2708_i2s_start_clock(dev); ++ ++ /* ++ * Clear both FIFOs if the one that should be started ++ * is not empty at the moment. This should only happen ++ * after overrun. Otherwise, hw_params would have cleared ++ * the FIFO. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ++ && !(cs_reg & BCM2708_I2S_TXE)) ++ bcm2708_i2s_clear_fifos(dev, true, false); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE ++ && (cs_reg & BCM2708_I2S_RXD)) ++ bcm2708_i2s_clear_fifos(dev, false, true); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev, ++ struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ uint32_t mask; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, 0); ++ ++ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ ++ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT)) ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t mask; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ bcm2708_i2s_start_clock(dev); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, mask); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ bcm2708_i2s_stop(dev, substream, dai); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ if (dai->active) ++ return 0; ++ ++ /* Should this still be running stop it */ ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Enable PCM block */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, BCM2708_I2S_EN); ++ ++ /* ++ * Disable STBY. ++ * Requires at least 4 PCM clock cycles to take effect. ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_STBY, BCM2708_I2S_STBY); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ bcm2708_i2s_stop(dev, substream, dai); ++ ++ /* If both streams are stopped, disable module and clock */ ++ if (dai->active) ++ return; ++ ++ /* Disable the module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, 0); ++ ++ /* ++ * Stopping clock is necessary, because stop does ++ * not stop the clock when SND_SOC_DAIFMT_CONT ++ */ ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = { ++ .startup = bcm2708_i2s_startup, ++ .shutdown = bcm2708_i2s_shutdown, ++ .prepare = bcm2708_i2s_prepare, ++ .trigger = bcm2708_i2s_trigger, ++ .hw_params = bcm2708_i2s_hw_params, ++ .set_fmt = bcm2708_i2s_set_dai_fmt, ++ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio ++}; ++ ++static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; ++ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ return 0; ++} ++ ++static struct snd_soc_dai_driver bcm2708_i2s_dai = { ++ .name = "bcm2708-i2s", ++ .probe = bcm2708_i2s_dai_probe, ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .capture = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .ops = &bcm2708_i2s_dai_ops, ++ .symmetric_rates = 1 ++}; ++ ++static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_CS_A_REG: ++ case BCM2708_I2S_FIFO_A_REG: ++ case BCM2708_I2S_INTSTC_A_REG: ++ case BCM2708_I2S_GRAY_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_FIFO_A_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_CLK_PCMCTL_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static const struct regmap_config bcm2708_regmap_config[] = { ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_I2S_GRAY_REG, ++ .precious_reg = bcm2708_i2s_precious_reg, ++ .volatile_reg = bcm2708_i2s_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ .name = "i2s", ++ }, ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_CLK_PCMDIV_REG, ++ .volatile_reg = bcm2708_clk_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ .name = "clk", ++ }, ++}; ++ ++static const struct snd_soc_component_driver bcm2708_i2s_component = { ++ .name = "bcm2708-i2s-comp", ++}; ++ ++static const struct snd_pcm_hardware bcm2708_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_JOINT_DUPLEX, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .period_bytes_min = 32, ++ .period_bytes_max = 64 * PAGE_SIZE, ++ .periods_min = 2, ++ .periods_max = 255, ++ .buffer_bytes_max = 128 * PAGE_SIZE, ++}; ++ ++static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &bcm2708_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, ++}; ++ ++ ++static int bcm2708_i2s_probe(struct platform_device *pdev) ++{ ++ struct bcm2708_i2s_dev *dev; ++ int i; ++ int ret; ++ struct regmap *regmap[2]; ++ struct resource *mem[2]; ++ ++ /* Request both ioareas */ ++ for (i = 0; i <= 1; i++) { ++ void __iomem *base; ++ ++ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ base = devm_ioremap_resource(&pdev->dev, mem[i]); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, ++ &bcm2708_regmap_config[i]); ++ if (IS_ERR(regmap[i])) { ++ dev_err(&pdev->dev, "I2S probe: regmap init failed\n"); ++ return PTR_ERR(regmap[i]); ++ } ++ } ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), ++ GFP_KERNEL); ++ if (IS_ERR(dev)) ++ return PTR_ERR(dev); ++ ++ dev->i2s_regmap = regmap[0]; ++ dev->clk_regmap = regmap[1]; ++ ++ /* Set the DMA address */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ /* Set the DREQ */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id = ++ BCM2708_DMA_DREQ_PCM_TX; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id = ++ BCM2708_DMA_DREQ_PCM_RX; ++ ++ /* Set the bus width */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ ++ /* Set burst */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; ++ ++ /* BCLK ratio - use default */ ++ dev->bclk_ratio = 0; ++ ++ /* Store the pdev */ ++ dev->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, dev); ++ ++ ret = snd_soc_register_component(&pdev->dev, ++ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = snd_dmaengine_pcm_register(&pdev->dev, ++ &bcm2708_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); ++ snd_soc_unregister_component(&pdev->dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_remove(struct platform_device *pdev) ++{ ++ snd_dmaengine_pcm_unregister(&pdev->dev); ++ snd_soc_unregister_component(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id bcm2708_i2s_of_match[] = { ++ { .compatible = "brcm,bcm2708-i2s", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_i2s_of_match); ++ ++static struct platform_driver bcm2708_i2s_driver = { ++ .probe = bcm2708_i2s_probe, ++ .remove = bcm2708_i2s_remove, ++ .driver = { ++ .name = "bcm2708-i2s", ++ .owner = THIS_MODULE, ++ .of_match_table = bcm2708_i2s_of_match, ++ }, ++}; ++ ++module_platform_driver(bcm2708_i2s_driver); ++ ++MODULE_ALIAS("platform:bcm2708-i2s"); ++MODULE_DESCRIPTION("BCM2708 I2S interface"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/bcm/bcm2708-i2s.h b/sound/soc/bcm/bcm2708-i2s.h +new file mode 100644 +index 0000000..6fdcbc1 +--- /dev/null ++++ b/sound/soc/bcm/bcm2708-i2s.h +@@ -0,0 +1,35 @@ ++/* ++ * I2S configuration for sound cards. ++ * ++ * Copyright (c) 2014 Daniel Matuschek ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef BCM2708_I2S_H ++#define BCM2708_I2S_H ++ ++/* I2S pin assignment */ ++#define BCM2708_I2S_GPIO_AUTO 0 ++#define BCM2708_I2S_GPIO_PIN18 1 ++#define BCM2708_I2S_GPIO_PIN28 2 ++ ++/* Alt mode to enable I2S */ ++#define BCM2708_I2S_GPIO_PIN18_ALT 0 ++#define BCM2708_I2S_GPIO_PIN28_ALT 2 ++ ++extern void bcm2708_i2s_set_gpio(int gpio); ++ ++#endif + +From 1f20015a049141afa162c6ce9d6349c17ca71f02 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:59:51 +0100 +Subject: [PATCH 031/104] ASoC: Add support for PCM5102A codec + +Some definitions to support the PCM5102A codec +by Texas Instruments. + +Signed-off-by: Florian Meier +--- + sound/soc/codecs/Kconfig | 4 +++ + sound/soc/codecs/Makefile | 2 ++ + sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 69 insertions(+) + create mode 100644 sound/soc/codecs/pcm5102a.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index ea9f0e3..bc39bbc 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM512x_I2C if I2C + select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C ++ select SND_SOC_PCM5102A if I2C + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C + select SND_SOC_RT5645 if I2C +@@ -506,6 +507,9 @@ config SND_SOC_RT286 + tristate + depends on I2C + ++config SND_SOC_PCM5102A ++ tristate ++ + config SND_SOC_RT5631 + tristate "Realtek ALC5631/RT5631 CODEC" + depends on I2C +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 69b8666..905ef6f 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -77,6 +77,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o + snd-soc-rl6231-objs := rl6231.o + snd-soc-rt286-objs := rt286.o ++snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o + snd-soc-rt5645-objs := rt5645.o +@@ -259,6 +260,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o ++obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o + obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o +diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c +new file mode 100644 +index 0000000..126f1e9 +--- /dev/null ++++ b/sound/soc/codecs/pcm5102a.c +@@ -0,0 +1,63 @@ ++/* ++ * Driver for the PCM5102A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static struct snd_soc_dai_driver pcm5102a_dai = { ++ .name = "pcm5102a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; ++ ++static int pcm5102a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, ++ &pcm5102a_dai, 1); ++} ++ ++static int pcm5102a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm5102a_codec_driver = { ++ .probe = pcm5102a_probe, ++ .remove = pcm5102a_remove, ++ .driver = { ++ .name = "pcm5102a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm5102a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); + +From 3b3a18623f4eb40c0cf34d738edecb8ee5f0d9f0 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:04:54 +0100 +Subject: [PATCH 032/104] BCM2708: Add I2S support to board file + +Adds the required initializations for I2S +to the board file of mach-bcm2708. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 5873f8b..7f4ebf4 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -613,6 +613,28 @@ static struct platform_device bcm2835_thermal_device = { + .name = "bcm2835_thermal", + }; + ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++static struct resource bcm2708_i2s_resources[] = { ++ { ++ .start = I2S_BASE, ++ .end = I2S_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PCM_CLOCK_BASE, ++ .end = PCM_CLOCK_BASE + 0x02, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_i2s_device = { ++ .name = "bcm2708-i2s", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), ++ .resource = bcm2708_i2s_resources, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -762,6 +784,10 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); + ++#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) ++ bcm_register_device_dt(&bcm2708_i2s_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + +From e4249088ec01bb083925564bcaf624c64af34e9a Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:19:08 +0100 +Subject: [PATCH 033/104] ASoC: Add support for HifiBerry DAC + +This adds a machine driver for the HifiBerry DAC. +It is a sound card that can +be stacked onto the Raspberry Pi. + +Signed-off-by: Florian Meier +--- + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 5 +++ + sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 112 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_dac.c + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 8642296..a92adb2 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -18,3 +18,10 @@ config SND_BCM2708_SOC_I2S + Say Y or M if you want to add support for codecs attached to + the BCM2708 I2S interface. You will also need + to select the audio interfaces to support below. ++ ++config SND_BCM2708_SOC_HIFIBERRY_DAC ++ tristate "Support for HifiBerry DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM5102A ++ help ++ Say Y or M if you want to add support for HifiBerry DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index f8bbe1f..be90a49cb 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -7,3 +7,8 @@ obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o + snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o + + obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o ++ ++# BCM2708 Machine Support ++snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o +diff --git a/sound/soc/bcm/hifiberry_dac.c b/sound/soc/bcm/hifiberry_dac.c +new file mode 100644 +index 0000000..4b70b45 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_dac.c +@@ -0,0 +1,100 @@ ++/* ++ * ASoC Driver for HifiBerry DAC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ unsigned int sample_bits = ++ snd_pcm_format_physical_width(params_format(params)); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = { ++ .hw_params = snd_rpi_hifiberry_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = { ++{ ++ .name = "HifiBerry DAC", ++ .stream_name = "HifiBerry DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm5102a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm5102a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_hifiberry_dac_ops, ++ .init = snd_rpi_hifiberry_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_dac = { ++ .name = "snd_rpi_hifiberry_dac", ++ .dai_link = snd_rpi_hifiberry_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), ++}; ++ ++static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac); ++} ++ ++static struct platform_driver snd_rpi_hifiberry_dac_driver = { ++ .driver = { ++ .name = "snd-hifiberry-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_hifiberry_dac_probe, ++ .remove = snd_rpi_hifiberry_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC"); ++MODULE_LICENSE("GPL v2"); + +From 160300e5f0194502f368d076e937d7d586ddaadd Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:21:34 +0100 +Subject: [PATCH 034/104] BCM2708: Add HifiBerry DAC to board file + +This adds the initalization of the HifiBerry DAC +to the mach-bcm2708 board file. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 7f4ebf4..94ce8fc 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -635,6 +635,20 @@ static struct platform_device bcm2708_i2s_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++static struct platform_device snd_hifiberry_dac_device = { ++ .name = "snd-hifiberry-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm5102a_codec_device = { ++ .name = "pcm5102a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -788,6 +802,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&bcm2708_i2s_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_dac_device); ++ bcm_register_device_dt(&snd_pcm5102a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + +From e6aa7036b6165cd7bd01501948ebdf8d5ad480be Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 6 Dec 2013 20:50:28 +0100 +Subject: [PATCH 035/104] ASoC: BCM2708: Add support for RPi-DAC + +This adds a machine driver for the RPi-DAC. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++ + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/Kconfig | 4 ++ + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/pcm1794a.c | 62 ++++++++++++++++++++++++++ + 7 files changed, 193 insertions(+) + create mode 100644 sound/soc/bcm/rpi-dac.c + create mode 100644 sound/soc/codecs/pcm1794a.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 94ce8fc..fb955755 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -649,6 +649,20 @@ static struct platform_device snd_pcm5102a_codec_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++static struct platform_device snd_rpi_dac_device = { ++ .name = "snd-rpi-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm1794a_codec_device = { ++ .name = "pcm1794a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -807,6 +821,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_dac_device); ++ bcm_register_device_dt(&snd_pcm1794a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index a92adb2..64c61ac 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -25,3 +25,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC + select SND_SOC_PCM5102A + help + Say Y or M if you want to add support for HifiBerry DAC. ++ ++config SND_BCM2708_SOC_RPI_DAC ++ tristate "Support for RPi-DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM1794A ++ help ++ Say Y or M if you want to add support for RPi-DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index be90a49cb..ccc9809 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -10,5 +10,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-rpi-dac-objs := rpi-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o +diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c +new file mode 100644 +index 0000000..6d6e0ba +--- /dev/null ++++ b/sound/soc/bcm/rpi-dac.c +@@ -0,0 +1,97 @@ ++/* ++ * ASoC Driver for RPi-DAC. ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_rpi_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_rpi_dac_ops = { ++ .hw_params = snd_rpi_rpi_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = { ++{ ++ .name = "RPi-DAC", ++ .stream_name = "RPi-DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm1794a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm1794a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_rpi_dac_ops, ++ .init = snd_rpi_rpi_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_rpi_dac = { ++ .name = "snd_rpi_rpi_dac", ++ .dai_link = snd_rpi_rpi_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), ++}; ++ ++static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_rpi_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_rpi_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_rpi_dac); ++} ++ ++static struct platform_driver snd_rpi_rpi_dac_driver = { ++ .driver = { ++ .name = "snd-rpi-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_rpi_dac_probe, ++ .remove = snd_rpi_rpi_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_rpi_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index bc39bbc..8682bcf 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -83,6 +83,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RT286 if I2C + select SND_SOC_PCM5102A if I2C ++ select SND_SOC_PCM1794A if I2C + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C + select SND_SOC_RT5645 if I2C +@@ -507,6 +508,9 @@ config SND_SOC_RT286 + tristate + depends on I2C + ++config SND_SOC_PCM1794A ++ tristate ++ + config SND_SOC_PCM5102A + tristate + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 905ef6f..c9bceba 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -77,6 +77,7 @@ snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o + snd-soc-pcm512x-spi-objs := pcm512x-spi.o + snd-soc-rl6231-objs := rl6231.o + snd-soc-rt286-objs := rt286.o ++snd-soc-pcm1794a-objs := pcm1794a.o + snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o +@@ -260,6 +261,7 @@ obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o + obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o + obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o + obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o ++obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o + obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c +new file mode 100644 +index 0000000..b4eaa44 +--- /dev/null ++++ b/sound/soc/codecs/pcm1794a.c +@@ -0,0 +1,62 @@ ++/* ++ * Driver for the PCM1794A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static struct snd_soc_dai_driver pcm1794a_dai = { ++ .name = "pcm1794a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm1794a; ++ ++static int pcm1794a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a, ++ &pcm1794a_dai, 1); ++} ++ ++static int pcm1794a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm1794a_codec_driver = { ++ .probe = pcm1794a_probe, ++ .remove = pcm1794a_remove, ++ .driver = { ++ .name = "pcm1794a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm1794a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); + +From d9a3158a7ddd3fdd03e61ca26ed228da5a7090d7 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:41:23 +0100 +Subject: [PATCH 036/104] ASoC: wm8804: Implement MCLK configuration options, + add 32bit support WM8804 can run with PLL frequencies of 256xfs and 128xfs + for most sample rates. At 192kHz only 128xfs is supported. The existing + driver selects 128xfs automatically for some lower samples rates. By using an + additional mclk_div divider, it is now possible to control the behaviour. + This allows using 256xfs PLL frequency on all sample rates up to 96kHz. It + should allow lower jitter and better signal quality. The behavior has to be + controlled by the sound card driver, because some sample frequency share the + same setting. e.g. 192kHz and 96kHz use 24.576MHz master clock. The only + difference is the MCLK divider. + +This also added support for 32bit data. + +Signed-off-by: Daniel Matuschek +--- + sound/soc/codecs/wm8804.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c +index b2b0e68..ba2a0f8 100644 +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -278,6 +278,7 @@ static int wm8804_hw_params(struct snd_pcm_substream *substream, + blen = 0x1; + break; + case 24: ++ case 32: + blen = 0x2; + break; + default: +@@ -621,7 +622,7 @@ static const struct snd_soc_dai_ops wm8804_dai_ops = { + }; + + #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ +- SNDRV_PCM_FMTBIT_S24_LE) ++ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + + #define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + +From 6bc408185c9f768523ad5eb0c4119149e4e31ef4 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Wed, 15 Jan 2014 21:42:08 +0100 +Subject: [PATCH 037/104] ASoC: BCM:Add support for HiFiBerry Digi. Driver is + based on the patched WM8804 driver. + +Signed-off-by: Daniel Matuschek +--- + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_digi.c | 153 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 162 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_digi.c + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 64c61ac..d4a00fc 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC + help + Say Y or M if you want to add support for HifiBerry DAC. + ++config SND_BCM2708_SOC_HIFIBERRY_DIGI ++ tristate "Support for HifiBerry Digi" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_WM8804 ++ help ++ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board. ++ + config SND_BCM2708_SOC_RPI_DAC + tristate "Support for RPi-DAC" + depends on SND_BCM2708_SOC_I2S +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index ccc9809..826df7d 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -10,7 +10,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-rpi-dac-objs := rpi-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o +diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c +new file mode 100644 +index 0000000..446ecdd +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_digi.c +@@ -0,0 +1,153 @@ ++/* ++ * ASoC Driver for HifiBerry Digi ++ * ++ * Author: Daniel Matuschek ++ * based on the HifiBerry DAC driver by Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++#include "../codecs/wm8804.h" ++ ++static int samplerate=44100; ++ ++static int snd_rpi_hifiberry_digi_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ struct snd_soc_codec *codec = rtd->codec; ++ ++ /* enable TX output */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); ++ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *codec_dai = rtd->codec_dai; ++ struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ int sysclk = 27000000; /* This is fixed on this board */ ++ ++ long mclk_freq=0; ++ int mclk_div=1; ++ ++ int ret; ++ ++ samplerate = params_rate(params); ++ ++ switch (samplerate) { ++ case 44100: ++ case 48000: ++ case 88200: ++ case 96000: ++ mclk_freq=samplerate*256; ++ mclk_div=WM8804_MCLKDIV_256FS; ++ break; ++ case 176400: ++ case 192000: ++ mclk_freq=samplerate*128; ++ mclk_div=WM8804_MCLKDIV_128FS; ++ break; ++ default: ++ dev_err(codec->dev, ++ "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); ++ } ++ ++ snd_soc_dai_set_clkdiv(codec_dai, WM8804_MCLK_DIV, mclk_div); ++ snd_soc_dai_set_pll(codec_dai, 0, 0, sysclk, mclk_freq); ++ ++ ret = snd_soc_dai_set_sysclk(codec_dai, WM8804_TX_CLKSRC_PLL, ++ sysclk, SND_SOC_CLOCK_OUT); ++ if (ret < 0) { ++ dev_err(codec->dev, ++ "Failed to set WM8804 SYSCLK: %d\n", ret); ++ return ret; ++ } ++ ++ /* Enable TX output */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x0); ++ ++ /* Power on */ ++ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai,64); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_digi_ops = { ++ .hw_params = snd_rpi_hifiberry_digi_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { ++{ ++ .name = "HifiBerry Digi", ++ .stream_name = "HifiBerry Digi HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "wm8804-spdif", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "wm8804.1-003b", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBM_CFM, ++ .ops = &snd_rpi_hifiberry_digi_ops, ++ .init = snd_rpi_hifiberry_digi_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_digi = { ++ .name = "snd_rpi_hifiberry_digi", ++ .dai_link = snd_rpi_hifiberry_digi_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_digi_dai), ++}; ++ ++static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_digi.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_hifiberry_digi); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_digi_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_digi); ++} ++ ++static struct platform_driver snd_rpi_hifiberry_digi_driver = { ++ .driver = { ++ .name = "snd-hifiberry-digi", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_hifiberry_digi_probe, ++ .remove = snd_rpi_hifiberry_digi_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_digi_driver); ++ ++MODULE_AUTHOR("Daniel Matuschek "); ++MODULE_DESCRIPTION("ASoC Driver for HifiBerry Digi"); ++MODULE_LICENSE("GPL v2"); + +From 17fa062cde886c00b02d56b8500a7972123d7975 Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Thu, 16 Jan 2014 07:26:08 +0100 +Subject: [PATCH 038/104] BCM2708: Added support for HiFiBerry Digi board Board + initalization by I2C + +Signed-off-by: Daniel Matuschek +--- + arch/arm/mach-bcm2708/bcm2708.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index fb955755..0ad5343 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -649,6 +649,21 @@ static struct platform_device snd_pcm5102a_codec_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++static struct platform_device snd_hifiberry_digi_device = { ++ .name = "snd-hifiberry-digi", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("wm8804", 0x3b) ++ }, ++}; ++ ++#endif ++ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) + static struct platform_device snd_rpi_dac_device = { + .name = "snd-rpi-dac", +@@ -821,6 +836,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) ++ bcm_register_device_dt(&snd_hifiberry_digi_device); ++ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); ++#endif ++ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) + bcm_register_device_dt(&snd_rpi_dac_device); + bcm_register_device_dt(&snd_pcm1794a_codec_device); + +From 0817352c2cfeab1a7e8b29be57010e2ebfa868bc Mon Sep 17 00:00:00 2001 +From: Daniel Matuschek +Date: Thu, 16 Jan 2014 07:36:35 +0100 +Subject: [PATCH 039/104] ASoC: wm8804: Set idle_bias_off to false Idle bias + has been change to remove warning on driver startup + +Signed-off-by: Daniel Matuschek +--- + sound/soc/codecs/wm8804.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c +index ba2a0f8..91974f9 100644 +--- a/sound/soc/codecs/wm8804.c ++++ b/sound/soc/codecs/wm8804.c +@@ -653,7 +653,7 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { + .probe = wm8804_probe, + .remove = wm8804_remove, + .set_bias_level = wm8804_set_bias_level, +- .idle_bias_off = true, ++ .idle_bias_off = false, + + .controls = wm8804_snd_controls, + .num_controls = ARRAY_SIZE(wm8804_snd_controls), + +From 146fd03395540ee2be74c216063d44375adf750a Mon Sep 17 00:00:00 2001 +From: Gordon Garrity +Date: Sat, 8 Mar 2014 16:56:57 +0000 +Subject: [PATCH 040/104] Add IQaudIO Sound Card support for Raspberry Pi + +--- + arch/arm/mach-bcm2708/bcm2708.c | 22 ++++++++ + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/iqaudio-dac.c | 111 ++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 142 insertions(+) + create mode 100644 sound/soc/bcm/iqaudio-dac.c + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 0ad5343..a812b51 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -678,6 +678,22 @@ static struct platform_device snd_pcm1794a_codec_device = { + }; + #endif + ++ ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++static struct platform_device snd_rpi_iqaudio_dac_device = { ++ .name = "snd-rpi-iqaudio-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++// Use the actual device name rather than generic driver name ++static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { ++ { ++ I2C_BOARD_INFO("pcm5122", 0x4c) ++ }, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -846,6 +862,12 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm1794a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) ++ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); ++#endif ++ ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index d4a00fc..faa89ef 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -39,3 +39,10 @@ config SND_BCM2708_SOC_RPI_DAC + select SND_SOC_PCM1794A + help + Say Y or M if you want to add support for RPi-DAC. ++ ++config SND_BCM2708_SOC_IQAUDIO_DAC ++ tristate "Support for IQaudIO-DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM512x_I2C ++ help ++ Say Y or M if you want to add support for IQaudIO-DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 826df7d..d597fb0 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -12,7 +12,9 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + snd-soc-hifiberry-dac-objs := hifiberry_dac.o + snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-rpi-dac-objs := rpi-dac.o ++snd-soc-iqaudio-dac-objs := iqaudio-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o +diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c +new file mode 100644 +index 0000000..8d0e2ae +--- /dev/null ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -0,0 +1,111 @@ ++/* ++ * ASoC Driver for IQaudIO DAC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT 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 ++ ++static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ ++ return 0; ++} ++ ++static int snd_rpi_iqaudio_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++// NOT USED struct snd_soc_dai *codec_dai = rtd->codec_dai; ++// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ unsigned int sample_bits = ++ snd_pcm_format_physical_width(params_format(params)); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_iqaudio_dac_ops = { ++ .hw_params = snd_rpi_iqaudio_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_iqaudio_dac_dai[] = { ++{ ++ .name = "IQaudIO DAC", ++ .stream_name = "IQaudIO DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm512x-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm512x.1-004c", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_iqaudio_dac_ops, ++ .init = snd_rpi_iqaudio_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_iqaudio_dac = { ++ .name = "IQaudIODAC", ++ .dai_link = snd_rpi_iqaudio_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_iqaudio_dac_dai), ++}; ++ ++static int snd_rpi_iqaudio_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_iqaudio_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_iqaudio_dac); ++ if (ret) ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_iqaudio_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_iqaudio_dac); ++} ++ ++static const struct of_device_id iqaudio_of_match[] = { ++ { .compatible = "iqaudio,iqaudio-dac", }, ++ {}, ++}; ++ ++static struct platform_driver snd_rpi_iqaudio_dac_driver = { ++ .driver = { ++ .name = "snd-rpi-iqaudio-dac", ++ .owner = THIS_MODULE, ++ .of_match_table = iqaudio_of_match, ++ }, ++ .probe = snd_rpi_iqaudio_dac_probe, ++ .remove = snd_rpi_iqaudio_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_iqaudio_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for IQAudio DAC"); ++MODULE_LICENSE("GPL v2"); + +From 27d378a18a7cbf89d874b933d7c37005f5f4ff60 Mon Sep 17 00:00:00 2001 +From: Howard Mitchell +Date: Mon, 2 Mar 2015 17:28:02 +0000 +Subject: [PATCH 041/104] Set a limit of 0dB on Digital Volume Control + +The main volume control in the PCM512x DAC has a range up to ++24dB. This is dangerously loud and can potentially cause massive +clipping in the output stages. Therefore this sets a sensible +limit of 0dB for this control. +--- + sound/soc/bcm/iqaudio-dac.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sound/soc/bcm/iqaudio-dac.c b/sound/soc/bcm/iqaudio-dac.c +index 8d0e2ae..aff7377 100644 +--- a/sound/soc/bcm/iqaudio-dac.c ++++ b/sound/soc/bcm/iqaudio-dac.c +@@ -25,7 +25,13 @@ + + static int snd_rpi_iqaudio_dac_init(struct snd_soc_pcm_runtime *rtd) + { +-// NOT USED struct snd_soc_codec *codec = rtd->codec; ++ int ret; ++ struct snd_soc_card *card = rtd->card; ++ struct snd_soc_codec *codec = rtd->codec; ++ ++ ret = snd_soc_limit_volume(codec, "Digital Playback Volume", 207); ++ if (ret < 0) ++ dev_warn(card->dev, "Failed to set volume limit: %d\n", ret); + + return 0; + } + +From d49a92454b9672d0e36893634f9b3fdb6c5defc3 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 18 Jun 2014 13:42:01 +0100 +Subject: [PATCH 042/104] vmstat: Workaround for issue where dirty page count + goes negative + +See: +https://github.com/raspberrypi/linux/issues/617 +http://www.spinics.net/lists/linux-mm/msg72236.html +--- + include/linux/vmstat.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h +index 82e7db7..f87d16d 100644 +--- a/include/linux/vmstat.h ++++ b/include/linux/vmstat.h +@@ -241,7 +241,11 @@ static inline void __inc_zone_state(struct zone *zone, enum zone_stat_item item) + static inline void __dec_zone_state(struct zone *zone, enum zone_stat_item item) + { + atomic_long_dec(&zone->vm_stat[item]); ++ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&zone->vm_stat[item]) < 0)) ++ atomic_long_set(&zone->vm_stat[item], 0); + atomic_long_dec(&vm_stat[item]); ++ if (item == NR_FILE_DIRTY && unlikely(atomic_long_read(&vm_stat[item]) < 0)) ++ atomic_long_set(&vm_stat[item], 0); + } + + static inline void __inc_zone_page_state(struct page *page, + +From 458e226338d78fbbbd88c1fe25c29d8bb3ad31b5 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 18:55:53 +0100 +Subject: [PATCH 043/104] BCM2708: armctrl: Add IRQ Device Tree support + +Add Device Tree IRQ support for BCM2708. +Usage is the same as for irq-bcm2835. +See binding document: brcm,bcm2835-armctrl-ic.txt + +A bank 3 is added to handle GPIO interrupts. This is done because +armctrl also handles GPIO interrupts. + +Signed-off-by: Noralf Tronnes + +BCM2708: armctrl: remove irq bank 3 + +irq bank 3 was needed by the pinctrl-bcm2708 and bcm2708_gpio +combination. It is no longer required. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/mach-bcm2708/armctrl.c | 96 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 96 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/armctrl.c b/arch/arm/mach-bcm2708/armctrl.c +index 96fa9b9..74bacb3 100644 +--- a/arch/arm/mach-bcm2708/armctrl.c ++++ b/arch/arm/mach-bcm2708/armctrl.c +@@ -23,6 +23,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -79,6 +81,99 @@ static void armctrl_unmask_irq(struct irq_data *d) + } + } + ++#ifdef CONFIG_OF ++ ++#define NR_IRQS_BANK0 21 ++#define NR_BANKS 3 ++#define IRQS_PER_BANK 32 ++ ++/* from drivers/irqchip/irq-bcm2835.c */ ++static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, ++ const u32 *intspec, unsigned int intsize, ++ unsigned long *out_hwirq, unsigned int *out_type) ++{ ++ if (WARN_ON(intsize != 2)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] >= NR_BANKS)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) ++ return -EINVAL; ++ ++ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) ++ return -EINVAL; ++ ++ if (intspec[0] == 0) ++ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; ++ else if (intspec[0] == 1) ++ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; ++ else ++ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; ++ ++ /* reverse remap_irqs[] */ ++ switch (*out_hwirq) { ++ case INTERRUPT_VC_JPEG: ++ *out_hwirq = INTERRUPT_JPEG; ++ break; ++ case INTERRUPT_VC_USB: ++ *out_hwirq = INTERRUPT_USB; ++ break; ++ case INTERRUPT_VC_3D: ++ *out_hwirq = INTERRUPT_3D; ++ break; ++ case INTERRUPT_VC_DMA2: ++ *out_hwirq = INTERRUPT_DMA2; ++ break; ++ case INTERRUPT_VC_DMA3: ++ *out_hwirq = INTERRUPT_DMA3; ++ break; ++ case INTERRUPT_VC_I2C: ++ *out_hwirq = INTERRUPT_I2C; ++ break; ++ case INTERRUPT_VC_SPI: ++ *out_hwirq = INTERRUPT_SPI; ++ break; ++ case INTERRUPT_VC_I2SPCM: ++ *out_hwirq = INTERRUPT_I2SPCM; ++ break; ++ case INTERRUPT_VC_SDIO: ++ *out_hwirq = INTERRUPT_SDIO; ++ break; ++ case INTERRUPT_VC_UART: ++ *out_hwirq = INTERRUPT_UART; ++ break; ++ case INTERRUPT_VC_ARASANSDIO: ++ *out_hwirq = INTERRUPT_ARASANSDIO; ++ break; ++ } ++ ++ *out_type = IRQ_TYPE_NONE; ++ return 0; ++} ++ ++static struct irq_domain_ops armctrl_ops = { ++ .xlate = armctrl_xlate ++}; ++ ++void __init armctrl_dt_init(void) ++{ ++ struct device_node *np; ++ struct irq_domain *domain; ++ ++ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); ++ if (!np) ++ return; ++ ++ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, ++ IRQ_ARMCTRL_START, 0, ++ &armctrl_ops, NULL); ++ WARN_ON(!domain); ++} ++#else ++void __init armctrl_dt_init(void) { } ++#endif /* CONFIG_OF */ ++ + #if defined(CONFIG_PM) + + /* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ +@@ -215,5 +310,6 @@ int __init armctrl_init(void __iomem * base, unsigned int irq_start, + + armctrl_pm_register(base, irq_start, resume_sources); + init_FIQ(FIQ_START); ++ armctrl_dt_init(); + return 0; + } + +From af606245311eeb7640ccfa4f9c596b2b05f6c3aa Mon Sep 17 00:00:00 2001 +From: notro +Date: Thu, 10 Jul 2014 13:59:47 +0200 +Subject: [PATCH 044/104] BCM2708: use pinctrl-bcm2835 + +Use pinctrl-bcm2835 instead of the pinctrl-bcm2708 and bcm2708_gpio +combination. + +Signed-off-by: Noralf Tronnes +--- + arch/arm/mach-bcm2708/Kconfig | 3 +++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + drivers/pinctrl/pinctrl-bcm2835.c | 2 +- + 3 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig +index 182e7ba..4cfae55 100644 +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -14,6 +14,9 @@ config BCM2708_DT + depends on MACH_BCM2708 + default n + select USE_OF ++ select ARCH_REQUIRE_GPIOLIB ++ select PINCTRL ++ select PINCTRL_BCM2835 + help + Enable Device Tree support for BCM2708 + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a812b51..a207ad8 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -816,7 +816,7 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO +- bcm_register_device(&bcm2708_gpio_device); ++ bcm_register_device_dt(&bcm2708_gpio_device); + #endif + #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) + w1_gpio_pdata.pin = w1_gpio_pin; +diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c +index 9aa8a3f..9d1149e 100644 +--- a/drivers/pinctrl/pinctrl-bcm2835.c ++++ b/drivers/pinctrl/pinctrl-bcm2835.c +@@ -382,7 +382,7 @@ static struct gpio_chip bcm2835_gpio_chip = { + .get = bcm2835_gpio_get, + .set = bcm2835_gpio_set, + .to_irq = bcm2835_gpio_to_irq, +- .base = -1, ++ .base = 0, + .ngpio = BCM2835_NUM_GPIOS, + .can_sleep = false, + }; + +From a51a99b3b595628233b62f8f4c3a88ed9f4e75b6 Mon Sep 17 00:00:00 2001 +From: notro +Date: Sun, 27 Jul 2014 20:12:58 +0200 +Subject: [PATCH 045/104] spi: bcm2708: add device tree support + +Add DT support to driver and add to .dtsi file. +Setup pins and spidev in .dts file. +SPI is disabled by default. + +Signed-off-by: Noralf Tronnes + +BCM2708: don't register SPI controller when using DT + +The device for the SPI controller is in the Device Tree. +Only register the device when not using DT. + +Signed-off-by: Noralf Tronnes + +spi: bcm2835: make driver available on ARCH_BCM2708 + +Make this driver available on ARCH_BCM2708 + +Signed-off-by: Noralf Tronnes + +bcm2708: Remove the prohibition on mixing SPIDEV and DT +--- + arch/arm/boot/dts/bcm2708.dtsi | 8 ++++++++ + arch/arm/mach-bcm2708/bcm2708.c | 7 ++++--- + drivers/spi/Kconfig | 4 ++-- + drivers/spi/spi-bcm2708.c | 8 ++++++++ + 4 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 96b7311..93f009a 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -82,5 +82,13 @@ + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <0>; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; + }; + }; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a207ad8..e366bb4 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -836,7 +836,7 @@ void __init bcm2708_init(void) + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + bcm_register_device(&bcm2708_alsa_devices[i]); + +- bcm_register_device(&bcm2708_spi_device); ++ bcm_register_device_dt(&bcm2708_spi_device); + bcm_register_device(&bcm2708_bsc0_device); + bcm_register_device(&bcm2708_bsc1_device); + +@@ -876,8 +876,9 @@ void __init bcm2708_init(void) + system_serial_low = serial; + + #ifdef CONFIG_BCM2708_SPIDEV +- spi_register_board_info(bcm2708_spi_devices, +- ARRAY_SIZE(bcm2708_spi_devices)); ++ if (!use_dt) ++ spi_register_board_info(bcm2708_spi_devices, ++ ARRAY_SIZE(bcm2708_spi_devices)); + #endif + } + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 9628e75..113bf64 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -77,7 +77,7 @@ config SPI_ATMEL + + config SPI_BCM2835 + tristate "BCM2835 SPI controller" +- depends on ARCH_BCM2835 || COMPILE_TEST ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST + help + This selects a driver for the Broadcom BCM2835 SPI master. + +@@ -88,7 +88,7 @@ config SPI_BCM2835 + + config SPI_BCM2708 + tristate "BCM2708 SPI controller driver (SPI0)" +- depends on MACH_BCM2708 ++ depends on MACH_BCM2708 || MACH_BCM2709 + help + This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This + driver is not compatible with the "Universal SPI Master" or the SPI slave +diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c +index 349d21f..041b5e2 100644 +--- a/drivers/spi/spi-bcm2708.c ++++ b/drivers/spi/spi-bcm2708.c +@@ -512,6 +512,7 @@ static int bcm2708_spi_probe(struct platform_device *pdev) + master->setup = bcm2708_spi_setup; + master->transfer = bcm2708_spi_transfer; + master->cleanup = bcm2708_spi_cleanup; ++ master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, master); + + bs = spi_master_get_devdata(master); +@@ -596,10 +597,17 @@ static int bcm2708_spi_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id bcm2708_spi_match[] = { ++ { .compatible = "brcm,bcm2708-spi", }, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_spi_match); ++ + static struct platform_driver bcm2708_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, ++ .of_match_table = bcm2708_spi_match, + }, + .probe = bcm2708_spi_probe, + .remove = bcm2708_spi_remove, + +From f84c5185e615c2ea21bde78393d819b3a891aea5 Mon Sep 17 00:00:00 2001 +From: notro +Date: Tue, 29 Jul 2014 11:04:49 +0200 +Subject: [PATCH 046/104] i2c: bcm2708: add device tree support + +Add DT support to driver and add to .dtsi file. +Setup pins in .dts file. +i2c is disabled by default. + +Signed-off-by: Noralf Tronnes + +bcm2708: don't register i2c controllers when using DT + +The devices for the i2c controllers are in the Device Tree. +Only register devices when not using DT. + +Signed-off-by: Noralf Tronnes + +i2c: bcm2835: make driver available on ARCH_BCM2708 + +Make this driver available on ARCH_BCM2708 + +Signed-off-by: Noralf Tronnes +--- + arch/arm/boot/dts/bcm2708.dtsi | 7 +++++++ + arch/arm/mach-bcm2708/bcm2708.c | 4 ++-- + drivers/i2c/busses/Kconfig | 4 ++-- + drivers/i2c/busses/i2c-bcm2708.c | 24 ++++++++++++++++++++++++ + 4 files changed, 35 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 93f009a..0a916a4 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -83,6 +83,13 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ + clk_spi: clock@2 { + compatible = "fixed-clock"; + reg = <2>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index e366bb4..a81200e 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -837,8 +837,8 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_alsa_devices[i]); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device(&bcm2708_bsc0_device); +- bcm_register_device(&bcm2708_bsc1_device); ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); + + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 00c7baa..92009dc 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -10,7 +10,7 @@ comment "PC SMBus host controller drivers" + + config I2C_BCM2708 + tristate "BCM2708 BSC" +- depends on MACH_BCM2708 ++ depends on MACH_BCM2708 || MACH_BCM2709 + help + Enabling this option will add BSC (Broadcom Serial Controller) + support for the BCM2708. BSC is a Broadcom proprietary bus compatible +@@ -381,7 +381,7 @@ config I2C_AXXIA + + config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" +- depends on ARCH_BCM2835 ++ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 + help + If you say yes to this option, support will be included for the + BCM2835 I2C controller. +diff --git a/drivers/i2c/busses/i2c-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c +index 7d385a3..526129b 100644 +--- a/drivers/i2c/busses/i2c-bcm2708.c ++++ b/drivers/i2c/busses/i2c-bcm2708.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -303,6 +304,21 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) + unsigned long bus_hz; + u32 cdiv; + ++ if (pdev->dev.of_node) { ++ u32 bus_clk_rate; ++ pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); ++ if (pdev->id < 0) { ++ dev_err(&pdev->dev, "alias is missing\n"); ++ return -EINVAL; ++ } ++ if (!of_property_read_u32(pdev->dev.of_node, ++ "clock-frequency", &bus_clk_rate)) ++ baudrate = bus_clk_rate; ++ else ++ dev_warn(&pdev->dev, ++ "Could not read clock-frequency property\n"); ++ } ++ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(&pdev->dev, "could not get IO memory\n"); +@@ -336,6 +352,7 @@ static int bcm2708_i2c_probe(struct platform_device *pdev) + adap->dev.parent = &pdev->dev; + adap->nr = pdev->id; + strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); ++ adap->dev.of_node = pdev->dev.of_node; + + switch (pdev->id) { + case 0: +@@ -416,10 +433,17 @@ static int bcm2708_i2c_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id bcm2708_i2c_of_match[] = { ++ { .compatible = "brcm,bcm2708-i2c" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); ++ + static struct platform_driver bcm2708_i2c_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, ++ .of_match_table = bcm2708_i2c_of_match, + }, + .probe = bcm2708_i2c_probe, + .remove = bcm2708_i2c_remove, + +From 6b6c91cab7046b83d671fa4087f3d987beed1831 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 14 Jul 2014 22:02:09 +0100 +Subject: [PATCH 047/104] hid: Reduce default mouse polling interval to 60Hz + +Reduces overhead when using X +--- + drivers/hid/usbhid/hid-core.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c +index bfbe1be..a738b25 100644 +--- a/drivers/hid/usbhid/hid-core.c ++++ b/drivers/hid/usbhid/hid-core.c +@@ -49,7 +49,7 @@ + * Module parameters. + */ + +-static unsigned int hid_mousepoll_interval; ++static unsigned int hid_mousepoll_interval = ~0; + module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); + MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); + +@@ -1090,8 +1090,12 @@ static int usbhid_start(struct hid_device *hid) + } + + /* Change the polling interval of mice. */ +- if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) +- interval = hid_mousepoll_interval; ++ if (hid->collection->usage == HID_GD_MOUSE) { ++ if (hid_mousepoll_interval == ~0 && interval < 16) ++ interval = 16; ++ else if (hid_mousepoll_interval != ~0 && hid_mousepoll_interval != 0) ++ interval = hid_mousepoll_interval; ++ } + + ret = -ENOMEM; + if (usb_endpoint_dir_in(endpoint)) { + +From 0ef1043b14270c32125c90eb57bb47d5ba0617a2 Mon Sep 17 00:00:00 2001 +From: P33M +Date: Thu, 24 Jul 2014 21:24:03 +0100 +Subject: [PATCH 048/104] usb: core: make overcurrent messages more prominent + +Hub overcurrent messages are more serious than "debug". Increase loglevel. +--- + drivers/usb/core/hub.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index d7c3d5a..6ca08fa 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -4922,7 +4922,7 @@ static void port_event(struct usb_hub *hub, int port1) + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + u16 status = 0, unused; + +- dev_dbg(&port_dev->dev, "over-current change\n"); ++ dev_notice(&port_dev->dev, "over-current change\n"); + usb_clear_port_feature(hdev, port1, + USB_PORT_FEAT_C_OVER_CURRENT); + msleep(100); /* Cool down */ + +From e4a1dd5d714904a221745386afe77cf3e5a3c623 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Thu, 7 Aug 2014 02:03:50 +0100 -Subject: [PATCH 058/172] Revert "ARM: dma: Use dma_pfn_offset for dma address +Subject: [PATCH 049/104] Revert "ARM: dma: Use dma_pfn_offset for dma address translation" This reverts commit 6ce0d20016925d031f1e24d64302e4c976d7cec6. @@ -114947,1774 +117169,26 @@ index b52101d..f5572d9 100644 } -From 431ffd837c0694e865b7aa72e284eb15fc122d7c Mon Sep 17 00:00:00 2001 -From: gellert -Date: Fri, 15 Aug 2014 16:35:06 +0100 -Subject: [PATCH 059/172] MMC: added alternative MMC driver - ---- - arch/arm/configs/bcmrpi_defconfig | 2 + - arch/arm/mach-bcm2708/bcm2708.c | 31 + - drivers/mmc/host/Kconfig | 40 +- - drivers/mmc/host/Makefile | 1 + - drivers/mmc/host/bcm2835-mmc.c | 1553 +++++++++++++++++++++++++++++++++++++ - 5 files changed, 1616 insertions(+), 11 deletions(-) - create mode 100644 drivers/mmc/host/bcm2835-mmc.c - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 7ba662a..0db8119 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -938,6 +938,8 @@ CONFIG_MMC=y - CONFIG_MMC_BLOCK_MINORS=32 - CONFIG_MMC_SDHCI=y - CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y - CONFIG_MMC_SPI=m - CONFIG_LEDS_CLASS=y - CONFIG_LEDS_GPIO=y -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index b45f327..5ff1299 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -414,6 +414,34 @@ static struct platform_device bcm2708_systemtimer_device = { - }, - }; - -+#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ -+static struct resource bcm2835_emmc_resources[] = { -+ [0] = { -+ .start = EMMC_BASE, -+ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ -+ /* the memory map actually makes SZ_4K available */ -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_ARASANSDIO, -+ .end = IRQ_ARASANSDIO, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+static u64 bcm2835_emmc_dmamask = 0xffffffffUL; -+ -+struct platform_device bcm2835_emmc_device = { -+ .name = "mmc-bcm2835", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), -+ .resource = bcm2835_emmc_resources, -+ .dev = { -+ .dma_mask = &bcm2835_emmc_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+#endif /* CONFIG_MMC_BCM2835 */ -+ - static struct resource bcm2708_powerman_resources[] = { - [0] = { - .start = PM_BASE, -@@ -794,6 +822,9 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2708_uart1_device); - bcm_register_device(&bcm2708_powerman_device); - -+#ifdef CONFIG_MMC_BCM2835 -+ bcm_register_device(&bcm2835_emmc_device); -+#endif - bcm2708_init_led(); - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index 2d6fbdd..b6077b5 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -281,17 +281,6 @@ config MMC_SDHCI_BCM_KONA - - If you have a controller with this interface, say Y or M here. - --config MMC_SDHCI_BCM2835 -- tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" -- depends on ARCH_BCM2835 -- depends on MMC_SDHCI_PLTFM -- select MMC_SDHCI_IO_ACCESSORS -- help -- This selects the BCM2835 SD/MMC controller. If you have a BCM2835 -- platform with SD or MMC devices, say Y or M here. -- -- If unsure, say N. -- - config MMC_MOXART - tristate "MOXART SD/MMC Host Controller support" - depends on ARCH_MOXART && MMC -@@ -313,6 +302,35 @@ config MMC_SDHCI_ST - If you have a controller with this interface, say Y or M here. - If unsure, say N. - -+config MMC_BCM2835 -+ tristate "MMC support on BCM2835" -+ depends on MACH_BCM2708 -+ help -+ This selects the MMC Interface on BCM2835. -+ -+ If you have a controller with this interface, say Y or M here. -+ -+ If unsure, say N. -+ -+config MMC_BCM2835_DMA -+ bool "DMA support on BCM2835 Arasan controller" -+ depends on MMC_BCM2835 -+ help -+ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 -+ based chips. -+ -+ If unsure, say N. -+ -+config MMC_BCM2835_PIO_DMA_BARRIER -+ int "Block count limit for PIO transfers" -+ depends on MMC_BCM2835 && MMC_BCM2835_DMA -+ range 0 256 -+ default 2 -+ help -+ The inclusive limit in bytes under which PIO will be used instead of DMA -+ -+ If unsure, say 2 here. -+ - config MMC_OMAP - tristate "TI OMAP Multimedia Card Interface support" - depends on ARCH_OMAP -diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile -index f7b0a77..378aed9 100644 ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -17,6 +17,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o - obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o - obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o - obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o -+obj-$(CONFIG_MMC_BCM2835) += bcm2835-mmc.o - obj-$(CONFIG_MMC_WBSD) += wbsd.o - obj-$(CONFIG_MMC_AU1X) += au1xmmc.o - obj-$(CONFIG_MMC_OMAP) += omap.o -diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c -new file mode 100644 -index 0000000..92febc3 ---- /dev/null -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -0,0 +1,1553 @@ -+/* -+ * BCM2835 MMC host driver. -+ * -+ * Author: Gellert Weisz -+ * Copyright 2014 -+ * -+ * Based on -+ * sdhci-bcm2708.c by Broadcom -+ * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko -+ * sdhci.c and sdhci-pci.c by Pierre Ossman -+ * -+ * 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 it will be useful, but WITHOUT -+ * 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 . -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "sdhci.h" -+ -+ -+#ifndef CONFIG_OF -+ #define BCM2835_CLOCK_FREQ 250000000 -+#endif -+ -+#define DRIVER_NAME "mmc-bcm2835" -+ -+#define DBG(f, x...) \ -+pr_debug(DRIVER_NAME " [%s()]: " f, __func__, ## x) -+ -+#ifndef CONFIG_MMC_BCM2835_DMA -+ #define FORCE_PIO -+#endif -+ -+ -+/* the inclusive limit in bytes under which PIO will be used instead of DMA */ -+#ifdef CONFIG_MMC_BCM2835_PIO_DMA_BARRIER -+#define PIO_DMA_BARRIER CONFIG_MMC_BCM2835_PIO_DMA_BARRIER -+#else -+#define PIO_DMA_BARRIER 00 -+#endif -+ -+#define MIN_FREQ 400000 -+#define TIMEOUT_VAL 0xE -+#define BCM2835_SDHCI_WRITE_DELAY(f) (((2 * 1000000) / f) + 1) -+ -+#ifndef BCM2708_PERI_BASE -+ #define BCM2708_PERI_BASE 0x20000000 -+#endif -+ -+/* FIXME: Needs IOMMU support */ -+#define BCM2835_VCMMU_SHIFT (0x7E000000 - BCM2708_PERI_BASE) -+ -+ -+struct bcm2835_host { -+ spinlock_t lock; -+ -+ void __iomem *ioaddr; -+ u32 phys_addr; -+ -+ struct mmc_host *mmc; -+ -+ u32 timeout; -+ -+ int clock; /* Current clock speed */ -+ u8 pwr; /* Current voltage */ -+ -+ unsigned int max_clk; /* Max possible freq */ -+ unsigned int timeout_clk; /* Timeout freq (KHz) */ -+ unsigned int clk_mul; /* Clock Muliplier value */ -+ -+ struct tasklet_struct finish_tasklet; /* Tasklet structures */ -+ -+ struct timer_list timer; /* Timer for timeouts */ -+ -+ struct sg_mapping_iter sg_miter; /* SG state for PIO */ -+ unsigned int blocks; /* remaining PIO blocks */ -+ -+ int irq; /* Device IRQ */ -+ -+ -+ u32 ier; /* cached registers */ -+ -+ struct mmc_request *mrq; /* Current request */ -+ struct mmc_command *cmd; /* Current command */ -+ struct mmc_data *data; /* Current data request */ -+ unsigned int data_early:1; /* Data finished before cmd */ -+ -+ wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */ -+ -+ u32 thread_isr; -+ -+ u32 shadow; -+ -+ /*DMA part*/ -+ struct dma_chan *dma_chan_rx; /* DMA channel for reads */ -+ struct dma_chan *dma_chan_tx; /* DMA channel for writes */ -+ struct dma_async_tx_descriptor *tx_desc; /* descriptor */ -+ -+ bool have_dma; -+ bool use_dma; -+ /*end of DMA part*/ -+ -+ int max_delay; /* maximum length of time spent waiting */ -+ -+ int flags; /* Host attributes */ -+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ -+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ -+#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ -+#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ -+#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ -+}; -+ -+ -+static inline void bcm2835_mmc_writel(struct bcm2835_host *host, u32 val, int reg) -+{ -+ writel(val, host->ioaddr + reg); -+ udelay(BCM2835_SDHCI_WRITE_DELAY(max(host->clock, MIN_FREQ))); -+} -+ -+static inline void mmc_raw_writel(struct bcm2835_host *host, u32 val, int reg) -+{ -+ writel(val, host->ioaddr + reg); -+} -+ -+static inline u32 bcm2835_mmc_readl(struct bcm2835_host *host, int reg) -+{ -+ return readl(host->ioaddr + reg); -+} -+ -+static inline void bcm2835_mmc_writew(struct bcm2835_host *host, u16 val, int reg) -+{ -+ u32 oldval = (reg == SDHCI_COMMAND) ? host->shadow : -+ bcm2835_mmc_readl(host, reg & ~3); -+ u32 word_num = (reg >> 1) & 1; -+ u32 word_shift = word_num * 16; -+ u32 mask = 0xffff << word_shift; -+ u32 newval = (oldval & ~mask) | (val << word_shift); -+ -+ if (reg == SDHCI_TRANSFER_MODE) -+ host->shadow = newval; -+ else -+ bcm2835_mmc_writel(host, newval, reg & ~3); -+ -+} -+ -+static inline void bcm2835_mmc_writeb(struct bcm2835_host *host, u8 val, int reg) -+{ -+ u32 oldval = bcm2835_mmc_readl(host, reg & ~3); -+ u32 byte_num = reg & 3; -+ u32 byte_shift = byte_num * 8; -+ u32 mask = 0xff << byte_shift; -+ u32 newval = (oldval & ~mask) | (val << byte_shift); -+ -+ bcm2835_mmc_writel(host, newval, reg & ~3); -+} -+ -+ -+static inline u16 bcm2835_mmc_readw(struct bcm2835_host *host, int reg) -+{ -+ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); -+ u32 word_num = (reg >> 1) & 1; -+ u32 word_shift = word_num * 16; -+ u32 word = (val >> word_shift) & 0xffff; -+ -+ return word; -+} -+ -+static inline u8 bcm2835_mmc_readb(struct bcm2835_host *host, int reg) -+{ -+ u32 val = bcm2835_mmc_readl(host, (reg & ~3)); -+ u32 byte_num = reg & 3; -+ u32 byte_shift = byte_num * 8; -+ u32 byte = (val >> byte_shift) & 0xff; -+ -+ return byte; -+} -+ -+static void bcm2835_mmc_unsignal_irqs(struct bcm2835_host *host, u32 clear) -+{ -+ u32 ier; -+ -+ ier = bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE); -+ ier &= ~clear; -+ /* change which requests generate IRQs - makes no difference to -+ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ -+ bcm2835_mmc_writel(host, ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+ -+static void bcm2835_mmc_dumpregs(struct bcm2835_host *host) -+{ -+ pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", -+ mmc_hostname(host->mmc)); -+ -+ pr_debug(DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_DMA_ADDRESS), -+ bcm2835_mmc_readw(host, SDHCI_HOST_VERSION)); -+ pr_debug(DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_BLOCK_SIZE), -+ bcm2835_mmc_readw(host, SDHCI_BLOCK_COUNT)); -+ pr_debug(DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_ARGUMENT), -+ bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE)); -+ pr_debug(DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE), -+ bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL)); -+ pr_debug(DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_POWER_CONTROL), -+ bcm2835_mmc_readb(host, SDHCI_BLOCK_GAP_CONTROL)); -+ pr_debug(DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_WAKE_UP_CONTROL), -+ bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)); -+ pr_debug(DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", -+ bcm2835_mmc_readb(host, SDHCI_TIMEOUT_CONTROL), -+ bcm2835_mmc_readl(host, SDHCI_INT_STATUS)); -+ pr_debug(DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_INT_ENABLE), -+ bcm2835_mmc_readl(host, SDHCI_SIGNAL_ENABLE)); -+ pr_debug(DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_ACMD12_ERR), -+ bcm2835_mmc_readw(host, SDHCI_SLOT_INT_STATUS)); -+ pr_debug(DRIVER_NAME ": Caps: 0x%08x | Caps_1: 0x%08x\n", -+ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES), -+ bcm2835_mmc_readl(host, SDHCI_CAPABILITIES_1)); -+ pr_debug(DRIVER_NAME ": Cmd: 0x%08x | Max curr: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_COMMAND), -+ bcm2835_mmc_readl(host, SDHCI_MAX_CURRENT)); -+ pr_debug(DRIVER_NAME ": Host ctl2: 0x%08x\n", -+ bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2)); -+ -+ pr_debug(DRIVER_NAME ": ===========================================\n"); -+} -+ -+ -+static void bcm2835_mmc_reset(struct bcm2835_host *host, u8 mask) -+{ -+ unsigned long timeout; -+ -+ bcm2835_mmc_writeb(host, mask, SDHCI_SOFTWARE_RESET); -+ -+ if (mask & SDHCI_RESET_ALL) -+ host->clock = 0; -+ -+ /* Wait max 100 ms */ -+ timeout = 100; -+ -+ /* hw clears the bit when it's done */ -+ while (bcm2835_mmc_readb(host, SDHCI_SOFTWARE_RESET) & mask) { -+ if (timeout == 0) { -+ pr_err("%s: Reset 0x%x never completed.\n", -+ mmc_hostname(host->mmc), (int)mask); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } -+ -+ if (100-timeout > 10 && 100-timeout > host->max_delay) { -+ host->max_delay = 100-timeout; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+} -+ -+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); -+ -+static void bcm2835_mmc_init(struct bcm2835_host *host, int soft) -+{ -+ if (soft) -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD|SDHCI_RESET_DATA); -+ else -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | -+ SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | -+ SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | -+ SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END | -+ SDHCI_INT_RESPONSE; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+ -+ if (soft) { -+ /* force clock reconfiguration */ -+ host->clock = 0; -+ bcm2835_mmc_set_ios(host->mmc, &host->mmc->ios); -+ } -+} -+ -+ -+ -+static void bcm2835_mmc_finish_data(struct bcm2835_host *host); -+ -+static void bcm2835_mmc_dma_complete(void *param) -+{ -+ struct bcm2835_host *host = param; -+ struct dma_chan *dma_chan; -+ unsigned long flags; -+ u32 dir_data; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) { -+ /* otherwise handled in SDHCI IRQ */ -+ dma_chan = host->dma_chan_rx; -+ dir_data = DMA_FROM_DEVICE; -+ -+ dma_unmap_sg(dma_chan->device->dev, -+ host->data->sg, host->data->sg_len, -+ dir_data); -+ -+ bcm2835_mmc_finish_data(host); -+ } -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_bcm2835_mmc_read_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len, chunk; -+ -+ u32 uninitialized_var(scratch); -+ u8 *buf; -+ -+ blksize = host->data->blksz; -+ chunk = 0; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = host->sg_miter.addr; -+ -+ while (len) { -+ if (chunk == 0) { -+ scratch = bcm2835_mmc_readl(host, SDHCI_BUFFER); -+ chunk = 4; -+ } -+ -+ *buf = scratch & 0xFF; -+ -+ buf++; -+ scratch >>= 8; -+ chunk--; -+ len--; -+ } -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+static void bcm2835_bcm2835_mmc_write_block_pio(struct bcm2835_host *host) -+{ -+ unsigned long flags; -+ size_t blksize, len, chunk; -+ u32 scratch; -+ u8 *buf; -+ -+ blksize = host->data->blksz; -+ chunk = 0; -+ chunk = 0; -+ scratch = 0; -+ -+ local_irq_save(flags); -+ -+ while (blksize) { -+ if (!sg_miter_next(&host->sg_miter)) -+ BUG(); -+ -+ len = min(host->sg_miter.length, blksize); -+ -+ blksize -= len; -+ host->sg_miter.consumed = len; -+ -+ buf = host->sg_miter.addr; -+ -+ while (len) { -+ scratch |= (u32)*buf << (chunk * 8); -+ -+ buf++; -+ chunk++; -+ len--; -+ -+ if ((chunk == 4) || ((len == 0) && (blksize == 0))) { -+ mmc_raw_writel(host, scratch, SDHCI_BUFFER); -+ chunk = 0; -+ scratch = 0; -+ } -+ } -+ } -+ -+ sg_miter_stop(&host->sg_miter); -+ -+ local_irq_restore(flags); -+} -+ -+ -+static void bcm2835_mmc_transfer_pio(struct bcm2835_host *host) -+{ -+ u32 mask; -+ -+ BUG_ON(!host->data); -+ -+ if (host->blocks == 0) -+ return; -+ -+ if (host->data->flags & MMC_DATA_READ) -+ mask = SDHCI_DATA_AVAILABLE; -+ else -+ mask = SDHCI_SPACE_AVAILABLE; -+ -+ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { -+ -+ if (host->data->flags & MMC_DATA_READ) -+ bcm2835_bcm2835_mmc_read_block_pio(host); -+ else -+ bcm2835_bcm2835_mmc_write_block_pio(host); -+ -+ host->blocks--; -+ -+ /* QUIRK used in sdhci.c removes the 'if' */ -+ /* but it seems this is unnecessary */ -+ if (host->blocks == 0) -+ break; -+ -+ -+ } -+} -+ -+ -+static void bcm2835_mmc_transfer_dma(struct bcm2835_host *host) -+{ -+ u32 len, dir_data, dir_slave; -+ struct dma_async_tx_descriptor *desc = NULL; -+ struct dma_chan *dma_chan; -+ -+ -+ WARN_ON(!host->data); -+ -+ if (!host->data) -+ return; -+ -+ if (host->blocks == 0) -+ return; -+ -+ if (host->data->flags & MMC_DATA_READ) { -+ dma_chan = host->dma_chan_rx; -+ dir_data = DMA_FROM_DEVICE; -+ dir_slave = DMA_DEV_TO_MEM; -+ } else { -+ dma_chan = host->dma_chan_tx; -+ dir_data = DMA_TO_DEVICE; -+ dir_slave = DMA_MEM_TO_DEV; -+ } -+ -+ BUG_ON(!dma_chan->device); -+ BUG_ON(!dma_chan->device->dev); -+ BUG_ON(!host->data->sg); -+ -+ len = dma_map_sg(dma_chan->device->dev, host->data->sg, -+ host->data->sg_len, dir_data); -+ if (len > 0) { -+ desc = dmaengine_prep_slave_sg(dma_chan, host->data->sg, -+ len, dir_slave, -+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -+ } else { -+ dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n"); -+ } -+ if (desc) { -+ bcm2835_mmc_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | -+ SDHCI_INT_SPACE_AVAIL); -+ host->tx_desc = desc; -+ desc->callback = bcm2835_mmc_dma_complete; -+ desc->callback_param = host; -+ dmaengine_submit(desc); -+ dma_async_issue_pending(dma_chan); -+ } -+ -+} -+ -+ -+ -+static void bcm2835_mmc_set_transfer_irqs(struct bcm2835_host *host) -+{ -+ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; -+ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; -+ -+ if (host->use_dma) -+ host->ier = (host->ier & ~pio_irqs) | dma_irqs; -+ else -+ host->ier = (host->ier & ~dma_irqs) | pio_irqs; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+} -+ -+ -+static void bcm2835_mmc_prepare_data(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ u8 count; -+ struct mmc_data *data = cmd->data; -+ -+ WARN_ON(host->data); -+ -+ if (data || (cmd->flags & MMC_RSP_BUSY)) { -+ count = TIMEOUT_VAL; -+ bcm2835_mmc_writeb(host, count, SDHCI_TIMEOUT_CONTROL); -+ } -+ -+ if (!data) -+ return; -+ -+ /* Sanity checks */ -+ BUG_ON(data->blksz * data->blocks > 524288); -+ BUG_ON(data->blksz > host->mmc->max_blk_size); -+ BUG_ON(data->blocks > 65535); -+ -+ host->data = data; -+ host->data_early = 0; -+ host->data->bytes_xfered = 0; -+ -+ -+ if (!(host->flags & SDHCI_REQ_USE_DMA)) { -+ int flags; -+ -+ flags = SG_MITER_ATOMIC; -+ if (host->data->flags & MMC_DATA_READ) -+ flags |= SG_MITER_TO_SG; -+ else -+ flags |= SG_MITER_FROM_SG; -+ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); -+ host->blocks = data->blocks; -+ } -+ -+ host->use_dma = host->have_dma && data->blocks > PIO_DMA_BARRIER; -+ -+ bcm2835_mmc_set_transfer_irqs(host); -+ -+ /* Set the DMA boundary value and block size */ -+ bcm2835_mmc_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, -+ data->blksz), SDHCI_BLOCK_SIZE); -+ bcm2835_mmc_writew(host, data->blocks, SDHCI_BLOCK_COUNT); -+ -+ BUG_ON(!host->data); -+} -+ -+static void bcm2835_mmc_set_transfer_mode(struct bcm2835_host *host, -+ struct mmc_command *cmd) -+{ -+ u16 mode; -+ struct mmc_data *data = cmd->data; -+ -+ if (data == NULL) { -+ /* clear Auto CMD settings for no data CMDs */ -+ mode = bcm2835_mmc_readw(host, SDHCI_TRANSFER_MODE); -+ bcm2835_mmc_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | -+ SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); -+ return; -+ } -+ -+ WARN_ON(!host->data); -+ -+ mode = SDHCI_TRNS_BLK_CNT_EN; -+ -+ if ((mmc_op_multi(cmd->opcode) || data->blocks > 1)) { -+ mode |= SDHCI_TRNS_MULTI; -+ -+ /* -+ * If we are sending CMD23, CMD12 never gets sent -+ * on successful completion (so no Auto-CMD12). -+ */ -+ if (!host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD12)) -+ mode |= SDHCI_TRNS_AUTO_CMD12; -+ else if (host->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23)) { -+ mode |= SDHCI_TRNS_AUTO_CMD23; -+ bcm2835_mmc_writel(host, host->mrq->sbc->arg, SDHCI_ARGUMENT2); -+ } -+ } -+ -+ if (data->flags & MMC_DATA_READ) -+ mode |= SDHCI_TRNS_READ; -+ if (host->flags & SDHCI_REQ_USE_DMA) -+ mode |= SDHCI_TRNS_DMA; -+ -+ bcm2835_mmc_writew(host, mode, SDHCI_TRANSFER_MODE); -+} -+ -+void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd) -+{ -+ int flags; -+ u32 mask; -+ unsigned long timeout; -+ -+ WARN_ON(host->cmd); -+ -+ /* Wait max 10 ms */ -+ timeout = 1000; -+ -+ mask = SDHCI_CMD_INHIBIT; -+ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) -+ mask |= SDHCI_DATA_INHIBIT; -+ -+ /* We shouldn't wait for data inihibit for stop commands, even -+ though they might use busy signaling */ -+ if (host->mrq->data && (cmd == host->mrq->data->stop)) -+ mask &= ~SDHCI_DATA_INHIBIT; -+ -+ while (bcm2835_mmc_readl(host, SDHCI_PRESENT_STATE) & mask) { -+ if (timeout == 0) { -+ pr_err("%s: Controller never released inhibit bit(s).\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ cmd->error = -EIO; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ timeout--; -+ udelay(10); -+ } -+ -+ if ((1000-timeout)/100 > 1 && (1000-timeout)/100 > host->max_delay) { -+ host->max_delay = (1000-timeout)/100; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+ -+ timeout = jiffies; -+#ifdef CONFIG_OF -+ if (!cmd->data && cmd->busy_timeout > 9000) -+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; -+ else -+#endif -+ timeout += 10 * HZ; -+ mod_timer(&host->timer, timeout); -+ -+ host->cmd = cmd; -+ -+ bcm2835_mmc_prepare_data(host, cmd); -+ -+ bcm2835_mmc_writel(host, cmd->arg, SDHCI_ARGUMENT); -+ -+ bcm2835_mmc_set_transfer_mode(host, cmd); -+ -+ if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { -+ pr_err("%s: Unsupported response type!\n", -+ mmc_hostname(host->mmc)); -+ cmd->error = -EINVAL; -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ -+ if (!(cmd->flags & MMC_RSP_PRESENT)) -+ flags = SDHCI_CMD_RESP_NONE; -+ else if (cmd->flags & MMC_RSP_136) -+ flags = SDHCI_CMD_RESP_LONG; -+ else if (cmd->flags & MMC_RSP_BUSY) -+ flags = SDHCI_CMD_RESP_SHORT_BUSY; -+ else -+ flags = SDHCI_CMD_RESP_SHORT; -+ -+ if (cmd->flags & MMC_RSP_CRC) -+ flags |= SDHCI_CMD_CRC; -+ if (cmd->flags & MMC_RSP_OPCODE) -+ flags |= SDHCI_CMD_INDEX; -+ -+ if (cmd->data) -+ flags |= SDHCI_CMD_DATA; -+ -+ bcm2835_mmc_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); -+} -+ -+ -+static void bcm2835_mmc_finish_data(struct bcm2835_host *host) -+{ -+ struct mmc_data *data; -+ -+ BUG_ON(!host->data); -+ -+ data = host->data; -+ host->data = NULL; -+ -+ if (data->error) -+ data->bytes_xfered = 0; -+ else -+ data->bytes_xfered = data->blksz * data->blocks; -+ -+ /* -+ * Need to send CMD12 if - -+ * a) open-ended multiblock transfer (no CMD23) -+ * b) error in multiblock transfer -+ */ -+ if (data->stop && -+ (data->error || -+ !host->mrq->sbc)) { -+ -+ /* -+ * The controller needs a reset of internal state machines -+ * upon error conditions. -+ */ -+ if (data->error) { -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); -+ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); -+ } -+ -+ bcm2835_mmc_send_command(host, data->stop); -+ } else -+ tasklet_schedule(&host->finish_tasklet); -+} -+ -+static void bcm2835_mmc_finish_command(struct bcm2835_host *host) -+{ -+ int i; -+ -+ BUG_ON(host->cmd == NULL); -+ -+ if (host->cmd->flags & MMC_RSP_PRESENT) { -+ if (host->cmd->flags & MMC_RSP_136) { -+ /* CRC is stripped so we need to do some shifting. */ -+ for (i = 0; i < 4; i++) { -+ host->cmd->resp[i] = bcm2835_mmc_readl(host, -+ SDHCI_RESPONSE + (3-i)*4) << 8; -+ if (i != 3) -+ host->cmd->resp[i] |= -+ bcm2835_mmc_readb(host, -+ SDHCI_RESPONSE + (3-i)*4-1); -+ } -+ } else { -+ host->cmd->resp[0] = bcm2835_mmc_readl(host, SDHCI_RESPONSE); -+ } -+ } -+ -+ host->cmd->error = 0; -+ -+ /* Finished CMD23, now send actual command. */ -+ if (host->cmd == host->mrq->sbc) { -+ host->cmd = NULL; -+ bcm2835_mmc_send_command(host, host->mrq->cmd); -+ -+ if (host->mrq->cmd->data && host->use_dma) { -+ /* DMA transfer starts now, PIO starts after interrupt */ -+ bcm2835_mmc_transfer_dma(host); -+ } -+ } else { -+ -+ /* Processed actual command. */ -+ if (host->data && host->data_early) -+ bcm2835_mmc_finish_data(host); -+ -+ if (!host->cmd->data) -+ tasklet_schedule(&host->finish_tasklet); -+ -+ host->cmd = NULL; -+ } -+} -+ -+ -+static void bcm2835_mmc_timeout_timer(unsigned long data) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = (struct bcm2835_host *)data; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (host->mrq) { -+ pr_err("%s: Timeout waiting for hardware interrupt.\n", -+ mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ -+ if (host->data) { -+ host->data->error = -ETIMEDOUT; -+ bcm2835_mmc_finish_data(host); -+ } else { -+ if (host->cmd) -+ host->cmd->error = -ETIMEDOUT; -+ else -+ host->mrq->cmd->error = -ETIMEDOUT; -+ -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ } -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+ -+static void bcm2835_mmc_enable_sdio_irq_nolock(struct bcm2835_host *host, int enable) -+{ -+ if (!(host->flags & SDHCI_DEVICE_DEAD)) { -+ if (enable) -+ host->ier |= SDHCI_INT_CARD_INT; -+ else -+ host->ier &= ~SDHCI_INT_CARD_INT; -+ -+ bcm2835_mmc_writel(host, host->ier, SDHCI_INT_ENABLE); -+ bcm2835_mmc_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); -+ mmiowb(); -+ } -+} -+ -+static void bcm2835_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) -+{ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ if (enable) -+ host->flags |= SDHCI_SDIO_IRQ_ENABLED; -+ else -+ host->flags &= ~SDHCI_SDIO_IRQ_ENABLED; -+ -+ bcm2835_mmc_enable_sdio_irq_nolock(host, enable); -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+static void bcm2835_mmc_cmd_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ -+ BUG_ON(intmask == 0); -+ -+ if (!host->cmd) { -+ pr_err("%s: Got command interrupt 0x%08x even " -+ "though no command operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_TIMEOUT) -+ host->cmd->error = -ETIMEDOUT; -+ else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT | -+ SDHCI_INT_INDEX)) { -+ host->cmd->error = -EILSEQ; -+ } -+ -+ if (host->cmd->error) { -+ tasklet_schedule(&host->finish_tasklet); -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_RESPONSE) -+ bcm2835_mmc_finish_command(host); -+ -+} -+ -+static void bcm2835_mmc_data_irq(struct bcm2835_host *host, u32 intmask) -+{ -+ struct dma_chan *dma_chan; -+ u32 dir_data; -+ -+ BUG_ON(intmask == 0); -+ -+ if (!host->data) { -+ /* -+ * The "data complete" interrupt is also used to -+ * indicate that a busy state has ended. See comment -+ * above in sdhci_cmd_irq(). -+ */ -+ if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { -+ if (intmask & SDHCI_INT_DATA_END) { -+ bcm2835_mmc_finish_command(host); -+ return; -+ } -+ } -+ -+ pr_debug("%s: Got data interrupt 0x%08x even " -+ "though no data operation was in progress.\n", -+ mmc_hostname(host->mmc), (unsigned)intmask); -+ bcm2835_mmc_dumpregs(host); -+ -+ return; -+ } -+ -+ if (intmask & SDHCI_INT_DATA_TIMEOUT) -+ host->data->error = -ETIMEDOUT; -+ else if (intmask & SDHCI_INT_DATA_END_BIT) -+ host->data->error = -EILSEQ; -+ else if ((intmask & SDHCI_INT_DATA_CRC) && -+ SDHCI_GET_CMD(bcm2835_mmc_readw(host, SDHCI_COMMAND)) -+ != MMC_BUS_TEST_R) -+ host->data->error = -EILSEQ; -+ -+ if (host->use_dma) { -+ if (host->data->flags & MMC_DATA_WRITE) { -+ /* IRQ handled here */ -+ -+ dma_chan = host->dma_chan_tx; -+ dir_data = DMA_TO_DEVICE; -+ dma_unmap_sg(dma_chan->device->dev, -+ host->data->sg, host->data->sg_len, -+ dir_data); -+ -+ bcm2835_mmc_finish_data(host); -+ } -+ -+ } else { -+ if (host->data->error) -+ bcm2835_mmc_finish_data(host); -+ else { -+ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) -+ bcm2835_mmc_transfer_pio(host); -+ -+ if (intmask & SDHCI_INT_DATA_END) { -+ if (host->cmd) { -+ /* -+ * Data managed to finish before the -+ * command completed. Make sure we do -+ * things in the proper order. -+ */ -+ host->data_early = 1; -+ } else { -+ bcm2835_mmc_finish_data(host); -+ } -+ } -+ } -+ } -+} -+ -+ -+static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) -+{ -+ irqreturn_t result = IRQ_NONE; -+ struct bcm2835_host *host = dev_id; -+ u32 intmask, mask, unexpected = 0; -+ int max_loops = 16; -+#ifndef CONFIG_OF -+ int cardint = 0; -+#endif -+ -+ spin_lock(&host->lock); -+ -+ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ -+ if (!intmask || intmask == 0xffffffff) { -+ result = IRQ_NONE; -+ goto out; -+ } -+ -+ do { -+ /* Clear selected interrupts. */ -+ mask = intmask & (SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | -+ SDHCI_INT_BUS_POWER); -+ bcm2835_mmc_writel(host, mask, SDHCI_INT_STATUS); -+ -+ -+ if (intmask & SDHCI_INT_CMD_MASK) -+ bcm2835_mmc_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); -+ -+ if (intmask & SDHCI_INT_DATA_MASK) -+ bcm2835_mmc_data_irq(host, intmask & SDHCI_INT_DATA_MASK); -+ -+ if (intmask & SDHCI_INT_BUS_POWER) -+ pr_err("%s: Card is consuming too much power!\n", -+ mmc_hostname(host->mmc)); -+ -+ if (intmask & SDHCI_INT_CARD_INT) { -+#ifndef CONFIG_OF -+ cardint = 1; -+#else -+ bcm2835_mmc_enable_sdio_irq_nolock(host, false); -+ host->thread_isr |= SDHCI_INT_CARD_INT; -+ result = IRQ_WAKE_THREAD; -+#endif -+ } -+ -+ intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | -+ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | -+ SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | -+ SDHCI_INT_CARD_INT); -+ -+ if (intmask) { -+ unexpected |= intmask; -+ bcm2835_mmc_writel(host, intmask, SDHCI_INT_STATUS); -+ } -+ -+ if (result == IRQ_NONE) -+ result = IRQ_HANDLED; -+ -+ intmask = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ } while (intmask && --max_loops); -+out: -+ spin_unlock(&host->lock); -+ -+ if (unexpected) { -+ pr_err("%s: Unexpected interrupt 0x%08x.\n", -+ mmc_hostname(host->mmc), unexpected); -+ bcm2835_mmc_dumpregs(host); -+ } -+ -+#ifndef CONFIG_OF -+ if (cardint) -+ mmc_signal_sdio_irq(host->mmc); -+#endif -+ -+ return result; -+} -+ -+#ifdef CONFIG_OF -+static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) -+{ -+ struct bcm2835_host *host = dev_id; -+ unsigned long flags; -+ u32 isr; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ isr = host->thread_isr; -+ host->thread_isr = 0; -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ if (isr & SDHCI_INT_CARD_INT) { -+ sdio_run_irqs(host->mmc); -+ -+ spin_lock_irqsave(&host->lock, flags); -+ if (host->flags & SDHCI_SDIO_IRQ_ENABLED) -+ bcm2835_mmc_enable_sdio_irq_nolock(host, true); -+ spin_unlock_irqrestore(&host->lock, flags); -+ } -+ -+ return isr ? IRQ_HANDLED : IRQ_NONE; -+} -+#endif -+ -+ -+ -+void bcm2835_mmc_set_clock(struct bcm2835_host *host, unsigned int clock) -+{ -+ int div = 0; /* Initialized for compiler warning */ -+ int real_div = div, clk_mul = 1; -+ u16 clk = 0; -+ unsigned long timeout; -+ -+ -+ host->mmc->actual_clock = 0; -+ -+ bcm2835_mmc_writew(host, 0, SDHCI_CLOCK_CONTROL); -+ -+ if (clock == 0) -+ return; -+ -+ /* Version 3.00 divisors must be a multiple of 2. */ -+ if (host->max_clk <= clock) -+ div = 1; -+ else { -+ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; -+ div += 2) { -+ if ((host->max_clk / div) <= clock) -+ break; -+ } -+ } -+ -+ real_div = div; -+ div >>= 1; -+ -+ if (real_div) -+ host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; -+ -+ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; -+ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) -+ << SDHCI_DIVIDER_HI_SHIFT; -+ clk |= SDHCI_CLOCK_INT_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+ -+ /* Wait max 20 ms */ -+ timeout = 20; -+ while (!((clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL)) -+ & SDHCI_CLOCK_INT_STABLE)) { -+ if (timeout == 0) { -+ pr_err("%s: Internal clock never " -+ "stabilised.\n", mmc_hostname(host->mmc)); -+ bcm2835_mmc_dumpregs(host); -+ return; -+ } -+ timeout--; -+ mdelay(1); -+ } -+ -+ if (20-timeout > 10 && 20-timeout > host->max_delay) { -+ host->max_delay = 20-timeout; -+ pr_warning("Warning: MMC controller hung for %d ms\n", host->max_delay); -+ } -+ -+ clk |= SDHCI_CLOCK_CARD_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+} -+ -+static void bcm2835_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ -+ host = mmc_priv(mmc); -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ WARN_ON(host->mrq != NULL); -+ -+ host->mrq = mrq; -+ -+ if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) -+ bcm2835_mmc_send_command(host, mrq->sbc); -+ else -+ bcm2835_mmc_send_command(host, mrq->cmd); -+ -+ mmiowb(); -+ spin_unlock_irqrestore(&host->lock, flags); -+ -+ if (!(mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) && mrq->cmd->data && host->use_dma) { -+ /* DMA transfer starts now, PIO starts after interrupt */ -+ bcm2835_mmc_transfer_dma(host); -+ } -+} -+ -+ -+static void bcm2835_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -+{ -+ -+ struct bcm2835_host *host = mmc_priv(mmc); -+ unsigned long flags; -+ u8 ctrl; -+ u16 clk, ctrl_2; -+ -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ if (!ios->clock || ios->clock != host->clock) { -+ bcm2835_mmc_set_clock(host, ios->clock); -+ host->clock = ios->clock; -+ } -+ -+ if (host->pwr != SDHCI_POWER_330) { -+ host->pwr = SDHCI_POWER_330; -+ bcm2835_mmc_writeb(host, SDHCI_POWER_330 | SDHCI_POWER_ON, SDHCI_POWER_CONTROL); -+ } -+ -+ ctrl = bcm2835_mmc_readb(host, SDHCI_HOST_CONTROL); -+ -+ /* set bus width */ -+ ctrl &= ~SDHCI_CTRL_8BITBUS; -+ if (ios->bus_width == MMC_BUS_WIDTH_4) -+ ctrl |= SDHCI_CTRL_4BITBUS; -+ else -+ ctrl &= ~SDHCI_CTRL_4BITBUS; -+ -+ ctrl &= ~SDHCI_CTRL_HISPD; /* NO_HISPD_BIT */ -+ -+ -+ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); -+ /* -+ * We only need to set Driver Strength if the -+ * preset value enable is not set. -+ */ -+ ctrl_2 = bcm2835_mmc_readw(host, SDHCI_HOST_CONTROL2); -+ ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; -+ if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) -+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; -+ else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) -+ ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; -+ -+ bcm2835_mmc_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); -+ -+ /* Reset SD Clock Enable */ -+ clk = bcm2835_mmc_readw(host, SDHCI_CLOCK_CONTROL); -+ clk &= ~SDHCI_CLOCK_CARD_EN; -+ bcm2835_mmc_writew(host, clk, SDHCI_CLOCK_CONTROL); -+ -+ /* Re-enable SD Clock */ -+ bcm2835_mmc_set_clock(host, host->clock); -+ bcm2835_mmc_writeb(host, ctrl, SDHCI_HOST_CONTROL); -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+} -+ -+ -+static struct mmc_host_ops bcm2835_ops = { -+ .request = bcm2835_mmc_request, -+ .set_ios = bcm2835_mmc_set_ios, -+ .enable_sdio_irq = bcm2835_mmc_enable_sdio_irq, -+}; -+ -+ -+static void bcm2835_mmc_tasklet_finish(unsigned long param) -+{ -+ struct bcm2835_host *host; -+ unsigned long flags; -+ struct mmc_request *mrq; -+ -+ host = (struct bcm2835_host *)param; -+ -+ spin_lock_irqsave(&host->lock, flags); -+ -+ /* -+ * If this tasklet gets rescheduled while running, it will -+ * be run again afterwards but without any active request. -+ */ -+ if (!host->mrq) { -+ spin_unlock_irqrestore(&host->lock, flags); -+ return; -+ } -+ -+ del_timer(&host->timer); -+ -+ mrq = host->mrq; -+ -+ /* -+ * The controller needs a reset of internal state machines -+ * upon error conditions. -+ */ -+ if (!(host->flags & SDHCI_DEVICE_DEAD) && -+ ((mrq->cmd && mrq->cmd->error) || -+ (mrq->data && (mrq->data->error || -+ (mrq->data->stop && mrq->data->stop->error))))) { -+ -+ bcm2835_mmc_reset(host, SDHCI_RESET_CMD); -+ bcm2835_mmc_reset(host, SDHCI_RESET_DATA); -+ } -+ -+ host->mrq = NULL; -+ host->cmd = NULL; -+ host->data = NULL; -+ -+ mmiowb(); -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ mmc_request_done(host->mmc, mrq); -+} -+ -+ -+ -+int bcm2835_mmc_add_host(struct bcm2835_host *host) -+{ -+ struct mmc_host *mmc; -+#ifndef FORCE_PIO -+ struct dma_slave_config cfg; -+#endif -+ int ret; -+ -+ mmc = host->mmc; -+ -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ host->clk_mul = 0; -+ -+ mmc->ops = &bcm2835_ops; -+ mmc->f_max = host->max_clk; -+ mmc->f_max = host->max_clk; -+ mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_300; -+ -+ /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ -+ host->timeout_clk = mmc->f_max / 1000; -+#ifdef CONFIG_OF -+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; -+#endif -+ /* host controller capabilities */ -+ mmc->caps = MMC_CAP_CMD23 | MMC_CAP_ERASE | MMC_CAP_NEEDS_POLL | MMC_CAP_SDIO_IRQ | -+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_4_BIT_DATA; -+ -+ host->flags = SDHCI_AUTO_CMD23; -+ -+ spin_lock_init(&host->lock); -+ -+ -+#ifdef FORCE_PIO -+ pr_info("Forcing PIO mode\n"); -+ host->have_dma = false; -+#else -+ if (!host->dma_chan_tx || !host->dma_chan_rx || -+ IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { -+ pr_err("%s: Unable to initialise DMA channels. Falling back to PIO\n", DRIVER_NAME); -+ host->have_dma = false; -+ } else { -+ pr_info("DMA channels allocated for the MMC driver"); -+ host->have_dma = true; -+ -+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ cfg.slave_id = 11; /* DREQ channel */ -+ -+ cfg.direction = DMA_MEM_TO_DEV; -+ cfg.src_addr = 0; -+ cfg.dst_addr = host->phys_addr + SDHCI_BUFFER; -+ ret = dmaengine_slave_config(host->dma_chan_tx, &cfg); -+ -+ cfg.direction = DMA_DEV_TO_MEM; -+ cfg.src_addr = host->phys_addr + SDHCI_BUFFER; -+ cfg.dst_addr = 0; -+ ret = dmaengine_slave_config(host->dma_chan_rx, &cfg); -+ } -+#endif -+ -+ -+ mmc->max_segs = 128; -+ mmc->max_req_size = 524288; -+ mmc->max_seg_size = mmc->max_req_size; -+ mmc->max_blk_size = 512; -+ mmc->max_blk_count = 65535; -+ -+ /* report supported voltage ranges */ -+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -+ -+ tasklet_init(&host->finish_tasklet, -+ bcm2835_mmc_tasklet_finish, (unsigned long)host); -+ -+ setup_timer(&host->timer, bcm2835_mmc_timeout_timer, (unsigned long)host); -+ init_waitqueue_head(&host->buf_ready_int); -+ -+ bcm2835_mmc_init(host, 0); -+#ifndef CONFIG_OF -+ ret = request_irq(host->irq, bcm2835_mmc_irq, 0 /*IRQF_SHARED*/, -+ mmc_hostname(mmc), host); -+#else -+ ret = request_threaded_irq(host->irq, bcm2835_mmc_irq, bcm2835_mmc_thread_irq, -+ IRQF_SHARED, mmc_hostname(mmc), host); -+#endif -+ if (ret) { -+ pr_err("%s: Failed to request IRQ %d: %d\n", -+ mmc_hostname(mmc), host->irq, ret); -+ goto untasklet; -+ } -+ -+ mmiowb(); -+ mmc_add_host(mmc); -+ -+ pr_info("Load BCM2835 MMC driver\n"); -+ -+ return 0; -+ -+untasklet: -+ tasklet_kill(&host->finish_tasklet); -+ -+ return ret; -+} -+ -+static int bcm2835_mmc_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+#ifdef CONFIG_OF -+ struct device_node *node = dev->of_node; -+ struct clk *clk; -+#endif -+ struct resource *iomem; -+ struct bcm2835_host *host = NULL; -+ -+ int ret; -+ struct mmc_host *mmc; -+#if !defined(CONFIG_OF) && !defined(FORCE_PIO) -+ dma_cap_mask_t mask; -+#endif -+ -+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!iomem) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if (resource_size(iomem) < 0x100) -+ dev_err(&pdev->dev, "Invalid iomem size!\n"); -+ -+ mmc = mmc_alloc_host(sizeof(struct bcm2835_host), dev); -+ host = mmc_priv(mmc); -+ host->mmc = mmc; -+ -+ -+ if (IS_ERR(host)) { -+ ret = PTR_ERR(host); -+ goto err; -+ } -+ -+ host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; -+ -+#ifndef CONFIG_OF -+#ifndef FORCE_PIO -+ dma_cap_zero(mask); -+ /* we don't care about the channel, any would work */ -+ dma_cap_set(DMA_SLAVE, mask); -+ -+ host->dma_chan_tx = dma_request_channel(mask, NULL, NULL); -+ host->dma_chan_rx = dma_request_channel(mask, NULL, NULL); -+#endif -+ host->max_clk = BCM2835_CLOCK_FREQ; -+ -+#else -+#ifndef FORCE_PIO -+ host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); -+ host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); -+#endif -+ clk = of_clk_get(node, 0); -+ if (IS_ERR(clk)) { -+ dev_err(dev, "get CLOCK failed\n"); -+ ret = PTR_ERR(clk); -+ goto out; -+ } -+ host->max_clk = (clk_get_rate(clk)); -+#endif -+ host->irq = platform_get_irq(pdev, 0); -+ -+ if (!request_mem_region(iomem->start, resource_size(iomem), -+ mmc_hostname(host->mmc))) { -+ dev_err(&pdev->dev, "cannot request region\n"); -+ ret = -EBUSY; -+ goto err_request; -+ } -+ -+ host->ioaddr = ioremap(iomem->start, resource_size(iomem)); -+ if (!host->ioaddr) { -+ dev_err(&pdev->dev, "failed to remap registers\n"); -+ ret = -ENOMEM; -+ goto err_remap; -+ } -+ -+ platform_set_drvdata(pdev, host); -+ -+ -+ if (host->irq <= 0) { -+ dev_err(dev, "get IRQ failed\n"); -+ ret = -EINVAL; -+ goto out; -+ } -+ -+ -+#ifndef CONFIG_OF -+ mmc->caps |= MMC_CAP_4_BIT_DATA; -+#else -+ mmc_of_parse(mmc); -+#endif -+ host->timeout = msecs_to_jiffies(1000); -+ spin_lock_init(&host->lock); -+ mmc->ops = &bcm2835_ops; -+ return bcm2835_mmc_add_host(host); -+ -+ -+err_remap: -+ release_mem_region(iomem->start, resource_size(iomem)); -+err_request: -+ mmc_free_host(host->mmc); -+err: -+ dev_err(&pdev->dev, "%s failed %d\n", __func__, ret); -+ return ret; -+out: -+ if (mmc) -+ mmc_free_host(mmc); -+ return ret; -+} -+ -+static int bcm2835_mmc_remove(struct platform_device *pdev) -+{ -+ struct bcm2835_host *host = platform_get_drvdata(pdev); -+ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ unsigned long flags; -+ int dead; -+ u32 scratch; -+ -+ dead = 0; -+ scratch = bcm2835_mmc_readl(host, SDHCI_INT_STATUS); -+ if (scratch == (u32)-1) -+ dead = 1; -+ -+ -+ if (dead) { -+ spin_lock_irqsave(&host->lock, flags); -+ -+ host->flags |= SDHCI_DEVICE_DEAD; -+ -+ if (host->mrq) { -+ pr_err("%s: Controller removed during " -+ " transfer!\n", mmc_hostname(host->mmc)); -+ -+ host->mrq->cmd->error = -ENOMEDIUM; -+ tasklet_schedule(&host->finish_tasklet); -+ } -+ -+ spin_unlock_irqrestore(&host->lock, flags); -+ } -+ -+ mmc_remove_host(host->mmc); -+ -+ if (!dead) -+ bcm2835_mmc_reset(host, SDHCI_RESET_ALL); -+ -+ free_irq(host->irq, host); -+ -+ del_timer_sync(&host->timer); -+ -+ tasklet_kill(&host->finish_tasklet); -+ -+ iounmap(host->ioaddr); -+ release_mem_region(iomem->start, resource_size(iomem)); -+ mmc_free_host(host->mmc); -+ platform_set_drvdata(pdev, NULL); -+ -+ return 0; -+} -+ -+ -+static const struct of_device_id bcm2835_mmc_match[] = { -+ { .compatible = "brcm,bcm2835-mmc" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, bcm2835_mmc_match); -+ -+ -+ -+static struct platform_driver bcm2835_mmc_driver = { -+ .probe = bcm2835_mmc_probe, -+ .remove = bcm2835_mmc_remove, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = bcm2835_mmc_match, -+ }, -+}; -+module_platform_driver(bcm2835_mmc_driver); -+ -+MODULE_ALIAS("platform:mmc-bcm2835"); -+MODULE_DESCRIPTION("BCM2835 SDHCI driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Gellert Weisz"); - -From 0ccc5b840254ab00236b28ccb0156f59ddc72807 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Tue, 13 Jan 2015 17:12:18 +0000 -Subject: [PATCH 060/172] mmc: Disable CMD23 transfers on all cards - -Pending wire-level investigation of these types of transfers -and associated errors on bcm2835-mmc, disable for now. Fallback of -CMD18/CMD25 transfers will be used automatically by the MMC layer. - -Reported/Tested-by: Gellert Weisz ---- - drivers/mmc/core/quirks.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c -index dd1d1e0..f472082 100644 ---- a/drivers/mmc/core/quirks.c -+++ b/drivers/mmc/core/quirks.c -@@ -95,5 +95,9 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) - f->vendor_fixup(card, f->data); - } - } -+ /* SDHCI on BCM2708 - bug causes a certain sequence of CMD23 operations to fail. -+ * Disable this flag for all cards (fall-back to CMD25/CMD18 multi-block transfers). -+ */ -+ card->quirks |= MMC_QUIRK_BLK_NO_CMD23; - } - EXPORT_SYMBOL(mmc_fixup_device); - -From 83621e3bccbabed1a256973918dc5bcf9039a56b Mon Sep 17 00:00:00 2001 +From 61df27580635db9976f02cc1f67a76fef3775092 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Mon, 4 Aug 2014 10:06:56 +0200 -Subject: [PATCH 061/172] Added support for HiFiBerry DAC+ +Subject: [PATCH 050/104] Added support for HiFiBerry DAC+ The driver is based on the HiFiBerry DAC driver. However HiFiBerry DAC+ uses a different codec chip (PCM5122), therefore a new driver is necessary. --- - arch/arm/configs/bcmrpi_defconfig | 1 + arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++ sound/soc/bcm/Kconfig | 7 +++ sound/soc/bcm/Makefile | 2 + sound/soc/bcm/hifiberry_dacplus.c | 119 ++++++++++++++++++++++++++++++++++++++ - 5 files changed, 148 insertions(+) + 4 files changed, 147 insertions(+) create mode 100644 sound/soc/bcm/hifiberry_dacplus.c -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 0db8119..a8c60cf 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -782,6 +782,7 @@ CONFIG_SND_USB_6FIRE=m - CONFIG_SND_SOC=m - CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 5ff1299..aa62e55 100644 +index a81200e..5f7df9e 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -645,6 +645,20 @@ static struct platform_device snd_pcm5102a_codec_device = { +@@ -649,6 +649,20 @@ static struct platform_device snd_pcm5102a_codec_device = { }; #endif @@ -116735,20 +117209,20 @@ index 5ff1299..aa62e55 100644 #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) static struct platform_device snd_hifiberry_digi_device = { .name = "snd-hifiberry-digi", -@@ -845,6 +859,11 @@ void __init bcm2708_init(void) - bcm_register_device(&snd_pcm5102a_codec_device); +@@ -852,6 +866,11 @@ void __init bcm2708_init(void) + bcm_register_device_dt(&snd_pcm5102a_codec_device); #endif +#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+ bcm_register_device(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); ++ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); ++ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); +#endif + #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) - bcm_register_device(&snd_hifiberry_digi_device); - i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); + bcm_register_device_dt(&snd_hifiberry_digi_device); + i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index c621a5e..926a82b 100644 +index faa89ef..7675501 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -26,6 +26,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC @@ -116909,45 +117383,32 @@ index 0000000..c63387b +MODULE_DESCRIPTION("ASoC Driver for HiFiBerry DAC+"); +MODULE_LICENSE("GPL v2"); -From ecc316d9abb1a30fae40c0581aba6439aa732474 Mon Sep 17 00:00:00 2001 +From 7d55e4b440fa99bb3427e23d33aa9c055531a907 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Mon, 4 Aug 2014 11:09:58 +0200 -Subject: [PATCH 062/172] Added driver for HiFiBerry Amp amplifier add-on board +Subject: [PATCH 051/104] Added driver for HiFiBerry Amp amplifier add-on board The driver contains a low-level hardware driver for the TAS5713 and the drivers for the Raspberry Pi I2S subsystem. --- - arch/arm/configs/bcmrpi_defconfig | 1 + - arch/arm/mach-bcm2708/bcm2708.c | 20 +++ - sound/soc/bcm/Kconfig | 7 + - sound/soc/bcm/Makefile | 2 + - sound/soc/bcm/hifiberry_amp.c | 106 +++++++++++ - sound/soc/codecs/Kconfig | 4 + - sound/soc/codecs/Makefile | 2 + - sound/soc/codecs/tas5713.c | 362 ++++++++++++++++++++++++++++++++++++++ - sound/soc/codecs/tas5713.h | 210 ++++++++++++++++++++++ - 9 files changed, 714 insertions(+) + arch/arm/mach-bcm2708/bcm2708.c | 19 +++ + sound/soc/bcm/Kconfig | 7 + + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/hifiberry_amp.c | 106 ++++++++++++ + sound/soc/codecs/Kconfig | 4 + + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/tas5713.c | 362 ++++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/tas5713.h | 210 +++++++++++++++++++++++ + 8 files changed, 712 insertions(+) create mode 100644 sound/soc/bcm/hifiberry_amp.c create mode 100644 sound/soc/codecs/tas5713.c create mode 100644 sound/soc/codecs/tas5713.h -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index a8c60cf..8381041 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -784,6 +784,7 @@ CONFIG_SND_BCM2708_SOC_I2S=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m - CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m - CONFIG_SND_BCM2708_SOC_RPI_DAC=m - CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m - CONFIG_SND_SIMPLE_CARD=m diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index aa62e55..5f6a1fa 100644 +index 5f7df9e..66bc839 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -674,6 +674,20 @@ static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { +@@ -678,6 +678,20 @@ static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { #endif @@ -116968,21 +117429,20 @@ index aa62e55..5f6a1fa 100644 #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) static struct platform_device snd_rpi_dac_device = { .name = "snd-rpi-dac", -@@ -869,6 +883,12 @@ void __init bcm2708_init(void) - i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); +@@ -876,6 +890,11 @@ void __init bcm2708_init(void) + i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); #endif +#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+ bcm_register_device(&snd_hifiberry_amp_device); -+ i2c_register_board_info(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); ++ bcm_register_device_dt(&snd_hifiberry_amp_device); ++ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); +#endif -+ + #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) - bcm_register_device(&snd_rpi_dac_device); - bcm_register_device(&snd_pcm1794a_codec_device); + bcm_register_device_dt(&snd_rpi_dac_device); + bcm_register_device_dt(&snd_pcm1794a_codec_device); diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index 926a82b..a562ddf 100644 +index 7675501..40d27c1 100644 --- a/sound/soc/bcm/Kconfig +++ b/sound/soc/bcm/Kconfig @@ -40,6 +40,13 @@ config SND_BCM2708_SOC_HIFIBERRY_DIGI @@ -117019,7 +117479,7 @@ index c02e3a2..17ea2b0 100644 obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o diff --git a/sound/soc/bcm/hifiberry_amp.c b/sound/soc/bcm/hifiberry_amp.c new file mode 100644 -index 0000000..1e87ee0 +index 0000000..1e87ee06 --- /dev/null +++ b/sound/soc/bcm/hifiberry_amp.c @@ -0,0 +1,106 @@ @@ -117130,10 +117590,10 @@ index 0000000..1e87ee0 +MODULE_DESCRIPTION("ASoC driver for HiFiBerry-AMP"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig -index 0a7aaf7..45dd4fd 100644 +index 8682bcf..f49e15d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig -@@ -107,6 +107,7 @@ config SND_SOC_ALL_CODECS +@@ -108,6 +108,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER @@ -117141,7 +117601,7 @@ index 0a7aaf7..45dd4fd 100644 select SND_SOC_TLV320AIC26 if SPI_MASTER select SND_SOC_TLV320AIC31XX if I2C select SND_SOC_TLV320AIC32X4 if I2C -@@ -612,6 +613,9 @@ config SND_SOC_TFA9879 +@@ -618,6 +619,9 @@ config SND_SOC_TFA9879 tristate "NXP Semiconductors TFA9879 amplifier" depends on I2C @@ -117152,10 +117612,10 @@ index 0a7aaf7..45dd4fd 100644 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile -index 1548bf33..756f628 100644 +index c9bceba..078943d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile -@@ -107,6 +107,7 @@ snd-soc-sta529-objs := sta529.o +@@ -108,6 +108,7 @@ snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o snd-soc-tfa9879-objs := tfa9879.o @@ -117163,7 +117623,7 @@ index 1548bf33..756f628 100644 snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o -@@ -287,6 +288,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o +@@ -289,6 +290,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o @@ -117756,1537 +118216,10 @@ index 0000000..8f019e0 + +#endif /* _TAS5713_H */ -From 0d81e702b6c48cf3f39d4a13588d273868554490 Mon Sep 17 00:00:00 2001 +From 2deb059167387273e5e3e83e463db54dbd460cad Mon Sep 17 00:00:00 2001 From: Phil Elwell -Date: Mon, 13 Oct 2014 11:47:53 +0100 -Subject: [PATCH 063/172] Improve __copy_to_user and __copy_from_user - performance - -Provide a __copy_from_user that uses memcpy. On BCM2708, use -optimised memcpy/memmove/memcmp/memset implementations. ---- - arch/arm/include/asm/string.h | 5 + - arch/arm/include/asm/uaccess.h | 1 + - arch/arm/lib/Makefile | 15 +- - arch/arm/lib/arm-mem.h | 159 ++++++++++++ - arch/arm/lib/copy_from_user.S | 4 +- - arch/arm/lib/exports_rpi.c | 37 +++ - arch/arm/lib/memcmp_rpi.S | 285 +++++++++++++++++++++ - arch/arm/lib/memcpy_rpi.S | 59 +++++ - arch/arm/lib/memcpymove.h | 506 +++++++++++++++++++++++++++++++++++++ - arch/arm/lib/memmove_rpi.S | 61 +++++ - arch/arm/lib/memset_rpi.S | 121 +++++++++ - arch/arm/lib/uaccess_with_memcpy.c | 112 +++++++- - 12 files changed, 1359 insertions(+), 6 deletions(-) - create mode 100644 arch/arm/lib/arm-mem.h - create mode 100644 arch/arm/lib/exports_rpi.c - create mode 100644 arch/arm/lib/memcmp_rpi.S - create mode 100644 arch/arm/lib/memcpy_rpi.S - create mode 100644 arch/arm/lib/memcpymove.h - create mode 100644 arch/arm/lib/memmove_rpi.S - create mode 100644 arch/arm/lib/memset_rpi.S - -diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h -index cf4f3aa..9fe7780 100644 ---- a/arch/arm/include/asm/string.h -+++ b/arch/arm/include/asm/string.h -@@ -24,6 +24,11 @@ extern void * memchr(const void *, int, __kernel_size_t); - #define __HAVE_ARCH_MEMSET - extern void * memset(void *, int, __kernel_size_t); - -+#ifdef CONFIG_MACH_BCM2708 -+#define __HAVE_ARCH_MEMCMP -+extern int memcmp(const void *, const void *, size_t); -+#endif -+ - extern void __memzero(void *ptr, __kernel_size_t n); - - #define memset(p,v,n) \ -diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h -index 4767eb9..bcd43de 100644 ---- a/arch/arm/include/asm/uaccess.h -+++ b/arch/arm/include/asm/uaccess.h -@@ -475,6 +475,7 @@ do { \ - - #ifdef CONFIG_MMU - extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n); -+extern unsigned long __must_check __copy_from_user_std(void *to, const void __user *from, unsigned long n); - extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n); - extern unsigned long __must_check __copy_to_user_std(void __user *to, const void *from, unsigned long n); - extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n); -diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile -index 0573faa..498f5dd 100644 ---- a/arch/arm/lib/Makefile -+++ b/arch/arm/lib/Makefile -@@ -6,15 +6,24 @@ - - lib-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ - csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ -- delay.o delay-loop.o findbit.o memchr.o memcpy.o \ -- memmove.o memset.o memzero.o setbit.o \ -- strchr.o strrchr.o \ -+ delay.o delay-loop.o findbit.o memchr.o memzero.o \ -+ setbit.o strchr.o strrchr.o \ - testchangebit.o testclearbit.o testsetbit.o \ - ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ - ucmpdi2.o lib1funcs.o div64.o \ - io-readsb.o io-writesb.o io-readsl.o io-writesl.o \ - call_with_stack.o bswapsdi2.o - -+# Choose optimised implementations for Raspberry Pi -+ifeq ($(CONFIG_MACH_BCM2708),y) -+ CFLAGS_uaccess_with_memcpy.o += -DCOPY_FROM_USER_THRESHOLD=1600 -+ CFLAGS_uaccess_with_memcpy.o += -DCOPY_TO_USER_THRESHOLD=672 -+ obj-$(CONFIG_MODULES) += exports_rpi.o -+ lib-y += memcpy_rpi.o memmove_rpi.o memset_rpi.o memcmp_rpi.o -+else -+ lib-y += memcpy.o memmove.o memset.o -+endif -+ - mmu-y := clear_user.o copy_page.o getuser.o putuser.o - - # the code in uaccess.S is not preemption safe and -diff --git a/arch/arm/lib/arm-mem.h b/arch/arm/lib/arm-mem.h -new file mode 100644 -index 0000000..5d4bda1 ---- /dev/null -+++ b/arch/arm/lib/arm-mem.h -@@ -0,0 +1,159 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+.macro myfunc fname -+ .func fname -+ .global fname -+fname: -+.endm -+ -+.macro preload_leading_step1 backwards, ptr, base -+/* If the destination is already 16-byte aligned, then we need to preload -+ * between 0 and prefetch_distance (inclusive) cache lines ahead so there -+ * are no gaps when the inner loop starts. -+ */ -+ .if backwards -+ sub ptr, base, #1 -+ bic ptr, ptr, #31 -+ .else -+ bic ptr, base, #31 -+ .endif -+ .set OFFSET, 0 -+ .rept prefetch_distance+1 -+ pld [ptr, #OFFSET] -+ .if backwards -+ .set OFFSET, OFFSET-32 -+ .else -+ .set OFFSET, OFFSET+32 -+ .endif -+ .endr -+.endm -+ -+.macro preload_leading_step2 backwards, ptr, base, leading_bytes, tmp -+/* However, if the destination is not 16-byte aligned, we may need to -+ * preload one more cache line than that. The question we need to ask is: -+ * are the leading bytes more than the amount by which the source -+ * pointer will be rounded down for preloading, and if so, by how many -+ * cache lines? -+ */ -+ .if backwards -+/* Here we compare against how many bytes we are into the -+ * cache line, counting down from the highest such address. -+ * Effectively, we want to calculate -+ * leading_bytes = dst&15 -+ * cacheline_offset = 31-((src-leading_bytes-1)&31) -+ * extra_needed = leading_bytes - cacheline_offset -+ * and test if extra_needed is <= 0, or rearranging: -+ * leading_bytes + (src-leading_bytes-1)&31 <= 31 -+ */ -+ mov tmp, base, lsl #32-5 -+ sbc tmp, tmp, leading_bytes, lsl #32-5 -+ adds tmp, tmp, leading_bytes, lsl #32-5 -+ bcc 61f -+ pld [ptr, #-32*(prefetch_distance+1)] -+ .else -+/* Effectively, we want to calculate -+ * leading_bytes = (-dst)&15 -+ * cacheline_offset = (src+leading_bytes)&31 -+ * extra_needed = leading_bytes - cacheline_offset -+ * and test if extra_needed is <= 0. -+ */ -+ mov tmp, base, lsl #32-5 -+ add tmp, tmp, leading_bytes, lsl #32-5 -+ rsbs tmp, tmp, leading_bytes, lsl #32-5 -+ bls 61f -+ pld [ptr, #32*(prefetch_distance+1)] -+ .endif -+61: -+.endm -+ -+.macro preload_trailing backwards, base, remain, tmp -+ /* We need either 0, 1 or 2 extra preloads */ -+ .if backwards -+ rsb tmp, base, #0 -+ mov tmp, tmp, lsl #32-5 -+ .else -+ mov tmp, base, lsl #32-5 -+ .endif -+ adds tmp, tmp, remain, lsl #32-5 -+ adceqs tmp, tmp, #0 -+ /* The instruction above has two effects: ensures Z is only -+ * set if C was clear (so Z indicates that both shifted quantities -+ * were 0), and clears C if Z was set (so C indicates that the sum -+ * of the shifted quantities was greater and not equal to 32) */ -+ beq 82f -+ .if backwards -+ sub tmp, base, #1 -+ bic tmp, tmp, #31 -+ .else -+ bic tmp, base, #31 -+ .endif -+ bcc 81f -+ .if backwards -+ pld [tmp, #-32*(prefetch_distance+1)] -+81: -+ pld [tmp, #-32*prefetch_distance] -+ .else -+ pld [tmp, #32*(prefetch_distance+2)] -+81: -+ pld [tmp, #32*(prefetch_distance+1)] -+ .endif -+82: -+.endm -+ -+.macro preload_all backwards, narrow_case, shift, base, remain, tmp0, tmp1 -+ .if backwards -+ sub tmp0, base, #1 -+ bic tmp0, tmp0, #31 -+ pld [tmp0] -+ sub tmp1, base, remain, lsl #shift -+ .else -+ bic tmp0, base, #31 -+ pld [tmp0] -+ add tmp1, base, remain, lsl #shift -+ sub tmp1, tmp1, #1 -+ .endif -+ bic tmp1, tmp1, #31 -+ cmp tmp1, tmp0 -+ beq 92f -+ .if narrow_case -+ /* In this case, all the data fits in either 1 or 2 cache lines */ -+ pld [tmp1] -+ .else -+91: -+ .if backwards -+ sub tmp0, tmp0, #32 -+ .else -+ add tmp0, tmp0, #32 -+ .endif -+ cmp tmp0, tmp1 -+ pld [tmp0] -+ bne 91b -+ .endif -+92: -+.endm -diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S -index 7a235b9..8aff879 100644 ---- a/arch/arm/lib/copy_from_user.S -+++ b/arch/arm/lib/copy_from_user.S -@@ -89,11 +89,13 @@ - - .text - --ENTRY(__copy_from_user) -+ENTRY(__copy_from_user_std) -+WEAK(__copy_from_user) - - #include "copy_template.S" - - ENDPROC(__copy_from_user) -+ENDPROC(__copy_from_user_std) - - .pushsection .fixup,"ax" - .align 0 -diff --git a/arch/arm/lib/exports_rpi.c b/arch/arm/lib/exports_rpi.c -new file mode 100644 -index 0000000..1f82604 ---- /dev/null -+++ b/arch/arm/lib/exports_rpi.c -@@ -0,0 +1,37 @@ -+/** -+ * Copyright (c) 2014, Raspberry Pi (Trading) Ltd. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") version 2, as published by the Free -+ * Software Foundation. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include -+#include -+ -+EXPORT_SYMBOL(memcmp); -diff --git a/arch/arm/lib/memcmp_rpi.S b/arch/arm/lib/memcmp_rpi.S -new file mode 100644 -index 0000000..bf6e4ed ---- /dev/null -+++ b/arch/arm/lib/memcmp_rpi.S -@@ -0,0 +1,285 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+.macro memcmp_process_head unaligned -+ .if unaligned -+ ldr DAT0, [S_1], #4 -+ ldr DAT1, [S_1], #4 -+ ldr DAT2, [S_1], #4 -+ ldr DAT3, [S_1], #4 -+ .else -+ ldmia S_1!, {DAT0, DAT1, DAT2, DAT3} -+ .endif -+ ldmia S_2!, {DAT4, DAT5, DAT6, DAT7} -+.endm -+ -+.macro memcmp_process_tail -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ cmpeq DAT3, DAT7 -+ bne 200f -+.endm -+ -+.macro memcmp_leading_31bytes -+ movs DAT0, OFF, lsl #31 -+ ldrmib DAT0, [S_1], #1 -+ ldrcsh DAT1, [S_1], #2 -+ ldrmib DAT4, [S_2], #1 -+ ldrcsh DAT5, [S_2], #2 -+ movpl DAT0, #0 -+ movcc DAT1, #0 -+ movpl DAT4, #0 -+ movcc DAT5, #0 -+ submi N, N, #1 -+ subcs N, N, #2 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ bne 200f -+ movs DAT0, OFF, lsl #29 -+ ldrmi DAT0, [S_1], #4 -+ ldrcs DAT1, [S_1], #4 -+ ldrcs DAT2, [S_1], #4 -+ ldrmi DAT4, [S_2], #4 -+ ldmcsia S_2!, {DAT5, DAT6} -+ movpl DAT0, #0 -+ movcc DAT1, #0 -+ movcc DAT2, #0 -+ movpl DAT4, #0 -+ movcc DAT5, #0 -+ movcc DAT6, #0 -+ submi N, N, #4 -+ subcs N, N, #8 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ bne 200f -+ tst OFF, #16 -+ beq 105f -+ memcmp_process_head 1 -+ sub N, N, #16 -+ memcmp_process_tail -+105: -+.endm -+ -+.macro memcmp_trailing_15bytes unaligned -+ movs N, N, lsl #29 -+ .if unaligned -+ ldrcs DAT0, [S_1], #4 -+ ldrcs DAT1, [S_1], #4 -+ .else -+ ldmcsia S_1!, {DAT0, DAT1} -+ .endif -+ ldrmi DAT2, [S_1], #4 -+ ldmcsia S_2!, {DAT4, DAT5} -+ ldrmi DAT6, [S_2], #4 -+ movcc DAT0, #0 -+ movcc DAT1, #0 -+ movpl DAT2, #0 -+ movcc DAT4, #0 -+ movcc DAT5, #0 -+ movpl DAT6, #0 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ cmpeq DAT2, DAT6 -+ bne 200f -+ movs N, N, lsl #2 -+ ldrcsh DAT0, [S_1], #2 -+ ldrmib DAT1, [S_1] -+ ldrcsh DAT4, [S_2], #2 -+ ldrmib DAT5, [S_2] -+ movcc DAT0, #0 -+ movpl DAT1, #0 -+ movcc DAT4, #0 -+ movpl DAT5, #0 -+ cmp DAT0, DAT4 -+ cmpeq DAT1, DAT5 -+ bne 200f -+.endm -+ -+.macro memcmp_long_inner_loop unaligned -+110: -+ memcmp_process_head unaligned -+ pld [S_2, #prefetch_distance*32 + 16] -+ memcmp_process_tail -+ memcmp_process_head unaligned -+ pld [S_1, OFF] -+ memcmp_process_tail -+ subs N, N, #32 -+ bhs 110b -+ /* Just before the final (prefetch_distance+1) 32-byte blocks, -+ * deal with final preloads */ -+ preload_trailing 0, S_1, N, DAT0 -+ preload_trailing 0, S_2, N, DAT0 -+ add N, N, #(prefetch_distance+2)*32 - 16 -+120: -+ memcmp_process_head unaligned -+ memcmp_process_tail -+ subs N, N, #16 -+ bhs 120b -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcmp_trailing_15bytes unaligned -+199: /* Reached end without detecting a difference */ -+ mov a1, #0 -+ setend le -+ pop {DAT1-DAT6, pc} -+.endm -+ -+.macro memcmp_short_inner_loop unaligned -+ subs N, N, #16 /* simplifies inner loop termination */ -+ blo 122f -+120: -+ memcmp_process_head unaligned -+ memcmp_process_tail -+ subs N, N, #16 -+ bhs 120b -+122: /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcmp_trailing_15bytes unaligned -+199: /* Reached end without detecting a difference */ -+ mov a1, #0 -+ setend le -+ pop {DAT1-DAT6, pc} -+.endm -+ -+/* -+ * int memcmp(const void *s1, const void *s2, size_t n); -+ * On entry: -+ * a1 = pointer to buffer 1 -+ * a2 = pointer to buffer 2 -+ * a3 = number of bytes to compare (as unsigned chars) -+ * On exit: -+ * a1 = >0/=0/<0 if s1 >/=/< s2 -+ */ -+ -+.set prefetch_distance, 2 -+ -+ENTRY(memcmp) -+ S_1 .req a1 -+ S_2 .req a2 -+ N .req a3 -+ DAT0 .req a4 -+ DAT1 .req v1 -+ DAT2 .req v2 -+ DAT3 .req v3 -+ DAT4 .req v4 -+ DAT5 .req v5 -+ DAT6 .req v6 -+ DAT7 .req ip -+ OFF .req lr -+ -+ push {DAT1-DAT6, lr} -+ setend be /* lowest-addressed bytes are most significant */ -+ -+ /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */ -+ cmp N, #(prefetch_distance+3)*32 - 1 -+ blo 170f -+ -+ /* Long case */ -+ /* Adjust N so that the decrement instruction can also test for -+ * inner loop termination. We want it to stop when there are -+ * (prefetch_distance+1) complete blocks to go. */ -+ sub N, N, #(prefetch_distance+2)*32 -+ preload_leading_step1 0, DAT0, S_1 -+ preload_leading_step1 0, DAT1, S_2 -+ tst S_2, #31 -+ beq 154f -+ rsb OFF, S_2, #0 /* no need to AND with 15 here */ -+ preload_leading_step2 0, DAT0, S_1, OFF, DAT2 -+ preload_leading_step2 0, DAT1, S_2, OFF, DAT2 -+ memcmp_leading_31bytes -+154: /* Second source now cacheline (32-byte) aligned; we have at -+ * least one prefetch to go. */ -+ /* Prefetch offset is best selected such that it lies in the -+ * first 8 of each 32 bytes - but it's just as easy to aim for -+ * the first one */ -+ and OFF, S_1, #31 -+ rsb OFF, OFF, #32*prefetch_distance -+ tst S_1, #3 -+ bne 140f -+ memcmp_long_inner_loop 0 -+140: memcmp_long_inner_loop 1 -+ -+170: /* Short case */ -+ teq N, #0 -+ beq 199f -+ preload_all 0, 0, 0, S_1, N, DAT0, DAT1 -+ preload_all 0, 0, 0, S_2, N, DAT0, DAT1 -+ tst S_2, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199f -+ ldrb DAT0, [S_1], #1 -+ ldrb DAT4, [S_2], #1 -+ cmp DAT0, DAT4 -+ bne 200f -+ tst S_2, #3 -+ bne 172b -+174: /* Second source now 4-byte aligned; we have 0 or more bytes to go */ -+ tst S_1, #3 -+ bne 140f -+ memcmp_short_inner_loop 0 -+140: memcmp_short_inner_loop 1 -+ -+200: /* Difference found: determine sign. */ -+ movhi a1, #1 -+ movlo a1, #-1 -+ setend le -+ pop {DAT1-DAT6, pc} -+ -+ .unreq S_1 -+ .unreq S_2 -+ .unreq N -+ .unreq DAT0 -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ .unreq DAT4 -+ .unreq DAT5 -+ .unreq DAT6 -+ .unreq DAT7 -+ .unreq OFF -+ENDPROC(memcmp) -diff --git a/arch/arm/lib/memcpy_rpi.S b/arch/arm/lib/memcpy_rpi.S -new file mode 100644 -index 0000000..15ff2bd ---- /dev/null -+++ b/arch/arm/lib/memcpy_rpi.S -@@ -0,0 +1,59 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+#include "memcpymove.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memcpy(void * restrict s1, const void * restrict s2, size_t n); -+ * On entry: -+ * a1 = pointer to destination -+ * a2 = pointer to source -+ * a3 = number of bytes to copy -+ * On exit: -+ * a1 preserved -+ */ -+ -+.set prefetch_distance, 3 -+ -+ENTRY(memcpy) -+ memcpy 0 -+ENDPROC(memcpy) -diff --git a/arch/arm/lib/memcpymove.h b/arch/arm/lib/memcpymove.h -new file mode 100644 -index 0000000..d8be584 ---- /dev/null -+++ b/arch/arm/lib/memcpymove.h -@@ -0,0 +1,506 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+.macro unaligned_words backwards, align, use_pld, words, r0, r1, r2, r3, r4, r5, r6, r7, r8 -+ .if words == 1 -+ .if backwards -+ mov r1, r0, lsl #32-align*8 -+ ldr r0, [S, #-4]! -+ orr r1, r1, r0, lsr #align*8 -+ str r1, [D, #-4]! -+ .else -+ mov r0, r1, lsr #align*8 -+ ldr r1, [S, #4]! -+ orr r0, r0, r1, lsl #32-align*8 -+ str r0, [D], #4 -+ .endif -+ .elseif words == 2 -+ .if backwards -+ ldr r1, [S, #-4]! -+ mov r2, r0, lsl #32-align*8 -+ ldr r0, [S, #-4]! -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r1, r2} -+ .else -+ ldr r1, [S, #4]! -+ mov r0, r2, lsr #align*8 -+ ldr r2, [S, #4]! -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ stmia D!, {r0, r1} -+ .endif -+ .elseif words == 4 -+ .if backwards -+ ldmdb S!, {r2, r3} -+ mov r4, r0, lsl #32-align*8 -+ ldmdb S!, {r0, r1} -+ orr r4, r4, r3, lsr #align*8 -+ mov r3, r3, lsl #32-align*8 -+ orr r3, r3, r2, lsr #align*8 -+ mov r2, r2, lsl #32-align*8 -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r1, r2, r3, r4} -+ .else -+ ldmib S!, {r1, r2} -+ mov r0, r4, lsr #align*8 -+ ldmib S!, {r3, r4} -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ mov r2, r2, lsr #align*8 -+ orr r2, r2, r3, lsl #32-align*8 -+ mov r3, r3, lsr #align*8 -+ orr r3, r3, r4, lsl #32-align*8 -+ stmia D!, {r0, r1, r2, r3} -+ .endif -+ .elseif words == 8 -+ .if backwards -+ ldmdb S!, {r4, r5, r6, r7} -+ mov r8, r0, lsl #32-align*8 -+ ldmdb S!, {r0, r1, r2, r3} -+ .if use_pld -+ pld [S, OFF] -+ .endif -+ orr r8, r8, r7, lsr #align*8 -+ mov r7, r7, lsl #32-align*8 -+ orr r7, r7, r6, lsr #align*8 -+ mov r6, r6, lsl #32-align*8 -+ orr r6, r6, r5, lsr #align*8 -+ mov r5, r5, lsl #32-align*8 -+ orr r5, r5, r4, lsr #align*8 -+ mov r4, r4, lsl #32-align*8 -+ orr r4, r4, r3, lsr #align*8 -+ mov r3, r3, lsl #32-align*8 -+ orr r3, r3, r2, lsr #align*8 -+ mov r2, r2, lsl #32-align*8 -+ orr r2, r2, r1, lsr #align*8 -+ mov r1, r1, lsl #32-align*8 -+ orr r1, r1, r0, lsr #align*8 -+ stmdb D!, {r5, r6, r7, r8} -+ stmdb D!, {r1, r2, r3, r4} -+ .else -+ ldmib S!, {r1, r2, r3, r4} -+ mov r0, r8, lsr #align*8 -+ ldmib S!, {r5, r6, r7, r8} -+ .if use_pld -+ pld [S, OFF] -+ .endif -+ orr r0, r0, r1, lsl #32-align*8 -+ mov r1, r1, lsr #align*8 -+ orr r1, r1, r2, lsl #32-align*8 -+ mov r2, r2, lsr #align*8 -+ orr r2, r2, r3, lsl #32-align*8 -+ mov r3, r3, lsr #align*8 -+ orr r3, r3, r4, lsl #32-align*8 -+ mov r4, r4, lsr #align*8 -+ orr r4, r4, r5, lsl #32-align*8 -+ mov r5, r5, lsr #align*8 -+ orr r5, r5, r6, lsl #32-align*8 -+ mov r6, r6, lsr #align*8 -+ orr r6, r6, r7, lsl #32-align*8 -+ mov r7, r7, lsr #align*8 -+ orr r7, r7, r8, lsl #32-align*8 -+ stmia D!, {r0, r1, r2, r3} -+ stmia D!, {r4, r5, r6, r7} -+ .endif -+ .endif -+.endm -+ -+.macro memcpy_leading_15bytes backwards, align -+ movs DAT1, DAT2, lsl #31 -+ sub N, N, DAT2 -+ .if backwards -+ ldrmib DAT0, [S, #-1]! -+ ldrcsh DAT1, [S, #-2]! -+ strmib DAT0, [D, #-1]! -+ strcsh DAT1, [D, #-2]! -+ .else -+ ldrmib DAT0, [S], #1 -+ ldrcsh DAT1, [S], #2 -+ strmib DAT0, [D], #1 -+ strcsh DAT1, [D], #2 -+ .endif -+ movs DAT1, DAT2, lsl #29 -+ .if backwards -+ ldrmi DAT0, [S, #-4]! -+ .if align == 0 -+ ldmcsdb S!, {DAT1, DAT2} -+ .else -+ ldrcs DAT2, [S, #-4]! -+ ldrcs DAT1, [S, #-4]! -+ .endif -+ strmi DAT0, [D, #-4]! -+ stmcsdb D!, {DAT1, DAT2} -+ .else -+ ldrmi DAT0, [S], #4 -+ .if align == 0 -+ ldmcsia S!, {DAT1, DAT2} -+ .else -+ ldrcs DAT1, [S], #4 -+ ldrcs DAT2, [S], #4 -+ .endif -+ strmi DAT0, [D], #4 -+ stmcsia D!, {DAT1, DAT2} -+ .endif -+.endm -+ -+.macro memcpy_trailing_15bytes backwards, align -+ movs N, N, lsl #29 -+ .if backwards -+ .if align == 0 -+ ldmcsdb S!, {DAT0, DAT1} -+ .else -+ ldrcs DAT1, [S, #-4]! -+ ldrcs DAT0, [S, #-4]! -+ .endif -+ ldrmi DAT2, [S, #-4]! -+ stmcsdb D!, {DAT0, DAT1} -+ strmi DAT2, [D, #-4]! -+ .else -+ .if align == 0 -+ ldmcsia S!, {DAT0, DAT1} -+ .else -+ ldrcs DAT0, [S], #4 -+ ldrcs DAT1, [S], #4 -+ .endif -+ ldrmi DAT2, [S], #4 -+ stmcsia D!, {DAT0, DAT1} -+ strmi DAT2, [D], #4 -+ .endif -+ movs N, N, lsl #2 -+ .if backwards -+ ldrcsh DAT0, [S, #-2]! -+ ldrmib DAT1, [S, #-1] -+ strcsh DAT0, [D, #-2]! -+ strmib DAT1, [D, #-1] -+ .else -+ ldrcsh DAT0, [S], #2 -+ ldrmib DAT1, [S] -+ strcsh DAT0, [D], #2 -+ strmib DAT1, [D] -+ .endif -+.endm -+ -+.macro memcpy_long_inner_loop backwards, align -+ .if align != 0 -+ .if backwards -+ ldr DAT0, [S, #-align]! -+ .else -+ ldr LAST, [S, #-align]! -+ .endif -+ .endif -+110: -+ .if align == 0 -+ .if backwards -+ ldmdb S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ pld [S, OFF] -+ stmdb D!, {DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT0, DAT1, DAT2, DAT3} -+ .else -+ ldmia S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ pld [S, OFF] -+ stmia D!, {DAT0, DAT1, DAT2, DAT3} -+ stmia D!, {DAT4, DAT5, DAT6, LAST} -+ .endif -+ .else -+ unaligned_words backwards, align, 1, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST -+ .endif -+ subs N, N, #32 -+ bhs 110b -+ /* Just before the final (prefetch_distance+1) 32-byte blocks, deal with final preloads */ -+ preload_trailing backwards, S, N, OFF -+ add N, N, #(prefetch_distance+2)*32 - 32 -+120: -+ .if align == 0 -+ .if backwards -+ ldmdb S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT4, DAT5, DAT6, LAST} -+ stmdb D!, {DAT0, DAT1, DAT2, DAT3} -+ .else -+ ldmia S!, {DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, LAST} -+ stmia D!, {DAT0, DAT1, DAT2, DAT3} -+ stmia D!, {DAT4, DAT5, DAT6, LAST} -+ .endif -+ .else -+ unaligned_words backwards, align, 0, 8, DAT0, DAT1, DAT2, DAT3, DAT4, DAT5, DAT6, DAT7, LAST -+ .endif -+ subs N, N, #32 -+ bhs 120b -+ tst N, #16 -+ .if align == 0 -+ .if backwards -+ ldmnedb S!, {DAT0, DAT1, DAT2, LAST} -+ stmnedb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldmneia S!, {DAT0, DAT1, DAT2, LAST} -+ stmneia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ .else -+ beq 130f -+ unaligned_words backwards, align, 0, 4, DAT0, DAT1, DAT2, DAT3, LAST -+130: -+ .endif -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ .if align != 0 -+ add S, S, #align -+ .endif -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {DAT3, DAT4, DAT5, DAT6, DAT7} -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy_medium_inner_loop backwards, align -+120: -+ .if backwards -+ .if align == 0 -+ ldmdb S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldr LAST, [S, #-4]! -+ ldr DAT2, [S, #-4]! -+ ldr DAT1, [S, #-4]! -+ ldr DAT0, [S, #-4]! -+ .endif -+ stmdb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ .if align == 0 -+ ldmia S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldr DAT0, [S], #4 -+ ldr DAT1, [S], #4 -+ ldr DAT2, [S], #4 -+ ldr LAST, [S], #4 -+ .endif -+ stmia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ subs N, N, #16 -+ bhs 120b -+ /* Trailing words and bytes */ -+ tst N, #15 -+ beq 199f -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy_short_inner_loop backwards, align -+ tst N, #16 -+ .if backwards -+ .if align == 0 -+ ldmnedb S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldrne LAST, [S, #-4]! -+ ldrne DAT2, [S, #-4]! -+ ldrne DAT1, [S, #-4]! -+ ldrne DAT0, [S, #-4]! -+ .endif -+ stmnedb D!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ .if align == 0 -+ ldmneia S!, {DAT0, DAT1, DAT2, LAST} -+ .else -+ ldrne DAT0, [S], #4 -+ ldrne DAT1, [S], #4 -+ ldrne DAT2, [S], #4 -+ ldrne LAST, [S], #4 -+ .endif -+ stmneia D!, {DAT0, DAT1, DAT2, LAST} -+ .endif -+ memcpy_trailing_15bytes backwards, align -+199: -+ pop {D, DAT1, DAT2, pc} -+.endm -+ -+.macro memcpy backwards -+ D .req a1 -+ S .req a2 -+ N .req a3 -+ DAT0 .req a4 -+ DAT1 .req v1 -+ DAT2 .req v2 -+ DAT3 .req v3 -+ DAT4 .req v4 -+ DAT5 .req v5 -+ DAT6 .req v6 -+ DAT7 .req sl -+ LAST .req ip -+ OFF .req lr -+ -+ .cfi_startproc -+ -+ push {D, DAT1, DAT2, lr} -+ -+ .cfi_def_cfa_offset 16 -+ .cfi_rel_offset D, 0 -+ .cfi_undefined S -+ .cfi_undefined N -+ .cfi_undefined DAT0 -+ .cfi_rel_offset DAT1, 4 -+ .cfi_rel_offset DAT2, 8 -+ .cfi_undefined LAST -+ .cfi_rel_offset lr, 12 -+ -+ .if backwards -+ add D, D, N -+ add S, S, N -+ .endif -+ -+ /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */ -+ cmp N, #31 -+ blo 170f -+ /* To preload ahead as we go, we need at least (prefetch_distance+2) 32-byte blocks */ -+ cmp N, #(prefetch_distance+3)*32 - 1 -+ blo 160f -+ -+ /* Long case */ -+ push {DAT3, DAT4, DAT5, DAT6, DAT7} -+ -+ .cfi_def_cfa_offset 36 -+ .cfi_rel_offset D, 20 -+ .cfi_rel_offset DAT1, 24 -+ .cfi_rel_offset DAT2, 28 -+ .cfi_rel_offset DAT3, 0 -+ .cfi_rel_offset DAT4, 4 -+ .cfi_rel_offset DAT5, 8 -+ .cfi_rel_offset DAT6, 12 -+ .cfi_rel_offset DAT7, 16 -+ .cfi_rel_offset lr, 32 -+ -+ /* Adjust N so that the decrement instruction can also test for -+ * inner loop termination. We want it to stop when there are -+ * (prefetch_distance+1) complete blocks to go. */ -+ sub N, N, #(prefetch_distance+2)*32 -+ preload_leading_step1 backwards, DAT0, S -+ .if backwards -+ /* Bug in GAS: it accepts, but mis-assembles the instruction -+ * ands DAT2, D, #60, 2 -+ * which sets DAT2 to the number of leading bytes until destination is aligned and also clears C (sets borrow) -+ */ -+ .word 0xE210513C -+ beq 154f -+ .else -+ ands DAT2, D, #15 -+ beq 154f -+ rsb DAT2, DAT2, #16 /* number of leading bytes until destination aligned */ -+ .endif -+ preload_leading_step2 backwards, DAT0, S, DAT2, OFF -+ memcpy_leading_15bytes backwards, 1 -+154: /* Destination now 16-byte aligned; we have at least one prefetch as well as at least one 16-byte output block */ -+ /* Prefetch offset is best selected such that it lies in the first 8 of each 32 bytes - but it's just as easy to aim for the first one */ -+ .if backwards -+ rsb OFF, S, #3 -+ and OFF, OFF, #28 -+ sub OFF, OFF, #32*(prefetch_distance+1) -+ .else -+ and OFF, S, #28 -+ rsb OFF, OFF, #32*prefetch_distance -+ .endif -+ movs DAT0, S, lsl #31 -+ bhi 157f -+ bcs 156f -+ bmi 155f -+ memcpy_long_inner_loop backwards, 0 -+155: memcpy_long_inner_loop backwards, 1 -+156: memcpy_long_inner_loop backwards, 2 -+157: memcpy_long_inner_loop backwards, 3 -+ -+ .cfi_def_cfa_offset 16 -+ .cfi_rel_offset D, 0 -+ .cfi_rel_offset DAT1, 4 -+ .cfi_rel_offset DAT2, 8 -+ .cfi_same_value DAT3 -+ .cfi_same_value DAT4 -+ .cfi_same_value DAT5 -+ .cfi_same_value DAT6 -+ .cfi_same_value DAT7 -+ .cfi_rel_offset lr, 12 -+ -+160: /* Medium case */ -+ preload_all backwards, 0, 0, S, N, DAT2, OFF -+ sub N, N, #16 /* simplifies inner loop termination */ -+ .if backwards -+ ands DAT2, D, #15 -+ beq 164f -+ .else -+ ands DAT2, D, #15 -+ beq 164f -+ rsb DAT2, DAT2, #16 -+ .endif -+ memcpy_leading_15bytes backwards, align -+164: /* Destination now 16-byte aligned; we have at least one 16-byte output block */ -+ tst S, #3 -+ bne 140f -+ memcpy_medium_inner_loop backwards, 0 -+140: memcpy_medium_inner_loop backwards, 1 -+ -+170: /* Short case, less than 31 bytes, so no guarantee of at least one 16-byte block */ -+ teq N, #0 -+ beq 199f -+ preload_all backwards, 1, 0, S, N, DAT2, LAST -+ tst D, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199f -+ .if backwards -+ ldrb DAT0, [S, #-1]! -+ strb DAT0, [D, #-1]! -+ .else -+ ldrb DAT0, [S], #1 -+ strb DAT0, [D], #1 -+ .endif -+ tst D, #3 -+ bne 172b -+174: /* Destination now 4-byte aligned; we have 0 or more output bytes to go */ -+ tst S, #3 -+ bne 140f -+ memcpy_short_inner_loop backwards, 0 -+140: memcpy_short_inner_loop backwards, 1 -+ -+ .cfi_endproc -+ -+ .unreq D -+ .unreq S -+ .unreq N -+ .unreq DAT0 -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ .unreq DAT4 -+ .unreq DAT5 -+ .unreq DAT6 -+ .unreq DAT7 -+ .unreq LAST -+ .unreq OFF -+.endm -diff --git a/arch/arm/lib/memmove_rpi.S b/arch/arm/lib/memmove_rpi.S -new file mode 100644 -index 0000000..8b0760c ---- /dev/null -+++ b/arch/arm/lib/memmove_rpi.S -@@ -0,0 +1,61 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+#include "memcpymove.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memmove(void *s1, const void *s2, size_t n); -+ * On entry: -+ * a1 = pointer to destination -+ * a2 = pointer to source -+ * a3 = number of bytes to copy -+ * On exit: -+ * a1 preserved -+ */ -+ -+.set prefetch_distance, 3 -+ -+ENTRY(memmove) -+ cmp a2, a1 -+ bpl memcpy /* pl works even over -1 - 0 and 0x7fffffff - 0x80000000 boundaries */ -+ memcpy 1 -+ENDPROC(memmove) -diff --git a/arch/arm/lib/memset_rpi.S b/arch/arm/lib/memset_rpi.S -new file mode 100644 -index 0000000..2cde883 ---- /dev/null -+++ b/arch/arm/lib/memset_rpi.S -@@ -0,0 +1,121 @@ -+/* -+Copyright (c) 2013, Raspberry Pi Foundation -+Copyright (c) 2013, RISC OS Open Ltd -+All rights reserved. -+ -+Redistribution and use in source and binary forms, with or without -+modification, are permitted provided that the following conditions are met: -+ * Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ * Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ * Neither the name of the copyright holder nor the -+ names of its contributors may be used to endorse or promote products -+ derived from this software without specific prior written permission. -+ -+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+*/ -+ -+#include -+#include "arm-mem.h" -+ -+/* Prevent the stack from becoming executable */ -+#if defined(__linux__) && defined(__ELF__) -+.section .note.GNU-stack,"",%progbits -+#endif -+ -+ .text -+ .arch armv6 -+ .object_arch armv4 -+ .arm -+ .altmacro -+ .p2align 2 -+ -+/* -+ * void *memset(void *s, int c, size_t n); -+ * On entry: -+ * a1 = pointer to buffer to fill -+ * a2 = byte pattern to fill with (caller-narrowed) -+ * a3 = number of bytes to fill -+ * On exit: -+ * a1 preserved -+ */ -+ENTRY(memset) -+ S .req a1 -+ DAT0 .req a2 -+ N .req a3 -+ DAT1 .req a4 -+ DAT2 .req ip -+ DAT3 .req lr -+ -+ orr DAT0, DAT0, lsl #8 -+ push {S, lr} -+ orr DAT0, DAT0, lsl #16 -+ mov DAT1, DAT0 -+ -+ /* See if we're guaranteed to have at least one 16-byte aligned 16-byte write */ -+ cmp N, #31 -+ blo 170f -+ -+161: sub N, N, #16 /* simplifies inner loop termination */ -+ /* Leading words and bytes */ -+ tst S, #15 -+ beq 164f -+ rsb DAT3, S, #0 /* bits 0-3 = number of leading bytes until aligned */ -+ movs DAT2, DAT3, lsl #31 -+ submi N, N, #1 -+ strmib DAT0, [S], #1 -+ subcs N, N, #2 -+ strcsh DAT0, [S], #2 -+ movs DAT2, DAT3, lsl #29 -+ submi N, N, #4 -+ strmi DAT0, [S], #4 -+ subcs N, N, #8 -+ stmcsia S!, {DAT0, DAT1} -+164: /* Delayed set up of DAT2 and DAT3 so we could use them as scratch registers above */ -+ mov DAT2, DAT0 -+ mov DAT3, DAT0 -+ /* Now the inner loop of 16-byte stores */ -+165: stmia S!, {DAT0, DAT1, DAT2, DAT3} -+ subs N, N, #16 -+ bhs 165b -+166: /* Trailing words and bytes */ -+ movs N, N, lsl #29 -+ stmcsia S!, {DAT0, DAT1} -+ strmi DAT0, [S], #4 -+ movs N, N, lsl #2 -+ strcsh DAT0, [S], #2 -+ strmib DAT0, [S] -+199: pop {S, pc} -+ -+170: /* Short case */ -+ mov DAT2, DAT0 -+ mov DAT3, DAT0 -+ tst S, #3 -+ beq 174f -+172: subs N, N, #1 -+ blo 199b -+ strb DAT0, [S], #1 -+ tst S, #3 -+ bne 172b -+174: tst N, #16 -+ stmneia S!, {DAT0, DAT1, DAT2, DAT3} -+ b 166b -+ -+ .unreq S -+ .unreq DAT0 -+ .unreq N -+ .unreq DAT1 -+ .unreq DAT2 -+ .unreq DAT3 -+ENDPROC(memset) -diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c -index 3e58d71..0622891 100644 ---- a/arch/arm/lib/uaccess_with_memcpy.c -+++ b/arch/arm/lib/uaccess_with_memcpy.c -@@ -22,6 +22,14 @@ - #include - #include - -+#ifndef COPY_FROM_USER_THRESHOLD -+#define COPY_FROM_USER_THRESHOLD 64 -+#endif -+ -+#ifndef COPY_TO_USER_THRESHOLD -+#define COPY_TO_USER_THRESHOLD 64 -+#endif -+ - static int - pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) - { -@@ -85,7 +93,44 @@ pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) - return 1; - } - --static unsigned long noinline -+static int -+pin_page_for_read(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp) -+{ -+ unsigned long addr = (unsigned long)_addr; -+ pgd_t *pgd; -+ pmd_t *pmd; -+ pte_t *pte; -+ pud_t *pud; -+ spinlock_t *ptl; -+ -+ pgd = pgd_offset(current->mm, addr); -+ if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd))) -+ { -+ return 0; -+ } -+ pud = pud_offset(pgd, addr); -+ if (unlikely(pud_none(*pud) || pud_bad(*pud))) -+ { -+ return 0; -+ } -+ -+ pmd = pmd_offset(pud, addr); -+ if (unlikely(pmd_none(*pmd) || pmd_bad(*pmd))) -+ return 0; -+ -+ pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl); -+ if (unlikely(!pte_present(*pte) || !pte_young(*pte))) { -+ pte_unmap_unlock(pte, ptl); -+ return 0; -+ } -+ -+ *ptep = pte; -+ *ptlp = ptl; -+ -+ return 1; -+} -+ -+unsigned long noinline - __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) - { - int atomic; -@@ -135,6 +180,54 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) - return n; - } - -+unsigned long noinline -+__copy_from_user_memcpy(void *to, const void __user *from, unsigned long n) -+{ -+ int atomic; -+ -+ if (unlikely(segment_eq(get_fs(), KERNEL_DS))) { -+ memcpy(to, (const void *)from, n); -+ return 0; -+ } -+ -+ /* the mmap semaphore is taken only if not in an atomic context */ -+ atomic = in_atomic(); -+ -+ if (!atomic) -+ down_read(¤t->mm->mmap_sem); -+ while (n) { -+ pte_t *pte; -+ spinlock_t *ptl; -+ int tocopy; -+ -+ while (!pin_page_for_read(from, &pte, &ptl)) { -+ char temp; -+ if (!atomic) -+ up_read(¤t->mm->mmap_sem); -+ if (__get_user(temp, (char __user *)from)) -+ goto out; -+ if (!atomic) -+ down_read(¤t->mm->mmap_sem); -+ } -+ -+ tocopy = (~(unsigned long)from & ~PAGE_MASK) + 1; -+ if (tocopy > n) -+ tocopy = n; -+ -+ memcpy(to, (const void *)from, tocopy); -+ to += tocopy; -+ from += tocopy; -+ n -= tocopy; -+ -+ pte_unmap_unlock(pte, ptl); -+ } -+ if (!atomic) -+ up_read(¤t->mm->mmap_sem); -+ -+out: -+ return n; -+} -+ - unsigned long - __copy_to_user(void __user *to, const void *from, unsigned long n) - { -@@ -145,10 +238,25 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) - * With frame pointer disabled, tail call optimization kicks in - * as well making this test almost invisible. - */ -- if (n < 64) -+ if (n < COPY_TO_USER_THRESHOLD) - return __copy_to_user_std(to, from, n); - return __copy_to_user_memcpy(to, from, n); - } -+ -+unsigned long -+__copy_from_user(void *to, const void __user *from, unsigned long n) -+{ -+ /* -+ * This test is stubbed out of the main function above to keep -+ * the overhead for small copies low by avoiding a large -+ * register dump on the stack just to reload them right away. -+ * With frame pointer disabled, tail call optimization kicks in -+ * as well making this test almost invisible. -+ */ -+ if (n < COPY_FROM_USER_THRESHOLD) -+ return __copy_from_user_std(to, from, n); -+ return __copy_from_user_memcpy(to, from, n); -+} - - static unsigned long noinline - __clear_user_memset(void __user *addr, unsigned long n) - -From 35fe870af907a92f71132723506d92329c3de6fe Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 1 Sep 2014 16:35:56 +0100 -Subject: [PATCH 064/172] bcm2708: Allow option card devices to be configured +Date: Mon, 13 Apr 2015 19:14:18 +0100 +Subject: [PATCH 052/104] bcm2708: Allow option card devices to be configured via DT If the kernel is built with Device Tree support, and if a DT blob @@ -119295,421 +118228,9 @@ for option cards are not created. This avoids both the need to blacklist unwanted devices, and the need to update the board support code with each new device. --- - arch/arm/mach-bcm2708/bcm2708.c | 70 ++++++++++++++++++++--------------------- - drivers/dma/bcm2708-dmaengine.c | 14 ++++----- - drivers/mmc/host/bcm2835-mmc.c | 24 +++++++------- - sound/soc/bcm/bcm2708-i2s.c | 29 +++++++++++------ - sound/soc/bcm/bcm2835-i2s.c | 1 + - 5 files changed, 75 insertions(+), 63 deletions(-) + sound/soc/bcm/bcm2835-i2s.c | 1 + + 1 file changed, 1 insertion(+) -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 5f6a1fa..2c6a29d 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -35,6 +35,7 @@ - #include - #include - #include -+#include - #include - - #include -@@ -93,6 +94,8 @@ static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; - -+static unsigned use_dt = 0; -+ - static void __init bcm2708_init_led(void); - - void __init bcm2708_init_irq(void) -@@ -514,7 +517,6 @@ static struct platform_device bcm2708_alsa_devices[] = { - }, - }; - --#ifndef CONFIG_OF - static struct resource bcm2708_spi_resources[] = { - { - .start = SPI0_BASE, -@@ -538,7 +540,6 @@ static struct platform_device bcm2708_spi_device = { - .dma_mask = &bcm2708_spi_dmamask, - .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, - }; --#endif - - #ifdef CONFIG_BCM2708_SPIDEV - static struct spi_board_info bcm2708_spi_devices[] = { -@@ -560,7 +561,6 @@ static struct spi_board_info bcm2708_spi_devices[] = { - }; - #endif - --#ifndef CONFIG_OF - static struct resource bcm2708_bsc0_resources[] = { - { - .start = BSC0_BASE, -@@ -599,7 +599,6 @@ static struct platform_device bcm2708_bsc1_device = { - .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), - .resource = bcm2708_bsc1_resources, - }; --#endif - - static struct platform_device bcm2835_hwmon_device = { - .name = "bcm2835_hwmon", -@@ -609,7 +608,7 @@ static struct platform_device bcm2835_thermal_device = { - .name = "bcm2835_thermal", - }; - --#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - static struct resource bcm2708_i2s_resources[] = { - { - .start = I2S_BASE, -@@ -731,14 +730,14 @@ int __init bcm_register_device(struct platform_device *pdev) - } - - /* -- * Use this macro for platform devices that are present in the Device Tree. -- * This way the device is only added on non-DT builds. -+ * Use these macros for platform and i2c devices that are present in the -+ * Device Tree. This way the devices are only added on non-DT systems. - */ --#ifdef CONFIG_OF --#define bcm_register_device_dt(pdev) --#else --#define bcm_register_device_dt(pdev) bcm_register_device(pdev) --#endif -+#define bcm_register_device_dt(pdev) \ -+ if (!use_dt) bcm_register_device(pdev) -+ -+#define i2c_register_board_info_dt(busnum, info, n) \ -+ if (!use_dt) i2c_register_board_info(busnum, info, n) - - int calc_rsts(int partition) - { -@@ -814,7 +813,9 @@ static void __init bcm2708_dt_init(void) - ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); - if (ret) { - pr_err("of_platform_populate failed: %d\n", ret); -- BUG(); -+ /* Proceed as if CONFIG_OF was not defined */ -+ } else { -+ use_dt = 1; - } - } - #else -@@ -842,7 +843,7 @@ void __init bcm2708_init(void) - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; - w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -- platform_device_register(&w1_device); -+ bcm_register_device_dt(&w1_device); - #endif - bcm_register_device(&bcm2708_systemtimer_device); - bcm_register_device(&bcm2708_fb_device); -@@ -857,46 +858,45 @@ void __init bcm2708_init(void) - for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) - bcm_register_device(&bcm2708_alsa_devices[i]); - -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ - bcm_register_device_dt(&bcm2708_spi_device); - bcm_register_device_dt(&bcm2708_bsc0_device); - bcm_register_device_dt(&bcm2708_bsc1_device); - -- bcm_register_device(&bcm2835_hwmon_device); -- bcm_register_device(&bcm2835_thermal_device); -- --#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE -- bcm_register_device(&bcm2708_i2s_device); -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+ bcm_register_device_dt(&bcm2708_i2s_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -- bcm_register_device(&snd_hifiberry_dac_device); -- bcm_register_device(&snd_pcm5102a_codec_device); -+ bcm_register_device_dt(&snd_hifiberry_dac_device); -+ bcm_register_device_dt(&snd_pcm5102a_codec_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -- bcm_register_device(&snd_rpi_hifiberry_dacplus_device); -- i2c_register_board_info(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); -+ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -- bcm_register_device(&snd_hifiberry_digi_device); -- i2c_register_board_info(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+ bcm_register_device_dt(&snd_hifiberry_digi_device); -+ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -- bcm_register_device(&snd_hifiberry_amp_device); -- i2c_register_board_info(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); -+ bcm_register_device_dt(&snd_hifiberry_amp_device); -+ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); - #endif - -- - #if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -- bcm_register_device(&snd_rpi_dac_device); -- bcm_register_device(&snd_pcm1794a_codec_device); -+ bcm_register_device_dt(&snd_rpi_dac_device); -+ bcm_register_device_dt(&snd_pcm1794a_codec_device); - #endif - - #if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -- bcm_register_device(&snd_rpi_iqaudio_dac_device); -- i2c_register_board_info(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); - #endif - - -@@ -1041,9 +1041,9 @@ static struct platform_device bcm2708_led_device = { - - static void __init bcm2708_init_led(void) - { -- bcm2708_leds[0].gpio = disk_led_gpio; -- bcm2708_leds[0].active_low = disk_led_active_low; -- platform_device_register(&bcm2708_led_device); -+ bcm2708_leds[0].gpio = disk_led_gpio; -+ bcm2708_leds[0].active_low = disk_led_active_low; -+ bcm_register_device_dt(&bcm2708_led_device); - } - #else - static inline void bcm2708_init_led(void) -diff --git a/drivers/dma/bcm2708-dmaengine.c b/drivers/dma/bcm2708-dmaengine.c -index 10463db..3f9be02 100644 ---- a/drivers/dma/bcm2708-dmaengine.c -+++ b/drivers/dma/bcm2708-dmaengine.c -@@ -42,7 +42,7 @@ - #include - #include - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - /* dma manager */ - #include -@@ -721,7 +721,7 @@ static int bcm2835_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, - } - } - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id, int irq) - { - struct bcm2835_chan *c; -@@ -784,7 +784,7 @@ static const struct of_device_id bcm2835_dma_of_match[] = { - }; - MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match); - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec, - struct of_dma *ofdma) - { -@@ -817,7 +817,7 @@ static int bcm2835_dma_device_slave_caps(struct dma_chan *dchan, - static int bcm2835_dma_probe(struct platform_device *pdev) - { - struct bcm2835_dmadev *od; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - struct resource *res; - void __iomem *base; - uint32_t chans_available; -@@ -830,10 +830,10 @@ static int bcm2835_dma_probe(struct platform_device *pdev) - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; - -- /* If CONFIG_OF is selected, device tree is used */ -+ /* If CONFIG_ARCH_BCM2835 is selected, device tree is used */ - /* hence the difference between probing */ - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (rc) -@@ -987,7 +987,7 @@ static int bcm2835_dma_remove(struct platform_device *pdev) - return 0; - } - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - - - static struct platform_driver bcm2835_dma_driver = { -diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c -index 92febc3..79808cb 100644 ---- a/drivers/mmc/host/bcm2835-mmc.c -+++ b/drivers/mmc/host/bcm2835-mmc.c -@@ -42,7 +42,7 @@ - #include "sdhci.h" - - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - #define BCM2835_CLOCK_FREQ 250000000 - #endif - -@@ -658,7 +658,7 @@ void bcm2835_mmc_send_command(struct bcm2835_host *host, struct mmc_command *cmd - } - - timeout = jiffies; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - if (!cmd->data && cmd->busy_timeout > 9000) - timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; - else -@@ -958,7 +958,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - struct bcm2835_host *host = dev_id; - u32 intmask, mask, unexpected = 0; - int max_loops = 16; --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - int cardint = 0; - #endif - -@@ -989,7 +989,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - mmc_hostname(host->mmc)); - - if (intmask & SDHCI_INT_CARD_INT) { --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - cardint = 1; - #else - bcm2835_mmc_enable_sdio_irq_nolock(host, false); -@@ -1022,7 +1022,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - bcm2835_mmc_dumpregs(host); - } - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - if (cardint) - mmc_signal_sdio_irq(host->mmc); - #endif -@@ -1030,7 +1030,7 @@ static irqreturn_t bcm2835_mmc_irq(int irq, void *dev_id) - return result; - } - --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - static irqreturn_t bcm2835_mmc_thread_irq(int irq, void *dev_id) - { - struct bcm2835_host *host = dev_id; -@@ -1284,7 +1284,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - - /* SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK */ - host->timeout_clk = mmc->f_max / 1000; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - mmc->max_busy_timeout = (1 << 27) / host->timeout_clk; - #endif - /* host controller capabilities */ -@@ -1341,7 +1341,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - init_waitqueue_head(&host->buf_ready_int); - - bcm2835_mmc_init(host, 0); --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - ret = request_irq(host->irq, bcm2835_mmc_irq, 0 /*IRQF_SHARED*/, - mmc_hostname(mmc), host); - #else -@@ -1370,7 +1370,7 @@ int bcm2835_mmc_add_host(struct bcm2835_host *host) - static int bcm2835_mmc_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; --#ifdef CONFIG_OF -+#ifdef CONFIG_ARCH_BCM2835 - struct device_node *node = dev->of_node; - struct clk *clk; - #endif -@@ -1379,7 +1379,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - - int ret; - struct mmc_host *mmc; --#if !defined(CONFIG_OF) && !defined(FORCE_PIO) -+#if !defined(CONFIG_ARCH_BCM2835) && !defined(FORCE_PIO) - dma_cap_mask_t mask; - #endif - -@@ -1404,7 +1404,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - - host->phys_addr = iomem->start + BCM2835_VCMMU_SHIFT; - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - #ifndef FORCE_PIO - dma_cap_zero(mask); - /* we don't care about the channel, any would work */ -@@ -1454,7 +1454,7 @@ static int bcm2835_mmc_probe(struct platform_device *pdev) - } - - --#ifndef CONFIG_OF -+#ifndef CONFIG_ARCH_BCM2835 - mmc->caps |= MMC_CAP_4_BIT_DATA; - #else - mmc_of_parse(mmc); -diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c -index 0b5322a..a3b65dc 100644 ---- a/sound/soc/bcm/bcm2708-i2s.c -+++ b/sound/soc/bcm/bcm2708-i2s.c -@@ -493,15 +493,19 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, - divf = dividend & BCM2708_CLK_DIVF_MASK; - } - -- /* Set clock divider */ -- regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -- | BCM2708_CLK_DIVI(divi) -- | BCM2708_CLK_DIVF(divf)); -- -- /* Setup clock, but don't start it yet */ -- regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -- | BCM2708_CLK_MASH(mash) -- | BCM2708_CLK_SRC(clk_src)); -+ /* Clock should only be set up here if CPU is clock master */ -+ if (((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFS) || -+ ((dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBS_CFM)) { -+ /* Set clock divider */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_DIVI(divi) -+ | BCM2708_CLK_DIVF(divf)); -+ -+ /* Setup clock, but don't start it yet */ -+ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD -+ | BCM2708_CLK_MASH(mash) -+ | BCM2708_CLK_SRC(clk_src)); -+ } - - /* Setup the frame format */ - format = BCM2708_I2S_CHEN; -@@ -981,12 +985,19 @@ static int bcm2708_i2s_remove(struct platform_device *pdev) - return 0; - } - -+static const struct of_device_id bcm2708_i2s_of_match[] = { -+ { .compatible = "brcm,bcm2708-i2s", }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, bcm2708_i2s_of_match); -+ - static struct platform_driver bcm2708_i2s_driver = { - .probe = bcm2708_i2s_probe, - .remove = bcm2708_i2s_remove, - .driver = { - .name = "bcm2708-i2s", - .owner = THIS_MODULE, -+ .of_match_table = bcm2708_i2s_of_match, - }, - }; - diff --git a/sound/soc/bcm/bcm2835-i2s.c b/sound/soc/bcm/bcm2835-i2s.c index 03fa1cb..c816526 100644 --- a/sound/soc/bcm/bcm2835-i2s.c @@ -119723,225 +118244,29 @@ index 03fa1cb..c816526 100644 static struct platform_driver bcm2835_i2s_driver = { .probe = bcm2835_i2s_probe, -From 905b491a759bb3264e073771cbd200e747ef7a02 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 12 Nov 2014 17:07:02 +0000 -Subject: [PATCH 065/172] Adding Device Tree support for some RPi audio cards +From 9823900486e99df17ba7f5e7b782750b7bce286b Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 18:45:39 +0100 +Subject: [PATCH 053/104] Adding Device Tree support for some RPi audio cards --- - arch/arm/boot/dts/Makefile | 4 ++ - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 81 +++++++++++++++++++++++++ - arch/arm/boot/dts/bcm2708-rpi-b.dts | 19 +++++- - arch/arm/boot/dts/bcm2708.dtsi | 18 ++++-- - arch/arm/boot/dts/hifiberry-dac-overlay.dts | 34 +++++++++++ - arch/arm/boot/dts/hifiberry-dacplus-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/hifiberry-digi-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/iqaudio-dac-overlay.dts | 39 ++++++++++++ - arch/arm/boot/dts/iqaudio-dacplus-overlay.dts | 39 ++++++++++++ - sound/soc/bcm/hifiberry_dac.c | 22 +++++++ - sound/soc/bcm/hifiberry_dacplus.c | 22 +++++++ - sound/soc/bcm/hifiberry_digi.c | 22 +++++++ - sound/soc/bcm/iqaudio-dac.c | 16 +++++ - sound/soc/codecs/pcm5102a.c | 7 +++ - 14 files changed, 395 insertions(+), 6 deletions(-) - create mode 100644 arch/arm/boot/dts/bcm2708-rpi-b-plus.dts + arch/arm/boot/dts/hifiberry-dac-overlay.dts | 34 +++++++++++++++++++++ + arch/arm/boot/dts/hifiberry-dacplus-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/hifiberry-digi-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/iqaudio-dac-overlay.dts | 39 +++++++++++++++++++++++++ + arch/arm/boot/dts/iqaudio-dacplus-overlay.dts | 39 +++++++++++++++++++++++++ + sound/soc/bcm/hifiberry_dac.c | 22 ++++++++++++++ + sound/soc/bcm/hifiberry_dacplus.c | 22 ++++++++++++++ + sound/soc/bcm/hifiberry_digi.c | 22 ++++++++++++++ + sound/soc/bcm/iqaudio-dac.c | 16 ++++++++++ + sound/soc/codecs/pcm5102a.c | 7 +++++ + 10 files changed, 279 insertions(+) create mode 100644 arch/arm/boot/dts/hifiberry-dac-overlay.dts create mode 100644 arch/arm/boot/dts/hifiberry-dacplus-overlay.dts create mode 100644 arch/arm/boot/dts/hifiberry-digi-overlay.dts create mode 100644 arch/arm/boot/dts/iqaudio-dac-overlay.dts create mode 100644 arch/arm/boot/dts/iqaudio-dacplus-overlay.dts -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index d177201..76e5f96 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -54,6 +54,7 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb -+dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -@@ -557,6 +558,9 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt6589-aquaris5.dtb \ - mt8127-moose.dtb \ - mt8135-evbp1.dtb - -+targets += dtbs dtbs_install -+targets += $(dtb-y) -+ - endif - - always := $(dtb-y) -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -new file mode 100644 -index 0000000..983c23f ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -0,0 +1,81 @@ -+/dts-v1/; -+ -+/include/ "bcm2708.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2708"; -+ model = "Raspberry Pi Model B+"; -+ -+ aliases { -+ spi0 = &spi0; -+ i2c0 = &i2c0; -+ i2c1 = &i2c1; -+ i2s = &i2s; -+ gpio = &gpio; -+ sound = &sound; -+ }; -+ -+ sound: sound { -+ }; -+}; -+ -+&gpio { -+ spi0_pins: spi0_pins { -+ brcm,pins = <7 8 9 10 11>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+ -+ i2c0_pins: i2c0 { -+ brcm,pins = <0 1>; -+ brcm,function = <4>; -+ }; -+ -+ i2c1_pins: i2c1 { -+ brcm,pins = <2 3>; -+ brcm,function = <4>; -+ }; -+ -+ i2s_pins: i2s { -+ brcm,pins = <18 19 20 21>; -+ brcm,function = <4>; /* alt0 */ -+ }; -+}; -+ -+&spi0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&spi0_pins>; -+ -+ spidev@0{ -+ compatible = "spidev"; -+ reg = <0>; /* CE0 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+ -+ spidev@1{ -+ compatible = "spidev"; -+ reg = <1>; /* CE1 */ -+ #address-cells = <1>; -+ #size-cells = <0>; -+ spi-max-frequency = <500000>; -+ }; -+}; -+ -+&i2c0 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c0_pins>; -+ clock-frequency = <100000>; -+}; -+ -+&i2c1 { -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2c1_pins>; -+ clock-frequency = <100000>; -+}; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 5893122..d8c6d15 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -4,12 +4,18 @@ - - / { - compatible = "brcm,bcm2708"; -- model = "Raspberry Pi"; -+ model = "Raspberry Pi Model B"; - - aliases { - spi0 = &spi0; - i2c0 = &i2c0; - i2c1 = &i2c1; -+ i2s = &i2s; -+ gpio = &gpio; -+ sound = &sound; -+ }; -+ -+ sound: sound { - }; - }; - -@@ -28,6 +34,11 @@ - brcm,pins = <2 3>; - brcm,function = <4>; - }; -+ -+ i2s_pins: i2s { -+ brcm,pins = <28 29 30 31>; -+ brcm,function = <4>; /* alt0 */ -+ }; - }; - - &spi0 { -@@ -62,3 +73,9 @@ - pinctrl-0 = <&i2c1_pins>; - clock-frequency = <100000>; - }; -+ -+&i2s { -+ #sound-dai-cells = <0>; -+ pinctrl-names = "default"; -+ pinctrl-0 = <&i2s_pins>; -+}; -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 2ca6d63..6b36128 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -7,11 +7,8 @@ - interrupt-parent = <&intc>; - - chosen { -- /* -- bootargs must be 1024 characters long because the -- VC bootloader can't expand it -- */ -- bootargs = "console=ttyAMA0 "; -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; - }; - - soc { -@@ -39,6 +36,17 @@ - #interrupt-cells = <2>; - }; - -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2708-i2s"; -+ reg = <0x7e203000 0x20>, -+ <0x7e101098 0x02>; -+ -+ //dmas = <&dma 2>, -+ // <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ - spi0: spi@7e204000 { - compatible = "brcm,bcm2708-spi"; - reg = <0x7e204000 0x1000>; diff --git a/arch/arm/boot/dts/hifiberry-dac-overlay.dts b/arch/arm/boot/dts/hifiberry-dac-overlay.dts new file mode 100644 index 0000000..5e7633a @@ -120251,7 +118576,7 @@ index c63387b..11e4f39 100644 .probe = snd_rpi_hifiberry_dacplus_probe, .remove = snd_rpi_hifiberry_dacplus_remove, diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index e4f769d..76af8a6 100644 +index 446ecdd..74a8c73 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -125,6 +125,21 @@ static int snd_rpi_hifiberry_digi_probe(struct platform_device *pdev) @@ -120353,17 +118678,17 @@ index 126f1e9..7c6598e 100644 }; -From 50224e08199906d8bad2b40db5a2445f18f1851b Mon Sep 17 00:00:00 2001 +From 069d2e987c6261c6d95b0212d23d214c7bada60d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 5 Dec 2014 17:26:26 +0000 -Subject: [PATCH 066/172] fdt: Add support for the CONFIG_CMDLINE_EXTEND option +Subject: [PATCH 054/104] fdt: Add support for the CONFIG_CMDLINE_EXTEND option --- drivers/of/fdt.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c -index 5100742..d6d944c 100644 +index 3a896c9..0320a6f 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -915,19 +915,38 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, @@ -120411,32 +118736,10 @@ index 5100742..d6d944c 100644 pr_debug("Command line is: %s\n", (char*)data); -From e321ee5ea13db9279cc73e4ed85feb572738f520 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 25 Nov 2014 13:39:03 +0000 -Subject: [PATCH 067/172] config: Enable device tree - ---- - arch/arm/configs/bcmrpi_defconfig | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 8381041..c1621fa 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -39,6 +39,7 @@ CONFIG_PARTITION_ADVANCED=y - CONFIG_MAC_PARTITION=y - CONFIG_CFQ_GROUP_IOSCHED=y - CONFIG_ARCH_BCM2708=y -+CONFIG_BCM2708_DT=y - CONFIG_PREEMPT=y - CONFIG_AEABI=y - CONFIG_CLEANCACHE=y - -From 30d815a127ab3fc90b634fc9e78b0a54e8cecd13 Mon Sep 17 00:00:00 2001 +From 0e1b5b9dbf5fcd0f1b3ec8ffd272aa52b19e1c6a Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 16 Dec 2014 10:23:48 +0000 -Subject: [PATCH 068/172] DT: Add overrides to enable i2c0, i2c1, spi and i2s +Subject: [PATCH 055/104] DT: Add overrides to enable i2c0, i2c1, spi and i2s --- arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 10 ++++++++++ @@ -120480,10 +118783,10 @@ index d8c6d15..167b22b 100644 + }; +}; -From 4221b401eb7ca80823af64dfabf78e5663b96077 Mon Sep 17 00:00:00 2001 +From e97c92c33e54e047c1adc0e0db6e8b17ce9576c7 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 18 Dec 2014 16:48:32 +0000 -Subject: [PATCH 069/172] lirc-rpi: Add device tree support, and a suitable +Subject: [PATCH 056/104] lirc-rpi: Add device tree support, and a suitable overlay The overlay supports DT parameters that match the old module @@ -120561,10 +118864,10 @@ index 0000000..7d5d82b + }; +}; diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -index c688364..cd66ca2 100644 +index 8aa452d..24563ec 100644 --- a/drivers/staging/media/lirc/lirc_rpi.c +++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -40,6 +40,7 @@ +@@ -41,6 +41,7 @@ #include #include #include @@ -120572,7 +118875,7 @@ index c688364..cd66ca2 100644 #include -@@ -295,32 +296,117 @@ static int is_right_chip(struct gpio_chip *chip, void *data) +@@ -303,32 +304,117 @@ static int is_right_chip(struct gpio_chip *chip, void *data) return 0; } @@ -120704,7 +119007,7 @@ index c688364..cd66ca2 100644 gpiochip->set(gpiochip, gpio_out_pin, invert); irq_num = gpiochip->to_irq(gpiochip, gpio_in_pin); -@@ -514,15 +600,23 @@ static struct lirc_driver driver = { +@@ -522,15 +608,23 @@ static struct lirc_driver driver = { .owner = THIS_MODULE, }; @@ -120728,7 +119031,7 @@ index c688364..cd66ca2 100644 int result; /* Init read buffer. */ -@@ -537,15 +631,26 @@ static int __init lirc_rpi_init(void) +@@ -545,15 +639,26 @@ static int __init lirc_rpi_init(void) goto exit_buffer_free; } @@ -120762,7 +119065,7 @@ index c688364..cd66ca2 100644 return 0; -@@ -577,13 +682,6 @@ static int __init lirc_rpi_init_module(void) +@@ -585,13 +690,6 @@ static int __init lirc_rpi_init_module(void) if (result) return result; @@ -120777,10 +119080,10 @@ index c688364..cd66ca2 100644 if (result < 0) goto exit_rpi; -From 5dc9913bd2a47c049baf736b93efc014725c0527 Mon Sep 17 00:00:00 2001 +From 9d3dac432c1080306d150765f4860f4cad48887f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 6 Jan 2015 12:06:55 +0000 -Subject: [PATCH 070/172] Fix the activity LED in DT mode +Subject: [PATCH 057/104] Fix the activity LED in DT mode Add a "leds" node to the base DTBs, and a subnode for the activity LED. You can change the LED function like this: @@ -120883,7 +119186,7 @@ index 167b22b..0631f45 100644 }; }; diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 6b36128..1b56cb5 100644 +index 0a916a4..d879316 100644 --- a/arch/arm/boot/dts/bcm2708.dtsi +++ b/arch/arm/boot/dts/bcm2708.dtsi @@ -11,7 +11,7 @@ @@ -120912,10 +119215,10 @@ index 6b36128..1b56cb5 100644 clocks { -From 86473a27065173f994c13a3431b1ebf3310e52e9 Mon Sep 17 00:00:00 2001 +From f3f8cd9a4c0cc9eef090b7fff1fc10f4982f9d5d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 15 Jan 2015 10:39:06 +0000 -Subject: [PATCH 071/172] Adding w1-gpio device tree overlays +Subject: [PATCH 058/104] Adding w1-gpio device tree overlays N.B. Requires firmware supporting multi-target overrides @@ -121025,10 +119328,10 @@ index 0000000..b3e97c2 + }; +}; -From 323b2e846c0d31be3fabcbed62734c0bb5023f80 Mon Sep 17 00:00:00 2001 +From 93f38c0e716cfab415fe3ea215b15fafaeabb80b Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Fri, 23 Jan 2015 16:41:17 +0100 -Subject: [PATCH 072/172] TAS5713: return error if initialisation fails +Subject: [PATCH 059/104] TAS5713: return error if initialisation fails Existing TAS5713 driver logs errors during initialisation, but does not return an error code. Therefore even if initialisation fails, the driver will still be @@ -121095,10 +119398,10 @@ index a24c1da..9b27138 100644 return 0; } -From 62301cba70c8943a53af4664c805573bdd86face Mon Sep 17 00:00:00 2001 +From 59b36868e0cc474049be8e8f687fcaee2d08f972 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 23 Jan 2015 14:48:55 +0000 -Subject: [PATCH 073/172] scripts/dtc: Update to upstream version with overlay +Subject: [PATCH 060/104] scripts/dtc: Update to upstream version with overlay patches --- @@ -126013,7 +124316,7 @@ index b61465f..e229b84 100644 else return get_node_by_label(tree, ref); diff --git a/scripts/dtc/srcpos.c b/scripts/dtc/srcpos.c -index c20bc53..f534c22 100644 +index c20bc531..f534c22 100644 --- a/scripts/dtc/srcpos.c +++ b/scripts/dtc/srcpos.c @@ -34,7 +34,7 @@ struct search_path { @@ -126311,48 +124614,40 @@ index 54d4e904..d644002 100644 -#define DTC_VERSION "DTC 1.4.0-dirty" +#define DTC_VERSION "DTC 1.4.1-g36c70742" -From 919255c79510712b052391025207715033647954 Mon Sep 17 00:00:00 2001 +From 3b2dc82b29b1675dc8d64971fa13fd2d58b8e1b1 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 23 Jan 2015 15:18:03 +0000 -Subject: [PATCH 074/172] BCM2708_DT: Build the overlays as well +Subject: [PATCH 061/104] BCM2708_DT: Build the overlays as well --- - arch/arm/boot/dts/Makefile | 13 +++++++++++++ - 1 file changed, 13 insertions(+) + arch/arm/boot/dts/Makefile | 9 +++++++++ + 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 76e5f96..54ee8c5 100644 +index c86833d..0784306 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -55,6 +55,14 @@ dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ -@@ -565,3 +573,8 @@ endif +@@ -12,6 +12,15 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif - always := $(dtb-y) - clean-files := *.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb + -+# Enable fixups to support overlays on BCM2708 platforms -+ifeq ($(CONFIG_BCM2708_DT),y) -+ DTC_FLAGS ?= -@ -+endif + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb + # Keep at91 dtb files sorted alphabetically for each SoC -From 404893c9ba110f8ea40335f8a62fb49465cf85df Mon Sep 17 00:00:00 2001 +From 6fc5c1aed7033ff0d900ea7a44ef051d9ce1ce9a Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Sun, 25 Jan 2015 19:41:06 +0100 -Subject: [PATCH 075/172] Add device tree overlay for HiFiBerry Amp/Amp+ +Subject: [PATCH 062/104] Add device tree overlay for HiFiBerry Amp/Amp+ This patch add the missing device tree file for the HiFiBerry Amp and Amp+ boards. --- @@ -126362,17 +124657,17 @@ This patch add the missing device tree file for the HiFiBerry Amp and Amp+ board create mode 100644 arch/arm/boot/dts/hifiberry-amp-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 54ee8c5..08a89bf 100644 +index 0784306..8e48eb3 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -58,6 +58,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb +@@ -15,6 +15,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb diff --git a/arch/arm/boot/dts/hifiberry-amp-overlay.dts b/arch/arm/boot/dts/hifiberry-amp-overlay.dts new file mode 100644 index 0000000..2c81448 @@ -126419,10 +124714,10 @@ index 0000000..2c81448 + }; +}; -From c38d9b463556778c95bdaafbae9d2a0617850797 Mon Sep 17 00:00:00 2001 +From 3159d1fd9e49d48303421d65570e3affcbc2268a Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 26 Jan 2015 09:18:24 +0000 -Subject: [PATCH 076/172] Add pps-gpio DT overlay +Subject: [PATCH 063/104] Add pps-gpio DT overlay Parameters: gpiopin= // Default 18 @@ -126433,17 +124728,17 @@ Parameters: create mode 100644 arch/arm/boot/dts/pps-gpio-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 08a89bf..3ae8fe1 100644 +index 8e48eb3..f542d0b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -62,6 +62,7 @@ dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb + diff --git a/arch/arm/boot/dts/pps-gpio-overlay.dts b/arch/arm/boot/dts/pps-gpio-overlay.dts new file mode 100644 index 0000000..40bf0e1 @@ -126485,66 +124780,20 @@ index 0000000..40bf0e1 + }; +}; -From a272e3850258f898431b680c23c771d6cd8a99a5 Mon Sep 17 00:00:00 2001 -From: Serge Schneider -Date: Wed, 3 Sep 2014 14:44:22 +0100 -Subject: [PATCH 077/172] I2C: Only register the I2C device for the current - board revision - ---- - arch/arm/mach-bcm2708/bcm2708.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index 2c6a29d..b36037b 100644 ---- a/arch/arm/mach-bcm2708/bcm2708.c -+++ b/arch/arm/mach-bcm2708/bcm2708.c -@@ -93,6 +93,7 @@ static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; -+static bool vc_i2c_override = false; - - static unsigned use_dt = 0; - -@@ -862,8 +863,15 @@ void __init bcm2708_init(void) - bcm_register_device(&bcm2835_thermal_device); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device_dt(&bcm2708_bsc0_device); -- bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+ if (vc_i2c_override) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ } else { -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } - - #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - bcm_register_device_dt(&bcm2708_i2s_device); -@@ -1093,3 +1101,5 @@ module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); - module_param(w1_gpio_pin, uint, 0644); - module_param(w1_gpio_pullup, uint, 0644); -+module_param(vc_i2c_override, bool, 0644); -+MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); - -From be479e15759f47ee002fa335e7ad5120fb493a54 Mon Sep 17 00:00:00 2001 +From a7eea64b0d9b471933bc65cf42d476456fa3af48 Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Wed, 29 Oct 2014 23:30:30 -0700 -Subject: [PATCH 078/172] Added support to reserve/enable a GPIO pin to be used +Subject: [PATCH 064/104] Added support to reserve/enable a GPIO pin to be used from pps-gpio module (LinuxPPS). Enable PPS modules in default config for RPi. --- arch/arm/mach-bcm2708/bcm2708.c | 27 +++++++++++++++++++++++++++ - 1 file changed, 27 insertions(+) + arch/arm/mach-bcm2709/bcm2709.c | 27 +++++++++++++++++++++++++++ + 2 files changed, 54 insertions(+) diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c -index b36037b..762e17c 100644 +index 66bc839..cc3e56c 100644 --- a/arch/arm/mach-bcm2708/bcm2708.c +++ b/arch/arm/mach-bcm2708/bcm2708.c @@ -37,6 +37,7 @@ @@ -126560,10 +124809,10 @@ index b36037b..762e17c 100644 static unsigned w1_gpio_pin = W1_GPIO; static unsigned w1_gpio_pullup = W1_PULLUP; +static int pps_gpio_pin = -1; - static bool vc_i2c_override = false; static unsigned use_dt = 0; -@@ -274,6 +276,19 @@ static struct platform_device w1_device = { + +@@ -278,6 +280,19 @@ static struct platform_device w1_device = { }; #endif @@ -126583,7 +124832,7 @@ index b36037b..762e17c 100644 static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); static struct platform_device bcm2708_fb_device = { -@@ -841,6 +856,16 @@ void __init bcm2708_init(void) +@@ -846,6 +861,16 @@ void __init bcm2708_init(void) #ifdef CONFIG_BCM2708_GPIO bcm_register_device_dt(&bcm2708_gpio_device); #endif @@ -126600,42 +124849,164 @@ index b36037b..762e17c 100644 #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) w1_gpio_pdata.pin = w1_gpio_pin; w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -@@ -1101,5 +1126,7 @@ module_param(disk_led_active_low, uint, 0644); +@@ -1099,3 +1124,5 @@ module_param(disk_led_active_low, uint, 0644); + module_param(reboot_part, uint, 0644); + module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); ++module_param(pps_gpio_pin, int, 0644); ++MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index 9edb06f..010cb76 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -95,6 +96,7 @@ static unsigned disk_led_active_low = 1; + static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; ++static int pps_gpio_pin = -1; + + static unsigned use_dt = 0; + +@@ -288,6 +290,19 @@ static struct platform_device w1_device = { + }; + #endif + ++static struct pps_gpio_platform_data pps_gpio_info = { ++ .assert_falling_edge = false, ++ .capture_clear = false, ++ .gpio_pin = -1, ++ .gpio_label = "PPS", ++}; ++ ++static struct platform_device pps_gpio_device = { ++ .name = "pps-gpio", ++ .id = PLATFORM_DEVID_NONE, ++ .dev.platform_data = &pps_gpio_info, ++}; ++ + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +@@ -868,6 +883,16 @@ void __init bcm2709_init(void) + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); + #endif ++ ++#if defined(CONFIG_PPS_CLIENT_GPIO) || defined(CONFIG_PPS_CLIENT_GPIO_MODULE) ++ if (!use_dt && (pps_gpio_pin >= 0)) { ++ pr_info("bcm2709: GPIO %d setup as pps-gpio device\n", pps_gpio_pin); ++ pps_gpio_info.gpio_pin = pps_gpio_pin; ++ pps_gpio_device.id = pps_gpio_pin; ++ bcm_register_device(&pps_gpio_device); ++ } ++#endif ++ + #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) + w1_gpio_pdata.pin = w1_gpio_pin; + w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; +@@ -1261,3 +1286,5 @@ module_param(disk_led_active_low, uint, 0644); module_param(reboot_part, uint, 0644); module_param(w1_gpio_pin, uint, 0644); module_param(w1_gpio_pullup, uint, 0644); +module_param(pps_gpio_pin, int, 0644); +MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); - module_param(vc_i2c_override, bool, 0644); - MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); -From f12f6d91c196ce12836bafcab75b0031903dc74c Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 26 Jan 2015 15:26:10 +0000 -Subject: [PATCH 079/172] BCM2708_DT: Correct length of the peripheral space +From 1585897a85631276569eb5f70f5c3957a1ca8123 Mon Sep 17 00:00:00 2001 +From: Serge Schneider +Date: Wed, 3 Sep 2014 14:44:22 +0100 +Subject: [PATCH 065/104] I2C: Only register the I2C device for the current + board revision --- - arch/arm/boot/dts/bcm2708.dtsi | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) + arch/arm/mach-bcm2708/bcm2708.c | 14 ++++++++++++-- + arch/arm/mach-bcm2709/bcm2709.c | 14 ++++++++++++-- + 2 files changed, 24 insertions(+), 4 deletions(-) -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index 1b56cb5..d879316 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -15,7 +15,7 @@ - compatible = "simple-bus"; - #address-cells = <1>; - #size-cells = <1>; -- ranges = <0x7e000000 0x20000000 0x02000000>; -+ ranges = <0x7e000000 0x20000000 0x01000000>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index cc3e56c..c8aea5b 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -95,6 +95,7 @@ static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; + static int pps_gpio_pin = -1; ++static bool vc_i2c_override = false; - intc: interrupt-controller { - compatible = "brcm,bcm2708-armctrl-ic"; + static unsigned use_dt = 0; + +@@ -890,8 +891,15 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_alsa_devices[i]); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device_dt(&bcm2708_bsc0_device); +- bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++ if (vc_i2c_override) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ } else { ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } + + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); +@@ -1126,3 +1134,5 @@ module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); + module_param(pps_gpio_pin, int, 0644); + MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); ++module_param(vc_i2c_override, bool, 0644); ++MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index 010cb76..e3882f3 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -97,6 +97,7 @@ static unsigned reboot_part = 0; + static unsigned w1_gpio_pin = W1_GPIO; + static unsigned w1_gpio_pullup = W1_PULLUP; + static int pps_gpio_pin = -1; ++static bool vc_i2c_override = false; + + static unsigned use_dt = 0; + +@@ -917,8 +918,15 @@ void __init bcm2709_init(void) + bcm_register_device(&bcm2835_thermal_device); + + bcm_register_device_dt(&bcm2708_spi_device); +- bcm_register_device_dt(&bcm2708_bsc0_device); +- bcm_register_device_dt(&bcm2708_bsc1_device); ++ ++ if (vc_i2c_override) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { ++ bcm_register_device_dt(&bcm2708_bsc0_device); ++ } else { ++ bcm_register_device_dt(&bcm2708_bsc1_device); ++ } + + #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) + bcm_register_device_dt(&bcm2708_i2s_device); +@@ -1288,3 +1296,5 @@ module_param(w1_gpio_pin, uint, 0644); + module_param(w1_gpio_pullup, uint, 0644); + module_param(pps_gpio_pin, int, 0644); + MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); ++module_param(vc_i2c_override, bool, 0644); ++MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); -From c9ee485e8bd038ba21ad37da134fe4170d32ab5c Mon Sep 17 00:00:00 2001 +From b93158e13710b07b0cb18b22ab9b0ad98871927f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 28 Jan 2015 16:22:04 +0000 -Subject: [PATCH 080/172] BCM2708_DT: Add pcf8523-rtc overlay +Subject: [PATCH 066/104] BCM2708_DT: Add pcf8523-rtc overlay --- arch/arm/boot/dts/Makefile | 1 + @@ -126644,17 +125015,17 @@ Subject: [PATCH 080/172] BCM2708_DT: Add pcf8523-rtc overlay create mode 100644 arch/arm/boot/dts/pcf8523-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 3ae8fe1..5ecd1005 100644 +index f542d0b..efd1c04 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -62,6 +62,7 @@ dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb diff --git a/arch/arm/boot/dts/pcf8523-rtc-overlay.dts b/arch/arm/boot/dts/pcf8523-rtc-overlay.dts new file mode 100644 index 0000000..0071f62 @@ -126684,10 +125055,10 @@ index 0000000..0071f62 + }; +}; -From 10beb512f97f3d5c1bf4279ee2890316081e9edb Mon Sep 17 00:00:00 2001 +From 039a864d9cb29552c4a61eb8b017d35afad635b3 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Sat, 31 Jan 2015 16:07:56 +0100 -Subject: [PATCH 081/172] Add a parameter to turn off SPDIF output if no audio +Subject: [PATCH 067/104] Add a parameter to turn off SPDIF output if no audio is playing This patch adds the paramater auto_shutdown_output to the kernel module. @@ -126698,7 +125069,7 @@ is set to 1, the SPDIF oputput will shutdown if no stream is playing. 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index 76af8a6..b0e3d28 100644 +index 74a8c73..a245995 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -26,6 +26,11 @@ @@ -126742,7 +125113,7 @@ index 76af8a6..b0e3d28 100644 @@ -70,7 +94,8 @@ static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, break; default: - dev_err(substream->pcm->dev, + dev_err(codec->dev, - "Failed to set WM8804 SYSCLK, unsupported samplerate\n"); + "Failed to set WM8804 SYSCLK, unsupported samplerate %d\n", + samplerate); @@ -126759,17 +125130,17 @@ index 76af8a6..b0e3d28 100644 static struct snd_soc_dai_link snd_rpi_hifiberry_digi_dai[] = { -From 1993de13349743b8edfa8cfc3be4dc4219bd1a33 Mon Sep 17 00:00:00 2001 +From 4f9c0d63e5041763cdce6705208808bee1486382 Mon Sep 17 00:00:00 2001 From: Joerg Hohensohn Date: Sun, 1 Feb 2015 22:08:03 +0100 -Subject: [PATCH 082/172] bugfix for 32kHz sample rate, was missing +Subject: [PATCH 068/104] bugfix for 32kHz sample rate, was missing --- sound/soc/bcm/hifiberry_digi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/soc/bcm/hifiberry_digi.c b/sound/soc/bcm/hifiberry_digi.c -index b0e3d28..133d51b 100644 +index a245995..a294a1b 100644 --- a/sound/soc/bcm/hifiberry_digi.c +++ b/sound/soc/bcm/hifiberry_digi.c @@ -80,6 +80,7 @@ static int snd_rpi_hifiberry_digi_hw_params(struct snd_pcm_substream *substream, @@ -126781,10 +125152,10 @@ index b0e3d28..133d51b 100644 case 48000: case 88200: -From 6085a9df53b0d0cc9898f09717f334a7c3eca4bc Mon Sep 17 00:00:00 2001 +From 4e37e8e0a34280ce4c0ebbf263a768277ab68685 Mon Sep 17 00:00:00 2001 From: Ryan Coe Date: Sat, 31 Jan 2015 18:25:49 -0700 -Subject: [PATCH 083/172] Update ds1307 driver for device-tree support +Subject: [PATCH 069/104] Update ds1307 driver for device-tree support Signed-off-by: Ryan Coe --- @@ -126811,10 +125182,10 @@ index 4ffabb3..c6789a7 100644 .driver = { .name = "rtc-ds1307", -From a4d37a3da2f277ca9e0dc3ad5b1e1aebe16f34b5 Mon Sep 17 00:00:00 2001 +From be4638cf2bd268dd40e6f6f9b9fca1c0f95eff82 Mon Sep 17 00:00:00 2001 From: Ryan Coe Date: Sat, 31 Jan 2015 18:26:03 -0700 -Subject: [PATCH 084/172] Add device-tree overlay for ds1307 +Subject: [PATCH 070/104] Add device-tree overlay for ds1307 Signed-off-by: Ryan Coe --- @@ -126824,17 +125195,17 @@ Signed-off-by: Ryan Coe create mode 100644 arch/arm/boot/dts/ds1307-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 5ecd1005..2685794 100644 +index efd1c04..d45424c 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -64,6 +64,7 @@ dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb -+dtb-$(CONFIG_BCM2708_DT) += ds1307-rtc-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb - dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb +@@ -12,6 +12,7 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif + ++dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb diff --git a/arch/arm/boot/dts/ds1307-rtc-overlay.dts b/arch/arm/boot/dts/ds1307-rtc-overlay.dts new file mode 100644 index 0000000..7d27044 @@ -126864,224 +125235,108 @@ index 0000000..7d27044 + }; +}; -From 2a055c5bad25645d230161b596df0a9702bbcc2b Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 7 May 2013 14:32:27 +0100 -Subject: [PATCH 085/172] Add 2709 platform for Raspberry Pi 2 +From a2a317e2349fba2c9b3b66ea0794836d57a178a9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 6 Feb 2015 13:50:57 +0000 +Subject: [PATCH 071/104] BCM270x_DT: Add pwr_led, and the required "input" + trigger +The "input" trigger makes the associated GPIO an input. This is to support +the Raspberry Pi PWR LED, which is driven by external hardware in normal use. + +N.B. pwr_led is not available on Model A or B boards. --- - arch/arm/Kconfig | 21 + - arch/arm/Makefile | 1 + - arch/arm/boot/dts/Makefile | 10 +- - arch/arm/boot/dts/bcm2708.dtsi | 2 +- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 101 ++ - arch/arm/boot/dts/bcm2709.dtsi | 160 +++ - arch/arm/configs/bcm2709_defconfig | 1156 ++++++++++++++++++++ - arch/arm/configs/bcm2709_sdcard_defconfig | 129 +++ - arch/arm/configs/bcm2709_small_defconfig | 61 ++ - arch/arm/configs/bcm2835_sdcard_defconfig | 176 +++ - arch/arm/configs/bcmrpi_sdcard_defconfig | 176 +++ - arch/arm/configs/bcmrpi_small_defconfig | 103 ++ - arch/arm/kernel/head.S | 8 + - arch/arm/mach-bcm2709/Kconfig | 49 + - arch/arm/mach-bcm2709/Makefile | 7 + - arch/arm/mach-bcm2709/Makefile.boot | 3 + - arch/arm/mach-bcm2709/armctrl.c | 357 +++++++ - arch/arm/mach-bcm2709/armctrl.h | 27 + - arch/arm/mach-bcm2709/bcm2708_gpio.c | 426 ++++++++ - arch/arm/mach-bcm2709/bcm2709.c | 1234 ++++++++++++++++++++++ - arch/arm/mach-bcm2709/bcm2709.h | 49 + - arch/arm/mach-bcm2709/clock.c | 61 ++ - arch/arm/mach-bcm2709/clock.h | 24 + - arch/arm/mach-bcm2709/delay.S | 21 + - arch/arm/mach-bcm2709/dma.c | 409 +++++++ - arch/arm/mach-bcm2709/dmaer.c | 886 ++++++++++++++++ - arch/arm/mach-bcm2709/include/mach/arm_control.h | 493 +++++++++ - arch/arm/mach-bcm2709/include/mach/arm_power.h | 62 ++ - arch/arm/mach-bcm2709/include/mach/barriers.h | 3 + - arch/arm/mach-bcm2709/include/mach/clkdev.h | 7 + - arch/arm/mach-bcm2709/include/mach/debug-macro.S | 22 + - arch/arm/mach-bcm2709/include/mach/dma.h | 94 ++ - arch/arm/mach-bcm2709/include/mach/entry-macro.S | 123 +++ - arch/arm/mach-bcm2709/include/mach/frc.h | 38 + - arch/arm/mach-bcm2709/include/mach/gpio.h | 17 + - arch/arm/mach-bcm2709/include/mach/hardware.h | 28 + - arch/arm/mach-bcm2709/include/mach/io.h | 27 + - arch/arm/mach-bcm2709/include/mach/irqs.h | 225 ++++ - arch/arm/mach-bcm2709/include/mach/memory.h | 57 + - arch/arm/mach-bcm2709/include/mach/platform.h | 225 ++++ - arch/arm/mach-bcm2709/include/mach/power.h | 26 + - arch/arm/mach-bcm2709/include/mach/system.h | 38 + - arch/arm/mach-bcm2709/include/mach/timex.h | 23 + - arch/arm/mach-bcm2709/include/mach/uncompress.h | 84 ++ - arch/arm/mach-bcm2709/include/mach/vc_mem.h | 35 + - arch/arm/mach-bcm2709/include/mach/vc_support.h | 69 ++ - arch/arm/mach-bcm2709/include/mach/vcio.h | 165 +++ - arch/arm/mach-bcm2709/include/mach/vmalloc.h | 20 + - arch/arm/mach-bcm2709/power.c | 195 ++++ - arch/arm/mach-bcm2709/vc_mem.c | 431 ++++++++ - arch/arm/mach-bcm2709/vc_support.c | 318 ++++++ - arch/arm/mach-bcm2709/vcio.c | 474 +++++++++ - arch/arm/mm/proc-v7.S | 1 + - arch/arm/tools/mach-types | 1 + - drivers/char/hw_random/Kconfig | 2 +- - drivers/clocksource/arm_arch_timer.c | 36 + - drivers/dma/Kconfig | 2 +- - drivers/i2c/busses/Kconfig | 4 +- - drivers/media/platform/bcm2835/Kconfig | 2 +- - drivers/misc/vc04_services/Kconfig | 2 +- - drivers/misc/vc04_services/Makefile | 3 - - drivers/mmc/host/Kconfig | 2 +- - drivers/spi/Kconfig | 4 +- - drivers/watchdog/Kconfig | 2 +- - sound/arm/Kconfig | 2 +- - sound/soc/bcm/Kconfig | 2 +- - 66 files changed, 9004 insertions(+), 17 deletions(-) + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 18 ++++- + arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 ++- + arch/arm/boot/dts/bcm2708.dtsi | 7 +- + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 115 +++++++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 5 -- + drivers/leds/trigger/Kconfig | 7 ++ + drivers/leds/trigger/Makefile | 1 + + drivers/leds/trigger/ledtrig-input.c | 65 +++++++++++++++++ + 8 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 arch/arm/boot/dts/bcm2709-rpi-2-b.dts - create mode 100644 arch/arm/boot/dts/bcm2709.dtsi - create mode 100644 arch/arm/configs/bcm2709_defconfig - create mode 100644 arch/arm/configs/bcm2709_sdcard_defconfig - create mode 100644 arch/arm/configs/bcm2709_small_defconfig - create mode 100644 arch/arm/configs/bcm2835_sdcard_defconfig - create mode 100644 arch/arm/configs/bcmrpi_sdcard_defconfig - create mode 100644 arch/arm/configs/bcmrpi_small_defconfig - create mode 100644 arch/arm/mach-bcm2709/Kconfig - create mode 100644 arch/arm/mach-bcm2709/Makefile - create mode 100644 arch/arm/mach-bcm2709/Makefile.boot - create mode 100644 arch/arm/mach-bcm2709/armctrl.c - create mode 100644 arch/arm/mach-bcm2709/armctrl.h - create mode 100644 arch/arm/mach-bcm2709/bcm2708_gpio.c - create mode 100644 arch/arm/mach-bcm2709/bcm2709.c - create mode 100644 arch/arm/mach-bcm2709/bcm2709.h - create mode 100644 arch/arm/mach-bcm2709/clock.c - create mode 100644 arch/arm/mach-bcm2709/clock.h - create mode 100644 arch/arm/mach-bcm2709/delay.S - create mode 100644 arch/arm/mach-bcm2709/dma.c - create mode 100755 arch/arm/mach-bcm2709/dmaer.c - create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_control.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/arm_power.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/barriers.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/clkdev.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/debug-macro.S - create mode 100644 arch/arm/mach-bcm2709/include/mach/dma.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/entry-macro.S - create mode 100644 arch/arm/mach-bcm2709/include/mach/frc.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/gpio.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/hardware.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/io.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/irqs.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/memory.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/platform.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/power.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/system.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/timex.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/uncompress.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vc_mem.h - create mode 100755 arch/arm/mach-bcm2709/include/mach/vc_support.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vcio.h - create mode 100644 arch/arm/mach-bcm2709/include/mach/vmalloc.h - create mode 100644 arch/arm/mach-bcm2709/power.c - create mode 100644 arch/arm/mach-bcm2709/vc_mem.c - create mode 100755 arch/arm/mach-bcm2709/vc_support.c - create mode 100644 arch/arm/mach-bcm2709/vcio.c + create mode 100644 drivers/leds/trigger/ledtrig-input.c -diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 96f98ce..062ef6a 100644 ---- a/arch/arm/Kconfig -+++ b/arch/arm/Kconfig -@@ -791,6 +791,26 @@ config ARCH_OMAP1 - help - Support for older TI OMAP1 (omap7xx, omap15xx or omap16xx) +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +index 95f03ba..b409c2c 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -83,8 +83,18 @@ + pinctrl-0 = <&i2s_pins>; + }; -+config ARCH_BCM2709 -+ bool "Broadcom BCM2709 family" -+ select ARCH_HAS_BARRIERS if SMP -+ select CPU_V7 -+ select HAVE_SMP -+ select ARM_AMBA -+ select MIGHT_HAVE_CACHE_L2X0 -+ select HAVE_SCHED_CLOCK -+ select NEED_MACH_MEMORY_H -+ select NEED_MACH_IO_H -+ select COMMON_CLK -+ select ARCH_HAS_CPUFREQ -+ select GENERIC_CLOCKEVENTS -+ select MACH_BCM2709 -+ select VC4 -+ select FIQ -+# select ZONE_DMA -+ help -+ This enables support for Broadcom BCM2709 boards. +-&act_led { +- gpios = <&gpio 47 0>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; + - endchoice ++ pwr_led: pwr { ++ label = "led1"; ++ linux,default-trigger = "input"; ++ gpios = <&gpio 35 0>; ++ }; + }; - menu "Multiple platform selection" -@@ -980,6 +1000,7 @@ source "arch/arm/mach-vt8500/Kconfig" - - source "arch/arm/mach-w90x900/Kconfig" - source "arch/arm/mach-bcm2708/Kconfig" -+source "arch/arm/mach-bcm2709/Kconfig" - - source "arch/arm/mach-zynq/Kconfig" - -diff --git a/arch/arm/Makefile b/arch/arm/Makefile -index ceb8a4e..2952f09 100644 ---- a/arch/arm/Makefile -+++ b/arch/arm/Makefile -@@ -147,6 +147,7 @@ machine-$(CONFIG_ARCH_AT91) += at91 - machine-$(CONFIG_ARCH_AXXIA) += axxia - machine-$(CONFIG_ARCH_BCM) += bcm - machine-$(CONFIG_ARCH_BCM2708) += bcm2708 -+machine-$(CONFIG_ARCH_BCM2709) += bcm2709 - machine-$(CONFIG_ARCH_BERLIN) += berlin - machine-$(CONFIG_ARCH_CLPS711X) += clps711x - machine-$(CONFIG_ARCH_CNS3XXX) += cns3xxx -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 2685794..83dceff 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -53,6 +53,14 @@ dtb-$(CONFIG_ARCH_AT91) += at91-sama5d4ek.dtb - - dtb-$(CONFIG_ARCH_ATLAS6) += atlas6-evb.dtb - dtb-$(CONFIG_ARCH_AXXIA) += axm5516-amarillo.dtb + / { +@@ -97,5 +107,9 @@ + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; + -+# Raspberry Pi -+ifeq ($(CONFIG_BCM2708_DT),y) -+ RPI_DT_OVERLAYS=y -+endif -+ifeq ($(CONFIG_BCM2709_DT),y) -+ RPI_DT_OVERLAYS=y -+endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb -@@ -579,6 +587,6 @@ always := $(dtb-y) - clean-files := *.dtb ++ pwr_led_gpio = <&pwr_led>,"gpios:4"; ++ pwr_led_activelow = <&pwr_led>,"gpios:8"; ++ pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; + }; + }; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +index 0631f45..1ecd1a1 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -83,8 +83,12 @@ + pinctrl-0 = <&i2s_pins>; + }; - # Enable fixups to support overlays on BCM2708 platforms --ifeq ($(CONFIG_BCM2708_DT),y) -+ifeq ($(RPI_DT_OVERLAYS),y) - DTC_FLAGS ?= -@ - endif +-&act_led { +- gpios = <&gpio 16 1>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 16 1>; ++ }; + }; + + / { diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index d879316..af6b1d9 100644 +index d879316..0713a1ed 100644 --- a/arch/arm/boot/dts/bcm2708.dtsi +++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -81,7 +81,7 @@ - compatible = "gpio-leds"; +@@ -79,11 +79,10 @@ - act_led: act { + leds: leds { + compatible = "gpio-leds"; ++ }; + +- act_led: act { - label = "ACT"; -+ label = "led0"; - linux,default-trigger = "mmc0"; - }; +- linux,default-trigger = "mmc0"; +- }; ++ arm-pmu { ++ compatible = "arm,arm1176-pmu"; }; + }; + diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts new file mode 100644 -index 0000000..0cdff31 +index 0000000..46f4908 --- /dev/null +++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -0,0 +1,101 @@ +@@ -0,0 +1,115 @@ +/dts-v1/; + +/include/ "bcm2709.dtsi" @@ -127167,8 +125422,18 @@ index 0000000..0cdff31 + pinctrl-0 = <&i2s_pins>; +}; + -+&act_led { -+ gpios = <&gpio 47 0>; ++&leds { ++ act_led: act { ++ label = "led0"; ++ linux,default-trigger = "mmc0"; ++ gpios = <&gpio 47 0>; ++ }; ++ ++ pwr_led: pwr { ++ label = "led1"; ++ linux,default-trigger = "input"; ++ gpios = <&gpio 35 0>; ++ }; +}; + +/ { @@ -127181,9478 +125446,14 @@ index 0000000..0cdff31 + act_led_gpio = <&act_led>,"gpios:4"; + act_led_activelow = <&act_led>,"gpios:8"; + act_led_trigger = <&act_led>,"linux,default-trigger"; -+ }; -+}; -diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -new file mode 100644 -index 0000000..fee2053 ---- /dev/null -+++ b/arch/arm/boot/dts/bcm2709.dtsi -@@ -0,0 +1,160 @@ -+/include/ "skeleton.dtsi" -+ -+/ { -+ compatible = "brcm,bcm2709"; -+ model = "BCM2709"; -+ -+ interrupt-parent = <&intc>; -+ -+ chosen { -+ /* No padding required - the boot loader can do that. */ -+ bootargs = ""; -+ }; -+ -+ soc: soc { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ ranges = <0x7e000000 0x3f000000 0x01000000>; -+ -+ intc: interrupt-controller { -+ compatible = "brcm,bcm2708-armctrl-ic"; -+ reg = <0x7e00b200 0x200>; -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ gpio: gpio { -+ compatible = "brcm,bcm2835-gpio"; -+ reg = <0x7e200000 0xb4>; -+ interrupts = <2 17>, <2 18>; -+ -+ gpio-controller; -+ #gpio-cells = <2>; -+ -+ interrupt-controller; -+ #interrupt-cells = <2>; -+ }; -+ -+ i2s: i2s@7e203000 { -+ compatible = "brcm,bcm2708-i2s"; -+ reg = <0x7e203000 0x20>, -+ <0x7e101098 0x02>; -+ -+ //dmas = <&dma 2>, -+ // <&dma 3>; -+ dma-names = "tx", "rx"; -+ status = "disabled"; -+ }; -+ -+ spi0: spi@7e204000 { -+ compatible = "brcm,bcm2708-spi"; -+ reg = <0x7e204000 0x1000>; -+ interrupts = <2 22>; -+ clocks = <&clk_spi>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c0: i2c@7e205000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e205000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ i2c1: i2c@7e804000 { -+ compatible = "brcm,bcm2708-i2c"; -+ reg = <0x7e804000 0x1000>; -+ interrupts = <2 21>; -+ clocks = <&clk_i2c>; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ status = "disabled"; -+ }; -+ -+ leds: leds { -+ compatible = "gpio-leds"; -+ -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ }; -+ }; -+ }; -+ -+ clocks { -+ compatible = "simple-bus"; -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ clk_i2c: i2c { -+ compatible = "fixed-clock"; -+ reg = <1>; -+ #clock-cells = <0>; -+ clock-frequency = <250000000>; -+ }; -+ -+ clk_spi: clock@2 { -+ compatible = "fixed-clock"; -+ reg = <2>; -+ #clock-cells = <0>; -+ clock-output-names = "spi"; -+ clock-frequency = <250000000>; -+ }; -+ }; -+ -+ timer { -+ compatible = "arm,armv7-timer"; -+ clock-frequency = <19200000>; -+ interrupts = <3 0>, // PHYS_SECURE_PPI -+ <3 1>, // PHYS_NONSECURE_PPI -+ <3 3>, // VIRT_PPI -+ <3 2>; // HYP_PPI -+ always-on; -+ }; -+ -+ cpus: cpus { -+ #address-cells = <1>; -+ #size-cells = <0>; -+ -+ v7_cpu0: cpu@0 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf00>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu1: cpu@1 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf01>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu2: cpu@2 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf02>; -+ clock-frequency = <800000000>; -+ }; -+ -+ v7_cpu3: cpu@3 { -+ device_type = "cpu"; -+ compatible = "arm,cortex-a7"; -+ reg = <0xf03>; -+ clock-frequency = <800000000>; -+ }; -+ }; -+ -+ __overrides__ { -+ arm_freq = <&v7_cpu0>, "clock-frequency:0", -+ <&v7_cpu1>, "clock-frequency:0", -+ <&v7_cpu2>, "clock-frequency:0", -+ <&v7_cpu3>, "clock-frequency:0"; -+ }; -+}; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -new file mode 100644 -index 0000000..62f33d6 ---- /dev/null -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -0,0 +1,1156 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0 -+CONFIG_LOCALVERSION="-v7" -+# CONFIG_LOCALVERSION_AUTO is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_EMBEDDED=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_PROFILING=y -+CONFIG_OPROFILE=m -+CONFIG_KPROBES=y -+CONFIG_JUMP_LABEL=y -+CONFIG_MODULES=y -+CONFIG_MODULE_UNLOAD=y -+CONFIG_MODVERSIONS=y -+CONFIG_MODULE_SRCVERSION_ALL=y -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_PARTITION_ADVANCED=y -+CONFIG_MAC_PARTITION=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2709=y -+CONFIG_BCM2709_DT=y -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_VMSPLIT_2G=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_CLEANCACHE=y -+CONFIG_FRONTSWAP=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_KEXEC=y -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_STAT=m -+CONFIG_CPU_FREQ_STAT_DETAILS=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_NEON=y -+CONFIG_KERNEL_MODE_NEON=y -+CONFIG_BINFMT_MISC=m -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_XFRM_USER=y -+CONFIG_NET_KEY=m -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_ADVANCED_ROUTER=y -+CONFIG_IP_MULTIPLE_TABLES=y -+CONFIG_IP_ROUTE_MULTIPATH=y -+CONFIG_IP_ROUTE_VERBOSE=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_NET_IPIP=m -+CONFIG_NET_IPGRE_DEMUX=m -+CONFIG_NET_IPGRE=m -+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_INET_AH=m -+CONFIG_INET_ESP=m -+CONFIG_INET_IPCOMP=m -+CONFIG_INET_XFRM_MODE_TRANSPORT=m -+CONFIG_INET_XFRM_MODE_TUNNEL=m -+CONFIG_INET_XFRM_MODE_BEET=m -+CONFIG_INET_LRO=m -+CONFIG_INET_DIAG=m -+CONFIG_INET6_AH=m -+CONFIG_INET6_ESP=m -+CONFIG_INET6_IPCOMP=m -+CONFIG_IPV6_TUNNEL=m -+CONFIG_IPV6_MULTIPLE_TABLES=y -+CONFIG_IPV6_MROUTE=y -+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y -+CONFIG_IPV6_PIMSM_V2=y -+CONFIG_NETFILTER=y -+CONFIG_NF_CONNTRACK=m -+CONFIG_NF_CONNTRACK_ZONES=y -+CONFIG_NF_CONNTRACK_EVENTS=y -+CONFIG_NF_CONNTRACK_TIMESTAMP=y -+CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m -+CONFIG_NETFILTER_XT_TARGET_AUDIT=m -+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m -+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m -+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -+CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m -+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m -+CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 -+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_ESP=m -+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m -+CONFIG_NETFILTER_XT_MATCH_HELPER=m -+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m -+CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y -+CONFIG_IP_VS_PROTO_UDP=y -+CONFIG_IP_VS_PROTO_ESP=y -+CONFIG_IP_VS_PROTO_AH=y -+CONFIG_IP_VS_PROTO_SCTP=y -+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 -+CONFIG_IP_VS_FTP=m -+CONFIG_IP_VS_PE_SIP=m -+CONFIG_NF_CONNTRACK_IPV4=m -+CONFIG_IP_NF_IPTABLES=m -+CONFIG_IP_NF_MATCH_AH=m -+CONFIG_IP_NF_MATCH_ECN=m -+CONFIG_IP_NF_MATCH_TTL=m -+CONFIG_IP_NF_FILTER=m -+CONFIG_IP_NF_TARGET_REJECT=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 -+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_RT=m -+CONFIG_IP6_NF_TARGET_HL=m -+CONFIG_IP6_NF_FILTER=m -+CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y -+CONFIG_ATM=m -+CONFIG_L2TP=m -+CONFIG_L2TP_V3=y -+CONFIG_L2TP_IP=m -+CONFIG_L2TP_ETH=m -+CONFIG_BRIDGE=m -+CONFIG_VLAN_8021Q=m -+CONFIG_VLAN_8021Q_GVRP=y -+CONFIG_ATALK=m -+CONFIG_NET_SCHED=y -+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 -+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_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_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_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_BATMAN_ADV=m -+CONFIG_OPENVSWITCH=m -+CONFIG_NET_PKTGEN=m -+CONFIG_HAMRADIO=y -+CONFIG_AX25=m -+CONFIG_NETROM=m -+CONFIG_ROSE=m -+CONFIG_MKISS=m -+CONFIG_6PACK=m -+CONFIG_BPQETHER=m -+CONFIG_BAYCOM_SER_FDX=m -+CONFIG_BAYCOM_SER_HDX=m -+CONFIG_YAM=m -+CONFIG_IRDA=m -+CONFIG_IRLAN=m -+CONFIG_IRNET=m -+CONFIG_IRCOMM=m -+CONFIG_IRDA_ULTRA=y -+CONFIG_IRDA_CACHE_LAST_LSAP=y -+CONFIG_IRDA_FAST_RR=y -+CONFIG_IRTTY_SIR=m -+CONFIG_KINGSUN_DONGLE=m -+CONFIG_KSDAZZLE_DONGLE=m -+CONFIG_KS959_DONGLE=m -+CONFIG_USB_IRDA=m -+CONFIG_SIGMATEL_FIR=m -+CONFIG_MCS_FIR=m -+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 -+CONFIG_BT_HCIBTUSB=m -+CONFIG_BT_HCIBCM203X=m -+CONFIG_BT_HCIBPA10X=m -+CONFIG_BT_HCIBFUSB=m -+CONFIG_BT_HCIVHCI=m -+CONFIG_BT_MRVL=m -+CONFIG_BT_MRVL_SDIO=m -+CONFIG_BT_ATH3K=m -+CONFIG_BT_WILINK=m -+CONFIG_CFG80211_WEXT=y -+CONFIG_MAC80211=m -+CONFIG_MAC80211_MESH=y -+CONFIG_WIMAX=m -+CONFIG_RFKILL=m -+CONFIG_RFKILL_INPUT=y -+CONFIG_NET_9P=m -+CONFIG_NFC=m -+CONFIG_NFC_PN533=m -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=5 -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_CRYPTOLOOP=m -+CONFIG_BLK_DEV_DRBD=m -+CONFIG_BLK_DEV_NBD=m -+CONFIG_BLK_DEV_RAM=y -+CONFIG_CDROM_PKTCDVD=m -+CONFIG_EEPROM_AT24=m -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+CONFIG_BLK_DEV_SD=y -+CONFIG_CHR_DEV_ST=m -+CONFIG_CHR_DEV_OSST=m -+CONFIG_BLK_DEV_SR=m -+CONFIG_CHR_DEV_SG=m -+CONFIG_SCSI_ISCSI_ATTRS=y -+CONFIG_ISCSI_TCP=m -+CONFIG_ISCSI_BOOT_SYSFS=m -+CONFIG_MD=y -+CONFIG_MD_LINEAR=m -+CONFIG_MD_RAID0=m -+CONFIG_BLK_DEV_DM=m -+CONFIG_DM_CRYPT=m -+CONFIG_DM_SNAPSHOT=m -+CONFIG_DM_MIRROR=m -+CONFIG_DM_LOG_USERSPACE=m -+CONFIG_DM_RAID=m -+CONFIG_DM_ZERO=m -+CONFIG_DM_DELAY=m -+CONFIG_NETDEVICES=y -+CONFIG_BONDING=m -+CONFIG_DUMMY=m -+CONFIG_IFB=m -+CONFIG_MACVLAN=m -+CONFIG_NETCONSOLE=m -+CONFIG_TUN=m -+CONFIG_VETH=m -+CONFIG_ENC28J60=m -+CONFIG_MDIO_BITBANG=m -+CONFIG_PPP=m -+CONFIG_PPP_BSDCOMP=m -+CONFIG_PPP_DEFLATE=m -+CONFIG_PPP_FILTER=y -+CONFIG_PPP_MPPE=m -+CONFIG_PPP_MULTILINK=y -+CONFIG_PPPOATM=m -+CONFIG_PPPOE=m -+CONFIG_PPPOL2TP=m -+CONFIG_PPP_ASYNC=m -+CONFIG_PPP_SYNC_TTY=m -+CONFIG_SLIP=m -+CONFIG_SLIP_COMPRESSED=y -+CONFIG_SLIP_SMART=y -+CONFIG_USB_CATC=m -+CONFIG_USB_KAWETH=m -+CONFIG_USB_PEGASUS=m -+CONFIG_USB_RTL8150=m -+CONFIG_USB_RTL8152=m -+CONFIG_USB_USBNET=y -+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=y -+CONFIG_USB_NET_GL620A=m -+CONFIG_USB_NET_NET1080=m -+CONFIG_USB_NET_PLUSB=m -+CONFIG_USB_NET_MCS7830=m -+CONFIG_USB_NET_CDC_SUBSET=m -+CONFIG_USB_ALI_M5632=y -+CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m -+CONFIG_LIBERTAS_THINFIRM_USB=m -+CONFIG_AT76C50X_USB=m -+CONFIG_USB_ZD1201=m -+CONFIG_USB_NET_RNDIS_WLAN=m -+CONFIG_RTL8187=m -+CONFIG_MAC80211_HWSIM=m -+CONFIG_ATH_CARDS=m -+CONFIG_ATH9K=m -+CONFIG_ATH9K_HTC=m -+CONFIG_CARL9170=m -+CONFIG_ATH6KL=m -+CONFIG_ATH6KL_USB=m -+CONFIG_AR5523=m -+CONFIG_B43=m -+# CONFIG_B43_PHY_N is not set -+CONFIG_B43LEGACY=m -+CONFIG_BRCMFMAC=m -+CONFIG_BRCMFMAC_USB=y -+CONFIG_HOSTAP=m -+CONFIG_LIBERTAS=m -+CONFIG_LIBERTAS_USB=m -+CONFIG_LIBERTAS_SDIO=m -+CONFIG_P54_COMMON=m -+CONFIG_P54_USB=m -+CONFIG_RT2X00=m -+CONFIG_RT2500USB=m -+CONFIG_RT73USB=m -+CONFIG_RT2800USB=m -+CONFIG_RT2800USB_RT3573=y -+CONFIG_RT2800USB_RT53XX=y -+CONFIG_RT2800USB_RT55XX=y -+CONFIG_RT2800USB_UNKNOWN=y -+CONFIG_RTL8192CU=m -+CONFIG_ZD1211RW=m -+CONFIG_MWIFIEX=m -+CONFIG_MWIFIEX_SDIO=m -+CONFIG_WIMAX_I2400M_USB=m -+CONFIG_INPUT_POLLDEV=m -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+CONFIG_INPUT_JOYDEV=m -+CONFIG_INPUT_EVDEV=m -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+CONFIG_INPUT_JOYSTICK=y -+CONFIG_JOYSTICK_IFORCE=m -+CONFIG_JOYSTICK_IFORCE_USB=y -+CONFIG_JOYSTICK_XPAD=m -+CONFIG_JOYSTICK_XPAD_FF=y -+CONFIG_INPUT_TOUCHSCREEN=y -+CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_INPUT_MISC=y -+CONFIG_INPUT_AD714X=m -+CONFIG_INPUT_ATI_REMOTE2=m -+CONFIG_INPUT_KEYSPAN_REMOTE=m -+CONFIG_INPUT_POWERMATE=m -+CONFIG_INPUT_YEALINK=m -+CONFIG_INPUT_CM109=m -+CONFIG_INPUT_UINPUT=m -+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m -+CONFIG_INPUT_ADXL34X=m -+CONFIG_INPUT_CMA3000=m -+CONFIG_SERIO=m -+CONFIG_SERIO_RAW=m -+CONFIG_GAMEPORT=m -+CONFIG_GAMEPORT_NS558=m -+CONFIG_GAMEPORT_L4=m -+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=m -+CONFIG_RAW_DRIVER=y -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_BCM_VC_SM=y -+CONFIG_I2C=y -+CONFIG_I2C_CHARDEV=m -+CONFIG_I2C_MUX=m -+CONFIG_I2C_BCM2708=m -+CONFIG_SPI=y -+CONFIG_SPI_BCM2708=m -+CONFIG_SPI_SPIDEV=y -+CONFIG_PPS=m -+CONFIG_PPS_CLIENT_LDISC=m -+CONFIG_PPS_CLIENT_GPIO=m -+CONFIG_GPIO_SYSFS=y -+CONFIG_GPIO_ARIZONA=m -+CONFIG_W1=m -+CONFIG_W1_MASTER_DS2490=m -+CONFIG_W1_MASTER_DS2482=m -+CONFIG_W1_MASTER_DS1WM=m -+CONFIG_W1_MASTER_GPIO=m -+CONFIG_W1_SLAVE_THERM=m -+CONFIG_W1_SLAVE_SMEM=m -+CONFIG_W1_SLAVE_DS2408=m -+CONFIG_W1_SLAVE_DS2413=m -+CONFIG_W1_SLAVE_DS2406=m -+CONFIG_W1_SLAVE_DS2423=m -+CONFIG_W1_SLAVE_DS2431=m -+CONFIG_W1_SLAVE_DS2433=m -+CONFIG_W1_SLAVE_DS2760=m -+CONFIG_W1_SLAVE_DS2780=m -+CONFIG_W1_SLAVE_DS2781=m -+CONFIG_W1_SLAVE_DS28E04=m -+CONFIG_W1_SLAVE_BQ27000=m -+CONFIG_BATTERY_DS2760=m -+# CONFIG_HWMON is not set -+CONFIG_THERMAL=y -+CONFIG_THERMAL_BCM2835=y -+CONFIG_WATCHDOG=y -+CONFIG_BCM2708_WDT=m -+CONFIG_UCB1400_CORE=m -+CONFIG_MFD_ARIZONA_I2C=m -+CONFIG_MFD_ARIZONA_SPI=m -+CONFIG_MFD_WM5102=y -+CONFIG_MEDIA_SUPPORT=m -+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_LIRC=m -+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_MEDIA_USB_SUPPORT=y -+CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m -+CONFIG_USB_ZR364XX=m -+CONFIG_USB_STKWEBCAM=m -+CONFIG_USB_S2255=m -+CONFIG_VIDEO_USBTV=m -+CONFIG_VIDEO_PVRUSB2=m -+CONFIG_VIDEO_HDPVR=m -+CONFIG_VIDEO_TLG2300=m -+CONFIG_VIDEO_USBVISION=m -+CONFIG_VIDEO_STK1160_COMMON=m -+CONFIG_VIDEO_STK1160_AC97=y -+CONFIG_VIDEO_GO7007=m -+CONFIG_VIDEO_GO7007_USB=m -+CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m -+CONFIG_VIDEO_AU0828=m -+CONFIG_VIDEO_AU0828_RC=y -+CONFIG_VIDEO_CX231XX=m -+CONFIG_VIDEO_CX231XX_ALSA=m -+CONFIG_VIDEO_CX231XX_DVB=m -+CONFIG_VIDEO_TM6000=m -+CONFIG_VIDEO_TM6000_ALSA=m -+CONFIG_VIDEO_TM6000_DVB=m -+CONFIG_DVB_USB=m -+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_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_AS102=m -+CONFIG_VIDEO_EM28XX=m -+CONFIG_VIDEO_EM28XX_V4L2=m -+CONFIG_VIDEO_EM28XX_ALSA=m -+CONFIG_VIDEO_EM28XX_DVB=m -+CONFIG_V4L_PLATFORM_DRIVERS=y -+CONFIG_VIDEO_BCM2835=y -+CONFIG_VIDEO_BCM2835_MMAL=m -+CONFIG_RADIO_SI470X=y -+CONFIG_USB_SI470X=m -+CONFIG_I2C_SI470X=m -+CONFIG_RADIO_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_MA901=m -+CONFIG_RADIO_TEA5764=m -+CONFIG_RADIO_SAA7706H=m -+CONFIG_RADIO_TEF6862=m -+CONFIG_RADIO_WL1273=m -+CONFIG_RADIO_WL128X=m -+# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set -+CONFIG_VIDEO_UDA1342=m -+CONFIG_VIDEO_SONY_BTF_MPX=m -+CONFIG_VIDEO_TVP5150=m -+CONFIG_VIDEO_TW2804=m -+CONFIG_VIDEO_TW9903=m -+CONFIG_VIDEO_TW9906=m -+CONFIG_VIDEO_OV7640=m -+CONFIG_VIDEO_MT9V011=m -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+# CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=m -+CONFIG_SND_SEQUENCER=m -+CONFIG_SND_SEQ_DUMMY=m -+CONFIG_SND_MIXER_OSS=m -+CONFIG_SND_PCM_OSS=m -+CONFIG_SND_SEQUENCER_OSS=y -+CONFIG_SND_HRTIMER=m -+CONFIG_SND_DUMMY=m -+CONFIG_SND_ALOOP=m -+CONFIG_SND_VIRMIDI=m -+CONFIG_SND_MTPAV=m -+CONFIG_SND_SERIAL_U16550=m -+CONFIG_SND_MPU401=m -+CONFIG_SND_BCM2835=m -+CONFIG_SND_USB_AUDIO=m -+CONFIG_SND_USB_UA101=m -+CONFIG_SND_USB_CAIAQ=m -+CONFIG_SND_USB_CAIAQ_INPUT=y -+CONFIG_SND_USB_6FIRE=m -+CONFIG_SND_SOC=m -+CONFIG_SND_BCM2708_SOC_I2S=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m -+CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m -+CONFIG_SND_BCM2708_SOC_RPI_DAC=m -+CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m -+CONFIG_SND_SIMPLE_CARD=m -+CONFIG_SOUND_PRIME=m -+CONFIG_HIDRAW=y -+CONFIG_HID_A4TECH=m -+CONFIG_HID_ACRUX=m -+CONFIG_HID_APPLE=m -+CONFIG_HID_BELKIN=m -+CONFIG_HID_CHERRY=m -+CONFIG_HID_CHICONY=m -+CONFIG_HID_CYPRESS=m -+CONFIG_HID_DRAGONRISE=m -+CONFIG_HID_EMS_FF=m -+CONFIG_HID_ELECOM=m -+CONFIG_HID_ELO=m -+CONFIG_HID_EZKEY=m -+CONFIG_HID_HOLTEK=m -+CONFIG_HID_KEYTOUCH=m -+CONFIG_HID_KYE=m -+CONFIG_HID_UCLOGIC=m -+CONFIG_HID_WALTOP=m -+CONFIG_HID_GYRATION=m -+CONFIG_HID_TWINHAN=m -+CONFIG_HID_KENSINGTON=m -+CONFIG_HID_LCPOWER=m -+CONFIG_HID_LOGITECH=m -+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_HID_PETALYNX=m -+CONFIG_HID_PICOLCD=m -+CONFIG_HID_ROCCAT=m -+CONFIG_HID_SAMSUNG=m -+CONFIG_HID_SONY=m -+CONFIG_HID_SPEEDLINK=m -+CONFIG_HID_SUNPLUS=m -+CONFIG_HID_GREENASIA=m -+CONFIG_HID_SMARTJOYPLUS=m -+CONFIG_HID_TOPSEED=m -+CONFIG_HID_THINGM=m -+CONFIG_HID_THRUSTMASTER=m -+CONFIG_HID_WACOM=m -+CONFIG_HID_WIIMOTE=m -+CONFIG_HID_XINMO=m -+CONFIG_HID_ZEROPLUS=m -+CONFIG_HID_ZYDACRON=m -+CONFIG_HID_PID=y -+CONFIG_USB_HIDDEV=y -+CONFIG_USB=y -+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y -+CONFIG_USB_MON=m -+CONFIG_USB_DWCOTG=y -+CONFIG_USB_PRINTER=m -+CONFIG_USB_STORAGE=y -+CONFIG_USB_STORAGE_REALTEK=m -+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 -+CONFIG_USB_UAS=m -+CONFIG_USB_MDC800=m -+CONFIG_USB_MICROTEK=m -+CONFIG_USBIP_CORE=m -+CONFIG_USBIP_VHCI_HCD=m -+CONFIG_USBIP_HOST=m -+CONFIG_USB_SERIAL=m -+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_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_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_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_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_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 -+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=m -+CONFIG_USB_LD=m -+CONFIG_USB_TRANCEVIBRATOR=m -+CONFIG_USB_IOWARRIOR=m -+CONFIG_USB_TEST=m -+CONFIG_USB_ISIGHTFW=m -+CONFIG_USB_YUREX=m -+CONFIG_USB_ATM=m -+CONFIG_USB_SPEEDTOUCH=m -+CONFIG_USB_CXACRU=m -+CONFIG_USB_UEAGLEATM=m -+CONFIG_USB_XUSBATM=m -+CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y -+CONFIG_MMC_SPI=m -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_GPIO=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=y -+CONFIG_LEDS_TRIGGER_GPIO=y -+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y -+CONFIG_LEDS_TRIGGER_TRANSIENT=m -+CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_RTC_CLASS=y -+# CONFIG_RTC_HCTOSYS is not set -+CONFIG_RTC_DRV_DS1307=m -+CONFIG_RTC_DRV_DS1374=m -+CONFIG_RTC_DRV_DS1672=m -+CONFIG_RTC_DRV_DS3232=m -+CONFIG_RTC_DRV_MAX6900=m -+CONFIG_RTC_DRV_RS5C372=m -+CONFIG_RTC_DRV_ISL1208=m -+CONFIG_RTC_DRV_ISL12022=m -+CONFIG_RTC_DRV_ISL12057=m -+CONFIG_RTC_DRV_X1205=m -+CONFIG_RTC_DRV_PCF2127=m -+CONFIG_RTC_DRV_PCF8523=m -+CONFIG_RTC_DRV_PCF8563=m -+CONFIG_RTC_DRV_PCF8583=m -+CONFIG_RTC_DRV_M41T80=m -+CONFIG_RTC_DRV_BQ32K=m -+CONFIG_RTC_DRV_S35390A=m -+CONFIG_RTC_DRV_FM3130=m -+CONFIG_RTC_DRV_RX8581=m -+CONFIG_RTC_DRV_RX8025=m -+CONFIG_RTC_DRV_EM3027=m -+CONFIG_RTC_DRV_RV3029C2=m -+CONFIG_RTC_DRV_M41T93=m -+CONFIG_RTC_DRV_M41T94=m -+CONFIG_RTC_DRV_DS1305=m -+CONFIG_RTC_DRV_DS1390=m -+CONFIG_RTC_DRV_MAX6902=m -+CONFIG_RTC_DRV_R9701=m -+CONFIG_RTC_DRV_RS5C348=m -+CONFIG_RTC_DRV_DS3234=m -+CONFIG_RTC_DRV_PCF2123=m -+CONFIG_RTC_DRV_RX4581=m -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y -+CONFIG_UIO=m -+CONFIG_UIO_PDRV_GENIRQ=m -+CONFIG_STAGING=y -+CONFIG_PRISM2_USB=m -+CONFIG_R8712U=m -+CONFIG_R8188EU=m -+CONFIG_R8723AU=m -+CONFIG_VT6656=m -+CONFIG_SPEAKUP=m -+CONFIG_SPEAKUP_SYNTH_SOFT=m -+CONFIG_STAGING_MEDIA=y -+CONFIG_LIRC_STAGING=y -+CONFIG_LIRC_IGORPLUGUSB=m -+CONFIG_LIRC_IMON=m -+CONFIG_LIRC_RPI=m -+CONFIG_LIRC_SASEM=m -+CONFIG_LIRC_SERIAL=m -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXTCON=m -+CONFIG_EXTCON_ARIZONA=m -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_REISERFS_FS=m -+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_STATISTICS=y -+CONFIG_XFS_FS=m -+CONFIG_XFS_QUOTA=y -+CONFIG_XFS_POSIX_ACL=y -+CONFIG_XFS_RT=y -+CONFIG_GFS2_FS=m -+CONFIG_OCFS2_FS=m -+CONFIG_BTRFS_FS=m -+CONFIG_BTRFS_FS_POSIX_ACL=y -+CONFIG_NILFS2_FS=m -+CONFIG_FANOTIFY=y -+CONFIG_QFMT_V1=m -+CONFIG_QFMT_V2=m -+CONFIG_AUTOFS4_FS=y -+CONFIG_FUSE_FS=m -+CONFIG_CUSE=m -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_ISO9660_FS=m -+CONFIG_JOLIET=y -+CONFIG_ZISOFS=y -+CONFIG_UDF_FS=m -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_NTFS_FS=m -+CONFIG_NTFS_RW=y -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+CONFIG_ECRYPT_FS=m -+CONFIG_HFS_FS=m -+CONFIG_HFSPLUS_FS=m -+CONFIG_SQUASHFS=m -+CONFIG_SQUASHFS_XATTR=y -+CONFIG_SQUASHFS_LZO=y -+CONFIG_SQUASHFS_XZ=y -+CONFIG_F2FS_FS=y -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_NFS_SWAP=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NFSD=m -+CONFIG_NFSD_V3_ACL=y -+CONFIG_NFSD_V4=y -+CONFIG_CIFS=m -+CONFIG_CIFS_WEAK_PW_HASH=y -+CONFIG_CIFS_XATTR=y -+CONFIG_CIFS_POSIX=y -+CONFIG_9P_FS=m -+CONFIG_9P_FS_POSIX_ACL=y -+CONFIG_NLS_DEFAULT="utf8" -+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=y -+CONFIG_NLS_ISO8859_1=m -+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_DLM=m -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_MEMORY_INIT=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set -+CONFIG_IRQSOFF_TRACER=y -+CONFIG_SCHED_TRACER=y -+CONFIG_STACK_TRACER=y -+CONFIG_BLK_DEV_IO_TRACE=y -+# CONFIG_KPROBE_EVENT is not set -+CONFIG_FUNCTION_PROFILER=y -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+CONFIG_KDB_KEYBOARD=y -+CONFIG_CRYPTO_USER=m -+CONFIG_CRYPTO_NULL=m -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_CTS=m -+CONFIG_CRYPTO_XTS=m -+CONFIG_CRYPTO_XCBC=m -+CONFIG_CRYPTO_SHA1_ARM_NEON=m -+CONFIG_CRYPTO_SHA512_ARM_NEON=m -+CONFIG_CRYPTO_TGR192=m -+CONFIG_CRYPTO_WP512=m -+CONFIG_CRYPTO_AES_ARM_BS=m -+CONFIG_CRYPTO_CAST5=m -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcm2709_sdcard_defconfig b/arch/arm/configs/bcm2709_sdcard_defconfig -new file mode 100644 -index 0000000..287b15d ---- /dev/null -+++ b/arch/arm/configs/bcm2709_sdcard_defconfig -@@ -0,0 +1,129 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_PHYS_OFFSET=0x0 -+CONFIG_LOCALVERSION="-sdcard" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_FHANDLE=y -+CONFIG_AUDIT=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_BSD_PROCESS_ACCT=y -+CONFIG_BSD_PROCESS_ACCT_V3=y -+CONFIG_TASKSTATS=y -+CONFIG_TASK_DELAY_ACCT=y -+CONFIG_TASK_XACCT=y -+CONFIG_TASK_IO_ACCOUNTING=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_CGROUP_FREEZER=y -+CONFIG_CGROUP_DEVICE=y -+CONFIG_CGROUP_CPUACCT=y -+CONFIG_RESOURCE_COUNTERS=y -+CONFIG_MEMCG=y -+CONFIG_BLK_CGROUP=y -+CONFIG_NAMESPACES=y -+CONFIG_SCHED_AUTOGROUP=y -+CONFIG_RELAY=y -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_EMBEDDED=y -+# CONFIG_PERF_EVENTS is not set -+# CONFIG_COMPAT_BRK is not set -+CONFIG_PROFILING=y -+CONFIG_JUMP_LABEL=y -+CONFIG_BLK_DEV_BSGLIB=y -+CONFIG_BLK_DEV_THROTTLING=y -+CONFIG_CFQ_GROUP_IOSCHED=y -+CONFIG_ARCH_BCM2709=y -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_HOTPLUG_CPU=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_CMA=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_SECCOMP=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+# CONFIG_COREDUMP is not set -+# CONFIG_SUSPEND is not set -+CONFIG_NET=y -+CONFIG_UNIX=y -+CONFIG_CGROUP_NET_CLASSID=y -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_DMA_CMA=y -+CONFIG_CMA_SIZE_MBYTES=8 -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+# CONFIG_HW_RANDOM is not set -+CONFIG_BRCM_CHAR_DRIVERS=y -+CONFIG_BCM_VC_CMA=y -+CONFIG_GPIO_SYSFS=y -+# CONFIG_HWMON is not set -+CONFIG_FB=y -+CONFIG_FB_BCM2708=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+# CONFIG_HID is not set -+# CONFIG_USB_SUPPORT is not set -+CONFIG_MMC=y -+CONFIG_MMC_BLOCK_MINORS=32 -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_BCM2835=y -+CONFIG_MMC_BCM2835_DMA=y -+CONFIG_DMADEVICES=y -+CONFIG_DMA_BCM2708=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_FANOTIFY=y -+CONFIG_FSCACHE=y -+CONFIG_FSCACHE_STATS=y -+CONFIG_FSCACHE_HISTOGRAM=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+# CONFIG_NETWORK_FILESYSTEMS is not set -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_ASCII=y -+CONFIG_PRINTK_TIME=y -+CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DEBUG_FS=y -+CONFIG_MAGIC_SYSRQ=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y -+# CONFIG_DEBUG_PREEMPT is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_KEYS=y -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_AES=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -+CONFIG_AVERAGE=y -diff --git a/arch/arm/configs/bcm2709_small_defconfig b/arch/arm/configs/bcm2709_small_defconfig -new file mode 100644 -index 0000000..9974a9d ---- /dev/null -+++ b/arch/arm/configs/bcm2709_small_defconfig -@@ -0,0 +1,61 @@ -+CONFIG_LOCALVERSION="-small" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_LOG_BUF_SHIFT=16 -+CONFIG_BLK_DEV_INITRD=y -+CONFIG_INITRAMFS_SOURCE="../target_fs" -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+# CONFIG_IOSCHED_CFQ is not set -+CONFIG_ARCH_BCM2709=y -+# CONFIG_BCM2708_GPIO is not set -+# CONFIG_BCM2708_VCMEM is not set -+CONFIG_ARM_THUMBEE=y -+# CONFIG_SWP_EMULATE is not set -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_ARM_ERRATA_720789=y -+CONFIG_SMP=y -+CONFIG_SCHED_MC=y -+CONFIG_SCHED_SMT=y -+CONFIG_HOTPLUG_CPU=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_HIGHMEM=y -+CONFIG_HIGHPTE=y -+# CONFIG_COMPACTION is not set -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="earlyprintk=ttyAMA0,19200 loglevel=9 console=ttyAMA0,19200" -+CONFIG_AUTO_ZRELADDR=y -+CONFIG_BINFMT_MISC=y -+# CONFIG_SUSPEND is not set -+# CONFIG_UEVENT_HELPER is not set -+# CONFIG_FIRMWARE_IN_KERNEL is not set -+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+# CONFIG_HW_RANDOM is not set -+# CONFIG_HWMON is not set -+# CONFIG_HID is not set -+# CONFIG_USB_SUPPORT is not set -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_AUTOFS4_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_PRINTK_TIME=y -+CONFIG_FRAME_WARN=4096 -+CONFIG_MAGIC_SYSRQ=y -+CONFIG_DEBUG_KERNEL=y -+CONFIG_RCU_CPU_STALL_TIMEOUT=60 -+# CONFIG_FTRACE is not set -+# CONFIG_ARM_UNWIND is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_CRC16=y -+CONFIG_CRC_ITU_T=y -diff --git a/arch/arm/configs/bcm2835_sdcard_defconfig b/arch/arm/configs/bcm2835_sdcard_defconfig -new file mode 100644 -index 0000000..987471b ---- /dev/null -+++ b/arch/arm/configs/bcm2835_sdcard_defconfig -@@ -0,0 +1,176 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+# CONFIG_BCM2708_GPIO is not set -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+CONFIG_MII=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_PHYLIB=y -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_RAW_DRIVER=y -+CONFIG_WATCHDOG=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_sdcard_defconfig b/arch/arm/configs/bcmrpi_sdcard_defconfig -new file mode 100644 -index 0000000..fef887a ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_sdcard_defconfig -@@ -0,0 +1,176 @@ -+# CONFIG_ARM_PATCH_PHYS_VIRT is not set -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_POSIX_MQUEUE=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_EMBEDDED=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+CONFIG_SLAB=y -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2708=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_UACCESS_WITH_MEMCPY=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_FREQ=y -+CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y -+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -+CONFIG_CPU_FREQ_GOV_USERSPACE=y -+CONFIG_CPU_FREQ_GOV_ONDEMAND=y -+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y -+CONFIG_CPU_IDLE=y -+CONFIG_VFP=y -+CONFIG_BINFMT_MISC=y -+CONFIG_NET=y -+CONFIG_PACKET=y -+CONFIG_UNIX=y -+CONFIG_INET=y -+CONFIG_IP_MULTICAST=y -+CONFIG_IP_PNP=y -+CONFIG_IP_PNP_DHCP=y -+CONFIG_IP_PNP_RARP=y -+CONFIG_SYN_COOKIES=y -+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -+# CONFIG_INET_XFRM_MODE_TUNNEL is not set -+# CONFIG_INET_XFRM_MODE_BEET is not set -+# CONFIG_INET_LRO is not set -+# CONFIG_INET_DIAG is not set -+# CONFIG_IPV6 is not set -+# CONFIG_WIRELESS is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+CONFIG_SCSI=y -+# CONFIG_SCSI_PROC_FS is not set -+# CONFIG_SCSI_LOWLEVEL is not set -+CONFIG_NETDEVICES=y -+CONFIG_MII=y -+# CONFIG_NET_VENDOR_BROADCOM is not set -+# CONFIG_NET_VENDOR_CIRRUS is not set -+# CONFIG_NET_VENDOR_FARADAY is not set -+# CONFIG_NET_VENDOR_INTEL is not set -+# CONFIG_NET_VENDOR_MARVELL is not set -+# CONFIG_NET_VENDOR_MICREL is not set -+# CONFIG_NET_VENDOR_NATSEMI is not set -+# CONFIG_NET_VENDOR_SEEQ is not set -+# CONFIG_NET_VENDOR_STMICRO is not set -+# CONFIG_NET_VENDOR_WIZNET is not set -+CONFIG_PHYLIB=y -+# CONFIG_WLAN is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+CONFIG_VT_HW_CONSOLE_BINDING=y -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_TTY_PRINTK=y -+CONFIG_RAW_DRIVER=y -+CONFIG_WATCHDOG=y -+CONFIG_REGULATOR=y -+CONFIG_REGULATOR_DEBUG=y -+CONFIG_REGULATOR_FIXED_VOLTAGE=y -+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y -+CONFIG_REGULATOR_USERSPACE_CONSUMER=y -+CONFIG_FB=y -+CONFIG_FRAMEBUFFER_CONSOLE=y -+CONFIG_LOGO=y -+# CONFIG_LOGO_LINUX_MONO is not set -+# CONFIG_LOGO_LINUX_VGA16 is not set -+CONFIG_SOUND=y -+CONFIG_SND=y -+CONFIG_MMC=y -+CONFIG_MMC_SDHCI=y -+CONFIG_MMC_SDHCI_PLTFM=y -+CONFIG_MMC_SDHCI_BCM2708=y -+CONFIG_MMC_SDHCI_BCM2708_DMA=y -+CONFIG_NEW_LEDS=y -+CONFIG_LEDS_CLASS=y -+CONFIG_LEDS_TRIGGERS=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_EXT4_FS=y -+CONFIG_EXT4_FS_POSIX_ACL=y -+CONFIG_EXT4_FS_SECURITY=y -+CONFIG_AUTOFS4_FS=y -+CONFIG_FSCACHE=y -+CONFIG_CACHEFILES=y -+CONFIG_MSDOS_FS=y -+CONFIG_VFAT_FS=y -+CONFIG_FAT_DEFAULT_IOCHARSET="ascii" -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+CONFIG_CONFIGFS_FS=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NFS_FS=y -+CONFIG_NFS_V3_ACL=y -+CONFIG_NFS_V4=y -+CONFIG_ROOT_NFS=y -+CONFIG_NFS_FSCACHE=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_FS=y -+CONFIG_DETECT_HUNG_TASK=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_DEBUG_BUGVERBOSE is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_small_defconfig b/arch/arm/configs/bcmrpi_small_defconfig -new file mode 100644 -index 0000000..3f6e378 ---- /dev/null -+++ b/arch/arm/configs/bcmrpi_small_defconfig -@@ -0,0 +1,103 @@ -+CONFIG_LOCALVERSION="-quick" -+# CONFIG_LOCALVERSION_AUTO is not set -+# CONFIG_SWAP is not set -+CONFIG_SYSVIPC=y -+CONFIG_NO_HZ=y -+CONFIG_HIGH_RES_TIMERS=y -+CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG_PROC=y -+CONFIG_KALLSYMS_ALL=y -+CONFIG_PERF_EVENTS=y -+# CONFIG_COMPAT_BRK is not set -+# CONFIG_BLK_DEV_BSG is not set -+CONFIG_ARCH_BCM2836=y -+# CONFIG_BCM2708_GPIO is not set -+# CONFIG_BCM2708_VCMEM is not set -+# CONFIG_CACHE_L2X0 is not set -+CONFIG_SMP=y -+CONFIG_HAVE_ARM_ARCH_TIMER=y -+CONFIG_PREEMPT=y -+CONFIG_AEABI=y -+CONFIG_ZBOOT_ROM_TEXT=0x0 -+CONFIG_ZBOOT_ROM_BSS=0x0 -+CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" -+CONFIG_CPU_IDLE=y -+# CONFIG_SUSPEND is not set -+CONFIG_DEVTMPFS=y -+CONFIG_DEVTMPFS_MOUNT=y -+CONFIG_BLK_DEV_LOOP=y -+CONFIG_BLK_DEV_RAM=y -+# CONFIG_BCM2708_VCHIQ is not set -+# CONFIG_INPUT_MOUSEDEV is not set -+CONFIG_INPUT_EVDEV=y -+# CONFIG_INPUT_KEYBOARD is not set -+# CONFIG_INPUT_MOUSE is not set -+# CONFIG_SERIO is not set -+# CONFIG_LEGACY_PTYS is not set -+# CONFIG_DEVKMEM is not set -+CONFIG_SERIAL_AMBA_PL011=y -+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -+CONFIG_RAW_DRIVER=y -+# CONFIG_HID is not set -+CONFIG_DMADEVICES=y -+# CONFIG_IOMMU_SUPPORT is not set -+CONFIG_TMPFS=y -+CONFIG_TMPFS_POSIX_ACL=y -+# CONFIG_MISC_FILESYSTEMS is not set -+CONFIG_NLS=y -+CONFIG_NLS_DEFAULT="utf8" -+CONFIG_NLS_CODEPAGE_437=y -+CONFIG_NLS_CODEPAGE_737=y -+CONFIG_NLS_CODEPAGE_775=y -+CONFIG_NLS_CODEPAGE_850=y -+CONFIG_NLS_CODEPAGE_852=y -+CONFIG_NLS_CODEPAGE_855=y -+CONFIG_NLS_CODEPAGE_857=y -+CONFIG_NLS_CODEPAGE_860=y -+CONFIG_NLS_CODEPAGE_861=y -+CONFIG_NLS_CODEPAGE_862=y -+CONFIG_NLS_CODEPAGE_863=y -+CONFIG_NLS_CODEPAGE_864=y -+CONFIG_NLS_CODEPAGE_865=y -+CONFIG_NLS_CODEPAGE_866=y -+CONFIG_NLS_CODEPAGE_869=y -+CONFIG_NLS_CODEPAGE_936=y -+CONFIG_NLS_CODEPAGE_950=y -+CONFIG_NLS_CODEPAGE_932=y -+CONFIG_NLS_CODEPAGE_949=y -+CONFIG_NLS_CODEPAGE_874=y -+CONFIG_NLS_ISO8859_8=y -+CONFIG_NLS_CODEPAGE_1250=y -+CONFIG_NLS_CODEPAGE_1251=y -+CONFIG_NLS_ASCII=y -+CONFIG_NLS_ISO8859_1=y -+CONFIG_NLS_ISO8859_2=y -+CONFIG_NLS_ISO8859_3=y -+CONFIG_NLS_ISO8859_4=y -+CONFIG_NLS_ISO8859_5=y -+CONFIG_NLS_ISO8859_6=y -+CONFIG_NLS_ISO8859_7=y -+CONFIG_NLS_ISO8859_9=y -+CONFIG_NLS_ISO8859_13=y -+CONFIG_NLS_ISO8859_14=y -+CONFIG_NLS_ISO8859_15=y -+CONFIG_NLS_UTF8=y -+CONFIG_PRINTK_TIME=y -+CONFIG_DEBUG_KERNEL=y -+# CONFIG_DEBUG_PREEMPT is not set -+# CONFIG_FTRACE is not set -+CONFIG_KGDB=y -+CONFIG_KGDB_KDB=y -+# CONFIG_ARM_UNWIND is not set -+CONFIG_DEBUG_LL=y -+CONFIG_EARLY_PRINTK=y -+CONFIG_CRYPTO_CBC=y -+CONFIG_CRYPTO_HMAC=y -+CONFIG_CRYPTO_MD5=y -+CONFIG_CRYPTO_SHA1=y -+CONFIG_CRYPTO_DES=y -+# CONFIG_CRYPTO_ANSI_CPRNG is not set -+# CONFIG_CRYPTO_HW is not set -+CONFIG_CRC16=y -+CONFIG_CRC_ITU_T=y -+CONFIG_LIBCRC32C=y -diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S -index 664eee8..e1885fa 100644 ---- a/arch/arm/kernel/head.S -+++ b/arch/arm/kernel/head.S -@@ -673,6 +673,14 @@ ARM_BE8(rev16 ip, ip) - ldrcc r7, [r4], #4 @ use branch for delay slot - bcc 1b - ret lr -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop -+ nop - #endif - ENDPROC(__fixup_a_pv_table) - -diff --git a/arch/arm/mach-bcm2709/Kconfig b/arch/arm/mach-bcm2709/Kconfig -new file mode 100644 -index 0000000..4fb6e1b ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Kconfig -@@ -0,0 +1,49 @@ -+menu "Broadcom BCM2709 Implementations" -+ depends on ARCH_BCM2709 -+ -+config MACH_BCM2709 -+ bool "Broadcom BCM2709 Development Platform" -+ help -+ Include support for the Broadcom(R) BCM2709 platform. -+ -+config BCM2709_DT -+ bool "BCM2709 Device Tree support" -+ depends on MACH_BCM2709 -+ default n -+ select USE_OF -+ select ARCH_REQUIRE_GPIOLIB -+ select PINCTRL -+ select PINCTRL_BCM2835 -+ help -+ Enable Device Tree support for BCM2709 -+ -+config BCM2708_GPIO -+ bool "BCM2709 gpio support" -+ depends on MACH_BCM2709 -+ select ARCH_REQUIRE_GPIOLIB -+ default y -+ help -+ Include support for the Broadcom(R) BCM2709 gpio. -+ -+config BCM2708_VCMEM -+ bool "Videocore Memory" -+ depends on MACH_BCM2709 -+ default y -+ help -+ Helper for videocore memory access and total size allocation. -+ -+config BCM2708_NOL2CACHE -+ bool "Videocore L2 cache disable" -+ depends on MACH_BCM2709 -+ default y -+ help -+ Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. -+ -+config BCM2708_SPIDEV -+ bool "Bind spidev to SPI0 master" -+ depends on MACH_BCM2709 -+ depends on SPI -+ default y -+ help -+ Binds spidev driver to the SPI0 master -+endmenu -diff --git a/arch/arm/mach-bcm2709/Makefile b/arch/arm/mach-bcm2709/Makefile -new file mode 100644 -index 0000000..2a803bb ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Makefile -@@ -0,0 +1,7 @@ -+# -+# Makefile for the linux kernel. -+# -+ -+obj-$(CONFIG_MACH_BCM2709) += bcm2709.o armctrl.o vcio.o power.o dma.o -+obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o -+obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o -diff --git a/arch/arm/mach-bcm2709/Makefile.boot b/arch/arm/mach-bcm2709/Makefile.boot -new file mode 100644 -index 0000000..67039c3 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/Makefile.boot -@@ -0,0 +1,3 @@ -+ zreladdr-y := 0x00008000 -+params_phys-y := 0x00000100 -+initrd_phys-y := 0x00800000 -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -new file mode 100644 -index 0000000..8e69a813 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -0,0 +1,357 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/armctrl.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include "armctrl.h" -+ -+/* For support of kernels >= 3.0 assume only one VIC for now*/ -+static unsigned int remap_irqs[(INTERRUPT_ARASANSDIO + 1) - INTERRUPT_JPEG] = { -+ INTERRUPT_VC_JPEG, -+ INTERRUPT_VC_USB, -+ INTERRUPT_VC_3D, -+ INTERRUPT_VC_DMA2, -+ INTERRUPT_VC_DMA3, -+ INTERRUPT_VC_I2C, -+ INTERRUPT_VC_SPI, -+ INTERRUPT_VC_I2SPCM, -+ INTERRUPT_VC_SDIO, -+ INTERRUPT_VC_UART, -+ INTERRUPT_VC_ARASANSDIO -+}; -+ -+static void armctrl_mask_irq(struct irq_data *d) -+{ -+ static const unsigned int disables[4] = { -+ ARM_IRQ_DIBL1, -+ ARM_IRQ_DIBL2, -+ ARM_IRQ_DIBL3, -+ 0 -+ }; -+ int i; -+ if (d->irq >= FIQ_START) { -+ writel(0, __io_address(ARM_IRQ_FAST)); -+ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { -+#if 1 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; -+ for (i=0; i<4; i++) // i = raw_smp_processor_id(); // -+ { -+ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ writel(val &~ (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { -+#if 0 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; -+ for (i=0; i<4; i++) { -+ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ writel(val &~ (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } -+} -+ -+static void armctrl_unmask_irq(struct irq_data *d) -+{ -+ static const unsigned int enables[4] = { -+ ARM_IRQ_ENBL1, -+ ARM_IRQ_ENBL2, -+ ARM_IRQ_ENBL3, -+ 0 -+ }; -+ int i; -+ if (d->irq >= FIQ_START) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ writel(0x80 | data, __io_address(ARM_IRQ_FAST)); -+ } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { -+#if 1 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_CNTPSIRQ; -+ for (i=0; i<4; i++) // i = raw_smp_processor_id(); -+ { -+ unsigned int val = readl(__io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ writel(val | (1 << data), __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= IRQ_ARM_LOCAL_MAILBOX0 && d->irq < IRQ_ARM_LOCAL_MAILBOX0 + 4) { -+#if 0 -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - IRQ_ARM_LOCAL_MAILBOX0; -+ for (i=0; i<4; i++) { -+ unsigned int val = readl(__io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ writel(val | (1 << data), __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 4*i)); -+ } -+#endif -+ } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { -+ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); -+ writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } else { printk("%s: %d\n", __func__, d->irq); BUG(); } -+} -+ -+#ifdef CONFIG_OF -+ -+#define NR_IRQS_BANK0 21 -+#define NR_BANKS 4 -+#define IRQS_PER_BANK 32 -+ -+/* from drivers/irqchip/irq-bcm2835.c */ -+static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, -+ const u32 *intspec, unsigned int intsize, -+ unsigned long *out_hwirq, unsigned int *out_type) -+{ -+ if (WARN_ON(intsize != 2)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] >= NR_BANKS)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[1] >= IRQS_PER_BANK)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) -+ return -EINVAL; -+ -+ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5)) -+ return -EINVAL; -+ -+ if (intspec[0] == 0) -+ *out_hwirq = ARM_IRQ0_BASE + intspec[1]; -+ else if (intspec[0] == 1) -+ *out_hwirq = ARM_IRQ1_BASE + intspec[1]; -+ else if (intspec[0] == 2) -+ *out_hwirq = ARM_IRQ2_BASE + intspec[1]; -+ else -+ *out_hwirq = ARM_IRQ_LOCAL_BASE + intspec[1]; -+ -+ /* reverse remap_irqs[] */ -+ switch (*out_hwirq) { -+ case INTERRUPT_VC_JPEG: -+ *out_hwirq = INTERRUPT_JPEG; -+ break; -+ case INTERRUPT_VC_USB: -+ *out_hwirq = INTERRUPT_USB; -+ break; -+ case INTERRUPT_VC_3D: -+ *out_hwirq = INTERRUPT_3D; -+ break; -+ case INTERRUPT_VC_DMA2: -+ *out_hwirq = INTERRUPT_DMA2; -+ break; -+ case INTERRUPT_VC_DMA3: -+ *out_hwirq = INTERRUPT_DMA3; -+ break; -+ case INTERRUPT_VC_I2C: -+ *out_hwirq = INTERRUPT_I2C; -+ break; -+ case INTERRUPT_VC_SPI: -+ *out_hwirq = INTERRUPT_SPI; -+ break; -+ case INTERRUPT_VC_I2SPCM: -+ *out_hwirq = INTERRUPT_I2SPCM; -+ break; -+ case INTERRUPT_VC_SDIO: -+ *out_hwirq = INTERRUPT_SDIO; -+ break; -+ case INTERRUPT_VC_UART: -+ *out_hwirq = INTERRUPT_UART; -+ break; -+ case INTERRUPT_VC_ARASANSDIO: -+ *out_hwirq = INTERRUPT_ARASANSDIO; -+ break; -+ } -+ -+ *out_type = IRQ_TYPE_NONE; -+ return 0; -+} -+ -+static struct irq_domain_ops armctrl_ops = { -+ .xlate = armctrl_xlate -+}; -+ -+void __init armctrl_dt_init(void) -+{ -+ struct device_node *np; -+ struct irq_domain *domain; -+ -+ np = of_find_compatible_node(NULL, NULL, "brcm,bcm2708-armctrl-ic"); -+ if (!np) -+ return; -+ -+ domain = irq_domain_add_legacy(np, BCM2708_ALLOC_IRQS, -+ IRQ_ARMCTRL_START, 0, -+ &armctrl_ops, NULL); -+ WARN_ON(!domain); -+} -+#else -+void __init armctrl_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ -+#if defined(CONFIG_PM) -+ -+/* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ -+ -+/* Static defines -+ * struct armctrl_device - VIC PM device (< 3.xx) -+ * @sysdev: The system device which is registered. (< 3.xx) -+ * @irq: The IRQ number for the base of the VIC. -+ * @base: The register base for the VIC. -+ * @resume_sources: A bitmask of interrupts for resume. -+ * @resume_irqs: The IRQs enabled for resume. -+ * @int_select: Save for VIC_INT_SELECT. -+ * @int_enable: Save for VIC_INT_ENABLE. -+ * @soft_int: Save for VIC_INT_SOFT. -+ * @protect: Save for VIC_PROTECT. -+ */ -+struct armctrl_info { -+ void __iomem *base; -+ int irq; -+ u32 resume_sources; -+ u32 resume_irqs; -+ u32 int_select; -+ u32 int_enable; -+ u32 soft_int; -+ u32 protect; -+} armctrl; -+ -+static int armctrl_suspend(void) -+{ -+ return 0; -+} -+ -+static void armctrl_resume(void) -+{ -+ return; -+} -+ -+/** -+ * armctrl_pm_register - Register a VIC for later power management control -+ * @base: The base address of the VIC. -+ * @irq: The base IRQ for the VIC. -+ * @resume_sources: bitmask of interrupts allowed for resume sources. -+ * -+ * For older kernels (< 3.xx) do - -+ * Register the VIC with the system device tree so that it can be notified -+ * of suspend and resume requests and ensure that the correct actions are -+ * taken to re-instate the settings on resume. -+ */ -+static void __init armctrl_pm_register(void __iomem * base, unsigned int irq, -+ u32 resume_sources) -+{ -+ armctrl.base = base; -+ armctrl.resume_sources = resume_sources; -+ armctrl.irq = irq; -+} -+ -+static int armctrl_set_wake(struct irq_data *d, unsigned int on) -+{ -+ unsigned int off = d->irq & 31; -+ u32 bit = 1 << off; -+ -+ if (!(bit & armctrl.resume_sources)) -+ return -EINVAL; -+ -+ if (on) -+ armctrl.resume_irqs |= bit; -+ else -+ armctrl.resume_irqs &= ~bit; -+ -+ return 0; -+} -+ -+#else -+static inline void armctrl_pm_register(void __iomem * base, unsigned int irq, -+ u32 arg1) -+{ -+} -+ -+#define armctrl_suspend NULL -+#define armctrl_resume NULL -+#define armctrl_set_wake NULL -+#endif /* CONFIG_PM */ -+ -+static struct syscore_ops armctrl_syscore_ops = { -+ .suspend = armctrl_suspend, -+ .resume = armctrl_resume, -+}; -+ -+/** -+ * armctrl_syscore_init - initicall to register VIC pm functions -+ * -+ * This is called via late_initcall() to register -+ * the resources for the VICs due to the early -+ * nature of the VIC's registration. -+*/ -+static int __init armctrl_syscore_init(void) -+{ -+ register_syscore_ops(&armctrl_syscore_ops); -+ return 0; -+} -+ -+late_initcall(armctrl_syscore_init); -+ -+static struct irq_chip armctrl_chip = { -+ .name = "ARMCTRL", -+ .irq_ack = NULL, -+ .irq_mask = armctrl_mask_irq, -+ .irq_unmask = armctrl_unmask_irq, -+ .irq_set_wake = armctrl_set_wake, -+}; -+ -+/** -+ * armctrl_init - initialise a vectored interrupt controller -+ * @base: iomem base address -+ * @irq_start: starting interrupt number, must be muliple of 32 -+ * @armctrl_sources: bitmask of interrupt sources to allow -+ * @resume_sources: bitmask of interrupt sources to allow for resume -+ */ -+int __init armctrl_init(void __iomem * base, unsigned int irq_start, -+ u32 armctrl_sources, u32 resume_sources) -+{ -+ unsigned int irq; -+ -+ for (irq = 0; irq < BCM2708_ALLOC_IRQS; irq++) { -+ unsigned int data = irq; -+ if (irq >= INTERRUPT_JPEG && irq <= INTERRUPT_ARASANSDIO) -+ data = remap_irqs[irq - INTERRUPT_JPEG]; -+ if (irq >= IRQ_ARM_LOCAL_CNTPSIRQ && irq <= IRQ_ARM_LOCAL_TIMER) { -+ irq_set_percpu_devid(irq); -+ irq_set_chip_and_handler(irq, &armctrl_chip, handle_percpu_devid_irq); -+ set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); -+ } else { -+ irq_set_chip_and_handler(irq, &armctrl_chip, handle_level_irq); -+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE | IRQF_DISABLED); -+ } -+ irq_set_chip_data(irq, (void *)data); -+ } -+ -+ armctrl_pm_register(base, irq_start, resume_sources); -+ init_FIQ(FIQ_START); -+ armctrl_dt_init(); -+ return 0; -+} -diff --git a/arch/arm/mach-bcm2709/armctrl.h b/arch/arm/mach-bcm2709/armctrl.h -new file mode 100644 -index 0000000..0aa916e ---- /dev/null -+++ b/arch/arm/mach-bcm2709/armctrl.h -@@ -0,0 +1,27 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/armctrl.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_ARMCTRL_H -+#define __BCM2708_ARMCTRL_H -+ -+extern int __init armctrl_init(void __iomem * base, unsigned int irq_start, -+ u32 armctrl_sources, u32 resume_sources); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/bcm2708_gpio.c b/arch/arm/mach-bcm2709/bcm2708_gpio.c -new file mode 100644 -index 0000000..c1e9254 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2708_gpio.c -@@ -0,0 +1,426 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" -+#define DRIVER_NAME BCM_GPIO_DRIVER_NAME -+#define BCM_GPIO_USE_IRQ 1 -+ -+#define GPIOFSEL(x) (0x00+(x)*4) -+#define GPIOSET(x) (0x1c+(x)*4) -+#define GPIOCLR(x) (0x28+(x)*4) -+#define GPIOLEV(x) (0x34+(x)*4) -+#define GPIOEDS(x) (0x40+(x)*4) -+#define GPIOREN(x) (0x4c+(x)*4) -+#define GPIOFEN(x) (0x58+(x)*4) -+#define GPIOHEN(x) (0x64+(x)*4) -+#define GPIOLEN(x) (0x70+(x)*4) -+#define GPIOAREN(x) (0x7c+(x)*4) -+#define GPIOAFEN(x) (0x88+(x)*4) -+#define GPIOUD(x) (0x94+(x)*4) -+#define GPIOUDCLK(x) (0x98+(x)*4) -+ -+#define GPIO_BANKS 2 -+ -+enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, -+ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, -+ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, -+ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, -+}; -+ -+ /* Each of the two spinlocks protects a different set of hardware -+ * regiters and data structurs. This decouples the code of the IRQ from -+ * the GPIO code. This also makes the case of a GPIO routine call from -+ * the IRQ code simpler. -+ */ -+static DEFINE_SPINLOCK(lock); /* GPIO registers */ -+ -+struct bcm2708_gpio { -+ struct list_head list; -+ void __iomem *base; -+ struct gpio_chip gc; -+ unsigned long rising[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long falling[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long high[(BCM2708_NR_GPIOS + 31) / 32]; -+ unsigned long low[(BCM2708_NR_GPIOS + 31) / 32]; -+}; -+ -+static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, -+ int function) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned long flags; -+ unsigned gpiodir; -+ unsigned gpio_bank = offset / 10; -+ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3; -+ -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&lock, flags); -+ -+ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); -+ gpiodir &= ~(7 << gpio_field_offset); -+ gpiodir |= function << gpio_field_offset; -+ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank)); -+ spin_unlock_irqrestore(&lock, flags); -+ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); -+ -+ return 0; -+} -+ -+static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset) -+{ -+ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT); -+} -+ -+static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value); -+static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset, -+ int value) -+{ -+ int ret; -+ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT); -+ if (ret >= 0) -+ bcm2708_gpio_set(gc, offset, value); -+ return ret; -+} -+ -+static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+ unsigned lev; -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return 0; -+ lev = readl(gpio->base + GPIOLEV(gpio_bank)); -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); -+ return 0x1 & (lev >> gpio_field_offset); -+} -+ -+static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); -+ if (offset >= BCM2708_NR_GPIOS) -+ return; -+ if (value) -+ writel(1 << gpio_field_offset, gpio->base + GPIOSET(gpio_bank)); -+ else -+ writel(1 << gpio_field_offset, gpio->base + GPIOCLR(gpio_bank)); -+} -+ -+/********************** -+ * extension to configure pullups -+ */ -+int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset, -+ bcm2708_gpio_pull_t value) -+{ -+ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); -+ unsigned gpio_bank = offset / 32; -+ unsigned gpio_field_offset = (offset - 32 * gpio_bank); -+ -+ if (offset >= BCM2708_NR_GPIOS) -+ return -EINVAL; -+ -+ switch (value) { -+ case BCM2708_PULL_UP: -+ writel(2, gpio->base + GPIOUD(0)); -+ break; -+ case BCM2708_PULL_DOWN: -+ writel(1, gpio->base + GPIOUD(0)); -+ break; -+ case BCM2708_PULL_OFF: -+ writel(0, gpio->base + GPIOUD(0)); -+ break; -+ } -+ -+ udelay(5); -+ writel(1 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); -+ udelay(5); -+ writel(0, gpio->base + GPIOUD(0)); -+ writel(0 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank)); -+ -+ return 0; -+} -+EXPORT_SYMBOL(bcm2708_gpio_setpull); -+ -+/************************************************************************************************************************* -+ * bcm2708 GPIO IRQ -+ */ -+ -+#if BCM_GPIO_USE_IRQ -+ -+static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) -+{ -+ return gpio_to_irq(gpio); -+} -+ -+static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned go = gn % 32; -+ -+ gpio->rising[gb] &= ~(1 << go); -+ gpio->falling[gb] &= ~(1 << go); -+ gpio->high[gb] &= ~(1 << go); -+ gpio->low[gb] &= ~(1 << go); -+ -+ if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) -+ return -EINVAL; -+ -+ if (type & IRQ_TYPE_EDGE_RISING) -+ gpio->rising[gb] |= (1 << go); -+ if (type & IRQ_TYPE_EDGE_FALLING) -+ gpio->falling[gb] |= (1 << go); -+ if (type & IRQ_TYPE_LEVEL_HIGH) -+ gpio->high[gb] |= (1 << go); -+ if (type & IRQ_TYPE_LEVEL_LOW) -+ gpio->low[gb] |= (1 << go); -+ return 0; -+} -+ -+static void bcm2708_gpio_irq_mask(struct irq_data *d) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned long rising = readl(gpio->base + GPIOREN(gb)); -+ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); -+ unsigned long high = readl(gpio->base + GPIOHEN(gb)); -+ unsigned long low = readl(gpio->base + GPIOLEN(gb)); -+ -+ gn = gn % 32; -+ -+ writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb)); -+ writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb)); -+ writel(high & ~(1 << gn), gpio->base + GPIOHEN(gb)); -+ writel(low & ~(1 << gn), gpio->base + GPIOLEN(gb)); -+} -+ -+static void bcm2708_gpio_irq_unmask(struct irq_data *d) -+{ -+ unsigned irq = d->irq; -+ struct bcm2708_gpio *gpio = irq_get_chip_data(irq); -+ unsigned gn = irq_to_gpio(irq); -+ unsigned gb = gn / 32; -+ unsigned go = gn % 32; -+ unsigned long rising = readl(gpio->base + GPIOREN(gb)); -+ unsigned long falling = readl(gpio->base + GPIOFEN(gb)); -+ unsigned long high = readl(gpio->base + GPIOHEN(gb)); -+ unsigned long low = readl(gpio->base + GPIOLEN(gb)); -+ -+ if (gpio->rising[gb] & (1 << go)) { -+ writel(rising | (1 << go), gpio->base + GPIOREN(gb)); -+ } else { -+ writel(rising & ~(1 << go), gpio->base + GPIOREN(gb)); -+ } -+ -+ if (gpio->falling[gb] & (1 << go)) { -+ writel(falling | (1 << go), gpio->base + GPIOFEN(gb)); -+ } else { -+ writel(falling & ~(1 << go), gpio->base + GPIOFEN(gb)); -+ } -+ -+ if (gpio->high[gb] & (1 << go)) { -+ writel(high | (1 << go), gpio->base + GPIOHEN(gb)); -+ } else { -+ writel(high & ~(1 << go), gpio->base + GPIOHEN(gb)); -+ } -+ -+ if (gpio->low[gb] & (1 << go)) { -+ writel(low | (1 << go), gpio->base + GPIOLEN(gb)); -+ } else { -+ writel(low & ~(1 << go), gpio->base + GPIOLEN(gb)); -+ } -+} -+ -+static struct irq_chip bcm2708_irqchip = { -+ .name = "GPIO", -+ .irq_enable = bcm2708_gpio_irq_unmask, -+ .irq_disable = bcm2708_gpio_irq_mask, -+ .irq_unmask = bcm2708_gpio_irq_unmask, -+ .irq_mask = bcm2708_gpio_irq_mask, -+ .irq_set_type = bcm2708_gpio_irq_set_type, -+}; -+ -+static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id) -+{ -+ unsigned long edsr; -+ unsigned bank; -+ int i; -+ unsigned gpio; -+ unsigned level_bits; -+ struct bcm2708_gpio *gpio_data = dev_id; -+ -+ for (bank = 0; bank < GPIO_BANKS; bank++) { -+ edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank)); -+ level_bits = gpio_data->high[bank] | gpio_data->low[bank]; -+ -+ for_each_set_bit(i, &edsr, 32) { -+ gpio = i + bank * 32; -+ /* ack edge triggered IRQs immediately */ -+ if (!(level_bits & (1<gc.to_irq = bcm2708_gpio_to_irq; -+ -+ for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) { -+ irq_set_chip_data(irq, ucb); -+ irq_set_chip_and_handler(irq, &bcm2708_irqchip, -+ handle_simple_irq); -+ set_irq_flags(irq, IRQF_VALID); -+ } -+ -+ bcm2708_gpio_irq.dev_id = ucb; -+ setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq); -+} -+ -+#else -+ -+static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb) -+{ -+} -+ -+#endif /* #if BCM_GPIO_USE_IRQ ***************************************************************************************************************** */ -+ -+static int bcm2708_gpio_probe(struct platform_device *dev) -+{ -+ struct bcm2708_gpio *ucb; -+ struct resource *res; -+ int bank; -+ int err = 0; -+ -+ printk(KERN_INFO DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev); -+ -+ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL); -+ if (NULL == ucb) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "mailbox memory\n"); -+ err = -ENOMEM; -+ goto err; -+ } -+ -+ res = platform_get_resource(dev, IORESOURCE_MEM, 0); -+ -+ platform_set_drvdata(dev, ucb); -+ ucb->base = __io_address(GPIO_BASE); -+ -+ ucb->gc.label = "bcm2708_gpio"; -+ ucb->gc.base = 0; -+ ucb->gc.ngpio = BCM2708_NR_GPIOS; -+ ucb->gc.owner = THIS_MODULE; -+ -+ ucb->gc.direction_input = bcm2708_gpio_dir_in; -+ ucb->gc.direction_output = bcm2708_gpio_dir_out; -+ ucb->gc.get = bcm2708_gpio_get; -+ ucb->gc.set = bcm2708_gpio_set; -+ ucb->gc.can_sleep = 0; -+ -+ for (bank = 0; bank < GPIO_BANKS; bank++) { -+ writel(0, ucb->base + GPIOREN(bank)); -+ writel(0, ucb->base + GPIOFEN(bank)); -+ writel(0, ucb->base + GPIOHEN(bank)); -+ writel(0, ucb->base + GPIOLEN(bank)); -+ writel(0, ucb->base + GPIOAREN(bank)); -+ writel(0, ucb->base + GPIOAFEN(bank)); -+ writel(~0, ucb->base + GPIOEDS(bank)); -+ } -+ -+ bcm2708_gpio_irq_init(ucb); -+ -+ err = gpiochip_add(&ucb->gc); -+ -+err: -+ return err; -+ -+} -+ -+static int bcm2708_gpio_remove(struct platform_device *dev) -+{ -+ int err = 0; -+ struct bcm2708_gpio *ucb = platform_get_drvdata(dev); -+ -+ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev); -+ -+ gpiochip_remove(&ucb->gc); -+ -+ platform_set_drvdata(dev, NULL); -+ kfree(ucb); -+ -+ return err; -+} -+ -+static struct platform_driver bcm2708_gpio_driver = { -+ .probe = bcm2708_gpio_probe, -+ .remove = bcm2708_gpio_remove, -+ .driver = { -+ .name = "bcm2708_gpio"}, -+}; -+ -+static int __init bcm2708_gpio_init(void) -+{ -+ return platform_driver_register(&bcm2708_gpio_driver); -+} -+ -+static void __exit bcm2708_gpio_exit(void) -+{ -+ platform_driver_unregister(&bcm2708_gpio_driver); -+} -+ -+module_init(bcm2708_gpio_init); -+module_exit(bcm2708_gpio_exit); -+ -+MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver"); -+MODULE_LICENSE("GPL"); -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -new file mode 100644 -index 0000000..3055e72d ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -0,0 +1,1234 @@ -+/* -+ * linux/arch/arm/mach-bcm2709/bcm2709.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "bcm2709.h" -+#include "armctrl.h" -+ -+#ifdef CONFIG_BCM_VC_CMA -+#include -+#endif -+ -+//#define SYSTEM_TIMER -+ -+/* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to -+ * give us IO access only to 64Mbytes of physical memory (26 bits). We could -+ * represent this window by setting our dmamasks to 26 bits but, in fact -+ * we're not going to use addresses outside this range (they're not in real -+ * memory) so we don't bother. -+ * -+ * In the future we might include code to use this IOMMU to remap other -+ * physical addresses onto VideoCore memory then the use of 32-bits would be -+ * more legitimate. -+ */ -+#define DMA_MASK_BITS_COMMON 32 -+ -+// use GPIO 4 for the one-wire GPIO pin, if enabled -+#define W1_GPIO 4 -+// ensure one-wire GPIO pullup is disabled by default -+#define W1_PULLUP -1 -+ -+/* command line parameters */ -+static unsigned boardrev, serial; -+static unsigned uart_clock = UART0_CLOCK; -+static unsigned disk_led_gpio = 16; -+static unsigned disk_led_active_low = 1; -+static unsigned reboot_part = 0; -+static unsigned w1_gpio_pin = W1_GPIO; -+static unsigned w1_gpio_pullup = W1_PULLUP; -+ -+static unsigned use_dt = 0; -+ -+static void __init bcm2709_init_led(void); -+ -+void __init bcm2709_init_irq(void) -+{ -+ armctrl_init(__io_address(ARMCTRL_IC_BASE), 0, 0, 0); -+} -+ -+static struct map_desc bcm2709_io_desc[] __initdata = { -+ { -+ .virtual = IO_ADDRESS(ARMCTRL_BASE), -+ .pfn = __phys_to_pfn(ARMCTRL_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(UART0_BASE), -+ .pfn = __phys_to_pfn(UART0_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(UART1_BASE), -+ .pfn = __phys_to_pfn(UART1_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(DMA_BASE), -+ .pfn = __phys_to_pfn(DMA_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(MCORE_BASE), -+ .pfn = __phys_to_pfn(MCORE_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(ST_BASE), -+ .pfn = __phys_to_pfn(ST_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(USB_BASE), -+ .pfn = __phys_to_pfn(USB_BASE), -+ .length = SZ_128K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(PM_BASE), -+ .pfn = __phys_to_pfn(PM_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(GPIO_BASE), -+ .pfn = __phys_to_pfn(GPIO_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+ { -+ .virtual = IO_ADDRESS(ARM_LOCAL_BASE), -+ .pfn = __phys_to_pfn(ARM_LOCAL_BASE), -+ .length = SZ_4K, -+ .type = MT_DEVICE}, -+}; -+ -+void __init bcm2709_map_io(void) -+{ -+ iotable_init(bcm2709_io_desc, ARRAY_SIZE(bcm2709_io_desc)); -+} -+ -+#ifdef SYSTEM_TIMER -+ -+/* The STC is a free running counter that increments at the rate of 1MHz */ -+#define STC_FREQ_HZ 1000000 -+ -+static inline uint32_t timer_read(void) -+{ -+ /* STC: a free running counter that increments at the rate of 1MHz */ -+ return readl(__io_address(ST_BASE + 0x04)); -+} -+ -+static unsigned long bcm2709_read_current_timer(void) -+{ -+ return timer_read(); -+} -+ -+static u64 notrace bcm2709_read_sched_clock(void) -+{ -+ return timer_read(); -+} -+ -+static cycle_t clksrc_read(struct clocksource *cs) -+{ -+ return timer_read(); -+} -+ -+static struct clocksource clocksource_stc = { -+ .name = "stc", -+ .rating = 300, -+ .read = clksrc_read, -+ .mask = CLOCKSOURCE_MASK(32), -+ .flags = CLOCK_SOURCE_IS_CONTINUOUS, -+}; -+ -+unsigned long frc_clock_ticks32(void) -+{ -+ return timer_read(); -+} -+ -+static void __init bcm2709_clocksource_init(void) -+{ -+ if (clocksource_register_hz(&clocksource_stc, STC_FREQ_HZ)) { -+ printk(KERN_ERR "timer: failed to initialize clock " -+ "source %s\n", clocksource_stc.name); -+ } -+} -+#endif -+ -+struct clk __init *bcm2709_clk_register(const char *name, unsigned long fixed_rate) -+{ -+ struct clk *clk; -+ -+ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, -+ fixed_rate); -+ if (IS_ERR(clk)) -+ pr_err("%s not registered\n", name); -+ -+ return clk; -+} -+ -+void __init bcm2709_register_clkdev(struct clk *clk, const char *name) -+{ -+ int ret; -+ -+ ret = clk_register_clkdev(clk, NULL, name); -+ if (ret) -+ pr_err("%s alias not registered\n", name); -+} -+ -+void __init bcm2709_init_clocks(void) -+{ -+ struct clk *clk; -+ -+ clk = bcm2709_clk_register("uart0_clk", uart_clock); -+ bcm2709_register_clkdev(clk, "dev:f1"); -+ -+ clk = bcm2709_clk_register("sdhost_clk", 250000000); -+ bcm2709_register_clkdev(clk, "bcm2708_spi.0"); -+ bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); -+ bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); -+} -+ -+#define UART0_IRQ { IRQ_UART, 0 /*NO_IRQ*/ } -+#define UART0_DMA { 15, 14 } -+ -+AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); -+ -+static struct amba_device *amba_devs[] __initdata = { -+ &uart0_device, -+}; -+ -+static struct resource bcm2708_dmaman_resources[] = { -+ { -+ .start = DMA_BASE, -+ .end = DMA_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_dmaman_device = { -+ .name = BCM_DMAMAN_DRIVER_NAME, -+ .id = 0, /* first bcm2708_dma */ -+ .resource = bcm2708_dmaman_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), -+}; -+ -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+static struct w1_gpio_platform_data w1_gpio_pdata = { -+ .pin = W1_GPIO, -+ .ext_pullup_enable_pin = W1_PULLUP, -+ .is_open_drain = 0, -+}; -+ -+static struct platform_device w1_device = { -+ .name = "w1-gpio", -+ .id = -1, -+ .dev.platform_data = &w1_gpio_pdata, -+}; -+#endif -+ -+static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_fb_device = { -+ .name = "bcm2708_fb", -+ .id = -1, /* only one bcm2708_fb */ -+ .resource = NULL, -+ .num_resources = 0, -+ .dev = { -+ .dma_mask = &fb_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { -+ { -+ .mapbase = UART1_BASE + 0x40, -+ .irq = IRQ_AUX, -+ .uartclk = 125000000, -+ .regshift = 2, -+ .iotype = UPIO_MEM, -+ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, -+ .type = PORT_8250, -+ }, -+ {}, -+}; -+ -+static struct platform_device bcm2708_uart1_device = { -+ .name = "serial8250", -+ .id = PLAT8250_DEV_PLATFORM, -+ .dev = { -+ .platform_data = bcm2708_uart1_platform_data, -+ }, -+}; -+ -+static struct resource bcm2708_usb_resources[] = { -+ [0] = { -+ .start = USB_BASE, -+ .end = USB_BASE + SZ_128K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = MPHI_BASE, -+ .end = MPHI_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [2] = { -+ .start = IRQ_HOSTPORT, -+ .end = IRQ_HOSTPORT, -+ .flags = IORESOURCE_IRQ, -+ }, -+ [3] = { -+ .start = IRQ_USB, -+ .end = IRQ_USB, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+ -+static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_usb_device = { -+ .name = "bcm2708_usb", -+ .id = -1, /* only one bcm2708_usb */ -+ .resource = bcm2708_usb_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), -+ .dev = { -+ .dma_mask = &usb_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+static struct resource bcm2708_vcio_resources[] = { -+ [0] = { /* mailbox/semaphore/doorbell access */ -+ .start = MCORE_BASE, -+ .end = MCORE_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_vcio_device = { -+ .name = BCM_VCIO_DRIVER_NAME, -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_vcio_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), -+ .dev = { -+ .dma_mask = &vcio_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+ -+#ifdef CONFIG_BCM2708_GPIO -+#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" -+ -+static struct resource bcm2708_gpio_resources[] = { -+ [0] = { /* general purpose I/O */ -+ .start = GPIO_BASE, -+ .end = GPIO_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_gpio_device = { -+ .name = BCM_GPIO_DRIVER_NAME, -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_gpio_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), -+ .dev = { -+ .dma_mask = &gpio_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+#endif -+ -+#ifdef SYSTEM_TIMER -+static struct resource bcm2708_systemtimer_resources[] = { -+ [0] = { /* system timer access */ -+ .start = ST_BASE, -+ .end = ST_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = IRQ_TIMER3, -+ .end = IRQ_TIMER3, -+ .flags = IORESOURCE_IRQ, -+ } -+ -+}; -+ -+static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+static struct platform_device bcm2708_systemtimer_device = { -+ .name = "bcm2708_systemtimer", -+ .id = -1, /* only one VideoCore I/O area */ -+ .resource = bcm2708_systemtimer_resources, -+ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), -+ .dev = { -+ .dma_mask = &systemtimer_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), -+ }, -+}; -+#endif -+ -+#ifdef CONFIG_MMC_BCM2835 /* Arasan emmc SD (new) */ -+static struct resource bcm2835_emmc_resources[] = { -+ [0] = { -+ .start = EMMC_BASE, -+ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ -+ /* the memory map actually makes SZ_4K available */ -+ .flags = IORESOURCE_MEM, -+ }, -+ [1] = { -+ .start = IRQ_ARASANSDIO, -+ .end = IRQ_ARASANSDIO, -+ .flags = IORESOURCE_IRQ, -+ }, -+}; -+ -+static u64 bcm2835_emmc_dmamask = 0xffffffffUL; -+ -+struct platform_device bcm2835_emmc_device = { -+ .name = "mmc-bcm2835", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2835_emmc_resources), -+ .resource = bcm2835_emmc_resources, -+ .dev = { -+ .dma_mask = &bcm2835_emmc_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+#endif /* CONFIG_MMC_BCM2835 */ -+ -+static struct resource bcm2708_powerman_resources[] = { -+ [0] = { -+ .start = PM_BASE, -+ .end = PM_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+}; -+ -+static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+ -+struct platform_device bcm2708_powerman_device = { -+ .name = "bcm2708_powerman", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), -+ .resource = bcm2708_powerman_resources, -+ .dev = { -+ .dma_mask = &powerman_dmamask, -+ .coherent_dma_mask = 0xffffffffUL}, -+}; -+ -+ -+static struct platform_device bcm2708_alsa_devices[] = { -+ [0] = { -+ .name = "bcm2835_AUD0", -+ .id = 0, /* first audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [1] = { -+ .name = "bcm2835_AUD1", -+ .id = 1, /* second audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [2] = { -+ .name = "bcm2835_AUD2", -+ .id = 2, /* third audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [3] = { -+ .name = "bcm2835_AUD3", -+ .id = 3, /* forth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [4] = { -+ .name = "bcm2835_AUD4", -+ .id = 4, /* fifth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [5] = { -+ .name = "bcm2835_AUD5", -+ .id = 5, /* sixth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [6] = { -+ .name = "bcm2835_AUD6", -+ .id = 6, /* seventh audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+ [7] = { -+ .name = "bcm2835_AUD7", -+ .id = 7, /* eighth audio device */ -+ .resource = 0, -+ .num_resources = 0, -+ }, -+}; -+ -+static struct resource bcm2708_spi_resources[] = { -+ { -+ .start = SPI0_BASE, -+ .end = SPI0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = IRQ_SPI, -+ .end = IRQ_SPI, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+ -+static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); -+static struct platform_device bcm2708_spi_device = { -+ .name = "bcm2708_spi", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_spi_resources), -+ .resource = bcm2708_spi_resources, -+ .dev = { -+ .dma_mask = &bcm2708_spi_dmamask, -+ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)}, -+}; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+static struct spi_board_info bcm2708_spi_devices[] = { -+#ifdef CONFIG_SPI_SPIDEV -+ { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 0, -+ .mode = SPI_MODE_0, -+ }, { -+ .modalias = "spidev", -+ .max_speed_hz = 500000, -+ .bus_num = 0, -+ .chip_select = 1, -+ .mode = SPI_MODE_0, -+ } -+#endif -+}; -+#endif -+ -+static struct resource bcm2708_bsc0_resources[] = { -+ { -+ .start = BSC0_BASE, -+ .end = BSC0_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc0_device = { -+ .name = "bcm2708_i2c", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources), -+ .resource = bcm2708_bsc0_resources, -+}; -+ -+ -+static struct resource bcm2708_bsc1_resources[] = { -+ { -+ .start = BSC1_BASE, -+ .end = BSC1_BASE + SZ_256 - 1, -+ .flags = IORESOURCE_MEM, -+ }, { -+ .start = INTERRUPT_I2C, -+ .end = INTERRUPT_I2C, -+ .flags = IORESOURCE_IRQ, -+ } -+}; -+ -+static struct platform_device bcm2708_bsc1_device = { -+ .name = "bcm2708_i2c", -+ .id = 1, -+ .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources), -+ .resource = bcm2708_bsc1_resources, -+}; -+ -+static struct platform_device bcm2835_hwmon_device = { -+ .name = "bcm2835_hwmon", -+}; -+ -+static struct platform_device bcm2835_thermal_device = { -+ .name = "bcm2835_thermal", -+}; -+ -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+static struct resource bcm2708_i2s_resources[] = { -+ { -+ .start = I2S_BASE, -+ .end = I2S_BASE + 0x20, -+ .flags = IORESOURCE_MEM, -+ }, -+ { -+ .start = PCM_CLOCK_BASE, -+ .end = PCM_CLOCK_BASE + 0x02, -+ .flags = IORESOURCE_MEM, -+ } -+}; -+ -+static struct platform_device bcm2708_i2s_device = { -+ .name = "bcm2708-i2s", -+ .id = 0, -+ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), -+ .resource = bcm2708_i2s_resources, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+static struct platform_device snd_hifiberry_dac_device = { -+ .name = "snd-hifiberry-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm5102a_codec_device = { -+ .name = "pcm5102a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+static struct platform_device snd_rpi_hifiberry_dacplus_device = { -+ .name = "snd-rpi-hifiberry-dacplus", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_pcm512x_hbdacplus_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4d) -+ }, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+static struct platform_device snd_hifiberry_digi_device = { -+ .name = "snd-hifiberry-digi", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_wm8804_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("wm8804", 0x3b) -+ }, -+}; -+ -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+static struct platform_device snd_hifiberry_amp_device = { -+ .name = "snd-hifiberry-amp", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct i2c_board_info __initdata snd_tas5713_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("tas5713", 0x1b) -+ }, -+}; -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+static struct platform_device snd_rpi_dac_device = { -+ .name = "snd-rpi-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+static struct platform_device snd_pcm1794a_codec_device = { -+ .name = "pcm1794a-codec", -+ .id = -1, -+ .num_resources = 0, -+}; -+#endif -+ -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+static struct platform_device snd_rpi_iqaudio_dac_device = { -+ .name = "snd-rpi-iqaudio-dac", -+ .id = 0, -+ .num_resources = 0, -+}; -+ -+// Use the actual device name rather than generic driver name -+static struct i2c_board_info __initdata snd_pcm512x_i2c_devices[] = { -+ { -+ I2C_BOARD_INFO("pcm5122", 0x4c) -+ }, -+}; -+#endif -+ -+int __init bcm_register_device(struct platform_device *pdev) -+{ -+ int ret; -+ -+ ret = platform_device_register(pdev); -+ if (ret) -+ pr_debug("Unable to register platform device '%s': %d\n", -+ pdev->name, ret); -+ -+ return ret; -+} -+ -+/* -+ * Use these macros for platform and i2c devices that are present in the -+ * Device Tree. This way the devices are only added on non-DT systems. -+ */ -+#define bcm_register_device_dt(pdev) \ -+ if (!use_dt) bcm_register_device(pdev) -+ -+#define i2c_register_board_info_dt(busnum, info, n) \ -+ if (!use_dt) i2c_register_board_info(busnum, info, n) -+ -+int calc_rsts(int partition) -+{ -+ return PM_PASSWORD | -+ ((partition & (1 << 0)) << 0) | -+ ((partition & (1 << 1)) << 1) | -+ ((partition & (1 << 2)) << 2) | -+ ((partition & (1 << 3)) << 3) | -+ ((partition & (1 << 4)) << 4) | -+ ((partition & (1 << 5)) << 5); -+} -+ -+static void bcm2709_restart(enum reboot_mode mode, const char *cmd) -+{ -+ extern char bcm2708_reboot_mode; -+ uint32_t pm_rstc, pm_wdog; -+ uint32_t timeout = 10; -+ uint32_t pm_rsts = 0; -+ -+ if(bcm2708_reboot_mode == 'q') -+ { -+ // NOOBS < 1.3 booting with reboot=q -+ pm_rsts = readl(__io_address(PM_RSTS)); -+ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRQ_SET; -+ } -+ else if(bcm2708_reboot_mode == 'p') -+ { -+ // NOOBS < 1.3 halting -+ pm_rsts = readl(__io_address(PM_RSTS)); -+ pm_rsts = PM_PASSWORD | pm_rsts | PM_RSTS_HADWRH_SET; -+ } -+ else -+ { -+ pm_rsts = calc_rsts(reboot_part); -+ } -+ -+ writel(pm_rsts, __io_address(PM_RSTS)); -+ -+ /* Setup watchdog for reset */ -+ pm_rstc = readl(__io_address(PM_RSTC)); -+ -+ pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0) -+ pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; -+ -+ writel(pm_wdog, __io_address(PM_WDOG)); -+ writel(pm_rstc, __io_address(PM_RSTC)); -+} -+ -+/* We can't really power off, but if we do the normal reset scheme, and indicate to bootcode.bin not to reboot, then most of the chip will be powered off */ -+static void bcm2709_power_off(void) -+{ -+ extern char bcm2708_reboot_mode; -+ if(bcm2708_reboot_mode == 'q') -+ { -+ // NOOBS < v1.3 -+ bcm2709_restart('p', ""); -+ } -+ else -+ { -+ /* partition 63 is special code for HALT the bootloader knows not to boot*/ -+ reboot_part = 63; -+ /* continue with normal reset mechanism */ -+ bcm2709_restart(0, ""); -+ } -+} -+ -+#ifdef CONFIG_OF -+static void __init bcm2709_dt_init(void) -+{ -+ int ret; -+ -+ of_clk_init(NULL); -+ ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -+ if (ret) { -+ pr_err("of_platform_populate failed: %d\n", ret); -+ /* Proceed as if CONFIG_OF was not defined */ -+ } else { -+ use_dt = 1; -+ } -+} -+#else -+static void __init bcm2709_dt_init(void) { } -+#endif /* CONFIG_OF */ -+ -+void __init bcm2709_init(void) -+{ -+ int i; -+ -+#if defined(CONFIG_BCM_VC_CMA) -+ vc_cma_early_init(); -+#endif -+ printk("bcm2709.uart_clock = %d\n", uart_clock); -+ pm_power_off = bcm2709_power_off; -+ -+ bcm2709_init_clocks(); -+ bcm2709_dt_init(); -+ -+ bcm_register_device(&bcm2708_dmaman_device); -+ bcm_register_device(&bcm2708_vcio_device); -+#ifdef CONFIG_BCM2708_GPIO -+ bcm_register_device_dt(&bcm2708_gpio_device); -+#endif -+#if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) -+ w1_gpio_pdata.pin = w1_gpio_pin; -+ w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -+ bcm_register_device_dt(&w1_device); -+#endif -+#ifdef SYSTEM_TIMER -+ bcm_register_device(&bcm2708_systemtimer_device); -+#endif -+ bcm_register_device(&bcm2708_fb_device); -+ bcm_register_device(&bcm2708_usb_device); -+ bcm_register_device(&bcm2708_uart1_device); -+ bcm_register_device(&bcm2708_powerman_device); -+ -+#ifdef CONFIG_MMC_BCM2835 -+ bcm_register_device(&bcm2835_emmc_device); -+#endif -+ bcm2709_init_led(); -+ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) -+ bcm_register_device(&bcm2708_alsa_devices[i]); -+ -+ bcm_register_device(&bcm2835_hwmon_device); -+ bcm_register_device(&bcm2835_thermal_device); -+ -+ bcm_register_device_dt(&bcm2708_spi_device); -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+#if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) -+ bcm_register_device_dt(&bcm2708_i2s_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_dac_device); -+ bcm_register_device_dt(&snd_pcm5102a_codec_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS_MODULE) -+ bcm_register_device_dt(&snd_rpi_hifiberry_dacplus_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_hbdacplus_i2c_devices, ARRAY_SIZE(snd_pcm512x_hbdacplus_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_digi_device); -+ i2c_register_board_info_dt(1, snd_wm8804_i2c_devices, ARRAY_SIZE(snd_wm8804_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP_MODULE) -+ bcm_register_device_dt(&snd_hifiberry_amp_device); -+ i2c_register_board_info_dt(1, snd_tas5713_i2c_devices, ARRAY_SIZE(snd_tas5713_i2c_devices)); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) -+ bcm_register_device_dt(&snd_rpi_dac_device); -+ bcm_register_device_dt(&snd_pcm1794a_codec_device); -+#endif -+ -+#if defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) || defined(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC_MODULE) -+ bcm_register_device_dt(&snd_rpi_iqaudio_dac_device); -+ i2c_register_board_info_dt(1, snd_pcm512x_i2c_devices, ARRAY_SIZE(snd_pcm512x_i2c_devices)); -+#endif -+ -+ -+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { -+ struct amba_device *d = amba_devs[i]; -+ amba_device_register(d, &iomem_resource); -+ } -+ system_rev = boardrev; -+ system_serial_low = serial; -+ -+#ifdef CONFIG_BCM2708_SPIDEV -+ if (!use_dt) -+ spi_register_board_info(bcm2708_spi_devices, -+ ARRAY_SIZE(bcm2708_spi_devices)); -+#endif -+} -+ -+#ifdef SYSTEM_TIMER -+static void timer_set_mode(enum clock_event_mode mode, -+ struct clock_event_device *clk) -+{ -+ switch (mode) { -+ case CLOCK_EVT_MODE_ONESHOT: /* Leave the timer disabled, .set_next_event will enable it */ -+ case CLOCK_EVT_MODE_SHUTDOWN: -+ break; -+ case CLOCK_EVT_MODE_PERIODIC: -+ -+ case CLOCK_EVT_MODE_UNUSED: -+ case CLOCK_EVT_MODE_RESUME: -+ -+ default: -+ printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", -+ (int)mode); -+ break; -+ } -+ -+} -+ -+static int timer_set_next_event(unsigned long cycles, -+ struct clock_event_device *unused) -+{ -+ unsigned long stc; -+ do { -+ stc = readl(__io_address(ST_BASE + 0x04)); -+ /* We could take a FIQ here, which may push ST above STC3 */ -+ writel(stc + cycles, __io_address(ST_BASE + 0x18)); -+ } while ((signed long) cycles >= 0 && -+ (signed long) (readl(__io_address(ST_BASE + 0x04)) - stc) -+ >= (signed long) cycles); -+ return 0; -+} -+ -+static struct clock_event_device timer0_clockevent = { -+ .name = "timer0", -+ .shift = 32, -+ .features = CLOCK_EVT_FEAT_ONESHOT, -+ .set_mode = timer_set_mode, -+ .set_next_event = timer_set_next_event, -+}; -+ -+/* -+ * IRQ handler for the timer -+ */ -+static irqreturn_t bcm2709_timer_interrupt(int irq, void *dev_id) -+{ -+ struct clock_event_device *evt = &timer0_clockevent; -+ -+ writel(1 << 3, __io_address(ST_BASE + 0x00)); /* stcs clear timer int */ -+ -+ evt->event_handler(evt); -+ -+ return IRQ_HANDLED; -+} -+ -+static struct irqaction bcm2709_timer_irq = { -+ .name = "BCM2709 Timer Tick", -+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, -+ .handler = bcm2709_timer_interrupt, -+}; -+ -+/* -+ * Set up timer interrupt, and return the current time in seconds. -+ */ -+ -+static struct delay_timer bcm2709_delay_timer = { -+ .read_current_timer = bcm2709_read_current_timer, -+ .freq = STC_FREQ_HZ, -+}; -+ -+static void __init bcm2709_timer_init(void) -+{ -+ /* init high res timer */ -+ bcm2709_clocksource_init(); -+ -+ /* -+ * Make irqs happen for the system timer -+ */ -+ setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); -+ -+ sched_clock_register(bcm2709_read_sched_clock, 32, STC_FREQ_HZ); -+ -+ timer0_clockevent.mult = -+ div_sc(STC_FREQ_HZ, NSEC_PER_SEC, timer0_clockevent.shift); -+ timer0_clockevent.max_delta_ns = -+ clockevent_delta2ns(0xffffffff, &timer0_clockevent); -+ timer0_clockevent.min_delta_ns = -+ clockevent_delta2ns(0xf, &timer0_clockevent); -+ -+ timer0_clockevent.cpumask = cpumask_of(0); -+ clockevents_register_device(&timer0_clockevent); -+ -+ register_current_timer_delay(&bcm2708_delay_timer); -+} -+ -+#else -+ -+static void __init bcm2709_timer_init(void) -+{ -+ extern void dc4_arch_timer_init(void); -+ // timer control -+ writel(0, __io_address(ARM_LOCAL_CONTROL)); -+ // timer pre_scaler -+ writel(0x80000000, __io_address(ARM_LOCAL_PRESCALER)); // 19.2MHz -+ //writel(0x06AAAAAB, __io_address(ARM_LOCAL_PRESCALER)); // 1MHz -+ -+ if (use_dt) -+ { -+ of_clk_init(NULL); -+ clocksource_of_init(); -+ } -+ else -+ dc4_arch_timer_init(); -+} -+ -+#endif -+ -+#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) -+#include -+ -+static struct gpio_led bcm2709_leds[] = { -+ [0] = { -+ .gpio = 16, -+ .name = "led0", -+ .default_trigger = "mmc0", -+ .active_low = 1, -+ }, -+}; -+ -+static struct gpio_led_platform_data bcm2709_led_pdata = { -+ .num_leds = ARRAY_SIZE(bcm2709_leds), -+ .leds = bcm2709_leds, -+}; -+ -+static struct platform_device bcm2709_led_device = { -+ .name = "leds-gpio", -+ .id = -1, -+ .dev = { -+ .platform_data = &bcm2709_led_pdata, -+ }, -+}; -+ -+static void __init bcm2709_init_led(void) -+{ -+ bcm2709_leds[0].gpio = disk_led_gpio; -+ bcm2709_leds[0].active_low = disk_led_active_low; -+ bcm_register_device_dt(&bcm2709_led_device); -+} -+#else -+static inline void bcm2709_init_led(void) -+{ -+} -+#endif -+ -+void __init bcm2709_init_early(void) -+{ -+ /* -+ * Some devices allocate their coherent buffers from atomic -+ * context. Increase size of atomic coherent pool to make sure such -+ * the allocations won't fail. -+ */ -+ init_dma_coherent_pool_size(SZ_4M); -+} -+ -+static void __init board_reserve(void) -+{ -+#if defined(CONFIG_BCM_VC_CMA) -+ vc_cma_reserve(); -+#endif -+} -+ -+ -+#include -+ -+#include -+#include -+#include -+int dc4=0; -+//void dc4_log(unsigned x) { if (dc4) writel((x), __io_address(ST_BASE+10 + raw_smp_processor_id()*4)); } -+void dc4_log_dead(unsigned x) { if (dc4) writel((readl(__io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)) & 0xffff) | ((x)<<16), __io_address(ST_BASE+0x10 + raw_smp_processor_id()*4)); } -+ -+static void bcm2835_send_doorbell(const struct cpumask *mask, unsigned int irq) -+{ -+ int cpu; -+ /* -+ * Ensure that stores to Normal memory are visible to the -+ * other CPUs before issuing the IPI. -+ */ -+ dsb(); -+ -+ /* Convert our logical CPU mask into a physical one. */ -+ for_each_cpu(cpu, mask) -+ { -+ /* submit softirq */ -+ writel(1<%x)\n", __FUNCTION__, (unsigned)virt_to_phys((void *)secondary_startup), (unsigned)__io_address(ST_BASE + 0x10)); -+ printk("[%s] ncores=%d\n", __FUNCTION__, ncores); -+ -+ for (i = 0; i < ncores; i++) { -+ set_cpu_possible(i, true); -+ /* enable IRQ (not FIQ) */ -+ writel(0x1, __io_address(ARM_LOCAL_MAILBOX_INT_CONTROL0 + 0x4 * i)); -+ //writel(0xf, __io_address(ARM_LOCAL_TIMER_INT_CONTROL0 + 0x4 * i)); -+ } -+ set_smp_cross_call(bcm2835_send_doorbell); -+} -+ -+/* -+ * for arch/arm/kernel/smp.c:smp_prepare_cpus(unsigned int max_cpus) -+ */ -+void __init bcm2709_smp_prepare_cpus(unsigned int max_cpus) -+{ -+ //void __iomem *scu_base; -+ -+ printk("[%s] enter\n", __FUNCTION__); -+ //scu_base = scu_base_addr(); -+ //scu_enable(scu_base); -+} -+ -+/* -+ * for linux/arch/arm/kernel/smp.c:secondary_start_kernel(void) -+ */ -+void __cpuinit bcm2709_secondary_init(unsigned int cpu) -+{ -+ printk("[%s] enter cpu:%d\n", __FUNCTION__, cpu); -+ //gic_secondary_init(0); -+} -+ -+/* -+ * for linux/arch/arm/kernel/smp.c:__cpu_up(..) -+ */ -+int __cpuinit bcm2709_boot_secondary(unsigned int cpu, struct task_struct *idle) -+{ -+ void secondary_startup(void); -+ void *mbox_set = __io_address(ARM_LOCAL_MAILBOX3_SET0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); -+ void *mbox_clr = __io_address(ARM_LOCAL_MAILBOX3_CLR0 + 0x10 * MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0)); -+ unsigned secondary_boot = (unsigned)virt_to_phys((void *)secondary_startup); -+ int timeout=20; -+ unsigned t = -1; -+ //printk("[%s] enter cpu:%d (%x->%p) %x\n", __FUNCTION__, cpu, secondary_boot, wake, readl(wake)); -+ -+ dsb(); -+ BUG_ON(readl(mbox_clr) != 0); -+ writel(secondary_boot, mbox_set); -+ -+ while (--timeout > 0) { -+ t = readl(mbox_clr); -+ if (t == 0) break; -+ cpu_relax(); -+ } -+ if (timeout==0) -+ printk("[%s] cpu:%d failed to start (%x)\n", __FUNCTION__, cpu, t); -+ else -+ printk("[%s] cpu:%d started (%x) %d\n", __FUNCTION__, cpu, t, timeout); -+ -+ return 0; -+} -+ -+ -+struct smp_operations bcm2709_smp_ops __initdata = { -+ .smp_init_cpus = bcm2709_smp_init_cpus, -+ .smp_prepare_cpus = bcm2709_smp_prepare_cpus, -+ .smp_secondary_init = bcm2709_secondary_init, -+ .smp_boot_secondary = bcm2709_boot_secondary, -+}; -+ -+static const char * const bcm2709_compat[] = { -+ "brcm,bcm2709", -+ "brcm,bcm2708", /* Could use bcm2708 in a pinch */ -+ NULL -+}; -+ -+MACHINE_START(BCM2709, "BCM2709") -+ /* Maintainer: Broadcom Europe Ltd. */ -+ .smp = smp_ops(bcm2709_smp_ops), -+ .map_io = bcm2709_map_io, -+ .init_irq = bcm2709_init_irq, -+ .init_time = bcm2709_timer_init, -+ .init_machine = bcm2709_init, -+ .init_early = bcm2709_init_early, -+ .reserve = board_reserve, -+ .restart = bcm2709_restart, -+ .dt_compat = bcm2709_compat, -+MACHINE_END -+ -+module_param(boardrev, uint, 0644); -+module_param(serial, uint, 0644); -+module_param(uart_clock, uint, 0644); -+module_param(disk_led_gpio, uint, 0644); -+module_param(disk_led_active_low, uint, 0644); -+module_param(reboot_part, uint, 0644); -+module_param(w1_gpio_pin, uint, 0644); -+module_param(w1_gpio_pullup, uint, 0644); -diff --git a/arch/arm/mach-bcm2709/bcm2709.h b/arch/arm/mach-bcm2709/bcm2709.h -new file mode 100644 -index 0000000..e339a93 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/bcm2709.h -@@ -0,0 +1,49 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/bcm2708.h -+ * -+ * BCM2708 machine support header -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_BCM2708_H -+#define __BCM2708_BCM2708_H -+ -+#include -+ -+extern void __init bcm2708_init(void); -+extern void __init bcm2708_init_irq(void); -+extern void __init bcm2708_map_io(void); -+extern struct sys_timer bcm2708_timer; -+extern unsigned int mmc_status(struct device *dev); -+ -+#define AMBA_DEVICE(name, busid, base, plat) \ -+static struct amba_device name##_device = { \ -+ .dev = { \ -+ .coherent_dma_mask = ~0, \ -+ .init_name = busid, \ -+ .platform_data = plat, \ -+ }, \ -+ .res = { \ -+ .start = base##_BASE, \ -+ .end = (base##_BASE) + SZ_4K - 1,\ -+ .flags = IORESOURCE_MEM, \ -+ }, \ -+ .irq = base##_IRQ, \ -+} -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/clock.c b/arch/arm/mach-bcm2709/clock.c -new file mode 100644 -index 0000000..4fc556e ---- /dev/null -+++ b/arch/arm/mach-bcm2709/clock.c -@@ -0,0 +1,61 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/clock.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "clock.h" -+ -+int clk_enable(struct clk *clk) -+{ -+ return 0; -+} -+EXPORT_SYMBOL(clk_enable); -+ -+void clk_disable(struct clk *clk) -+{ -+} -+EXPORT_SYMBOL(clk_disable); -+ -+unsigned long clk_get_rate(struct clk *clk) -+{ -+ return clk->rate; -+} -+EXPORT_SYMBOL(clk_get_rate); -+ -+long clk_round_rate(struct clk *clk, unsigned long rate) -+{ -+ return clk->rate; -+} -+EXPORT_SYMBOL(clk_round_rate); -+ -+int clk_set_rate(struct clk *clk, unsigned long rate) -+{ -+ return -EIO; -+} -+EXPORT_SYMBOL(clk_set_rate); -diff --git a/arch/arm/mach-bcm2709/clock.h b/arch/arm/mach-bcm2709/clock.h -new file mode 100644 -index 0000000..5f9d725 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/clock.h -@@ -0,0 +1,24 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/clock.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+struct module; -+ -+struct clk { -+ unsigned long rate; -+}; -diff --git a/arch/arm/mach-bcm2709/delay.S b/arch/arm/mach-bcm2709/delay.S -new file mode 100644 -index 0000000..06f4780 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/delay.S -@@ -0,0 +1,21 @@ -+/* -+ * linux/arch/arm/lib/delay.S -+ * -+ * Copyright (C) 1995, 1996 Russell King -+ * -+ * 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 -+#include -+ -+ .text -+.align 3 @ 8 byte alignment seems to be needed to avoid fetching stalls -+@ Delay routine -+ENTRY(bcm2708_delay) -+ subs r0, r0, #1 -+ bhi bcm2708_delay -+ mov pc, lr -+ENDPROC(bcm2708_delay) -diff --git a/arch/arm/mach-bcm2709/dma.c b/arch/arm/mach-bcm2709/dma.c -new file mode 100644 -index 0000000..a5e58d1 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/dma.c -@@ -0,0 +1,409 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/dma.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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 -+#include -+#include -+#include -+ -+#include -+#include -+ -+/*****************************************************************************\ -+ * * -+ * Configuration * -+ * * -+\*****************************************************************************/ -+ -+#define CACHE_LINE_MASK 31 -+#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME -+#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ -+ -+/* valid only for channels 0 - 14, 15 has its own base address */ -+#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */ -+#define BCM2708_DMA_CHANIO(dma_base, n) \ -+ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n))) -+ -+ -+/*****************************************************************************\ -+ * * -+ * DMA Auxilliary Functions * -+ * * -+\*****************************************************************************/ -+ -+/* A DMA buffer on an arbitrary boundary may separate a cache line into a -+ section inside the DMA buffer and another section outside it. -+ Even if we flush DMA buffers from the cache there is always the chance that -+ during a DMA someone will access the part of a cache line that is outside -+ the DMA buffer - which will then bring in unwelcome data. -+ Without being able to dictate our own buffer pools we must insist that -+ DMA buffers consist of a whole number of cache lines. -+*/ -+ -+extern int -+bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) -+{ -+ int i; -+ -+ for (i = 0; i < sg_len; i++) { -+ if (sg_ptr[i].offset & CACHE_LINE_MASK || -+ sg_ptr[i].length & CACHE_LINE_MASK) -+ return 0; -+ } -+ -+ return 1; -+} -+EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); -+ -+extern void -+bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block) -+{ -+ dsb(); /* ARM data synchronization (push) operation */ -+ -+ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); -+ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); -+} -+ -+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) -+{ -+ dsb(); -+ -+ /* ugly busy wait only option for now */ -+ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE) -+ cpu_relax(); -+} -+ -+EXPORT_SYMBOL_GPL(bcm_dma_start); -+ -+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base) -+{ -+ dsb(); -+ -+ return readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_is_busy); -+ -+/* Complete an ongoing DMA (assuming its results are to be ignored) -+ Does nothing if there is no DMA in progress. -+ This routine waits for the current AXI transfer to complete before -+ terminating the current DMA. If the current transfer is hung on a DREQ used -+ by an uncooperative peripheral the AXI transfer may never complete. In this -+ case the routine times out and return a non-zero error code. -+ Use of this routine doesn't guarantee that the ongoing or aborted DMA -+ does not produce an interrupt. -+*/ -+extern int -+bcm_dma_abort(void __iomem *dma_chan_base) -+{ -+ unsigned long int cs; -+ int rc = 0; -+ -+ cs = readl(dma_chan_base + BCM2708_DMA_CS); -+ -+ if (BCM2708_DMA_ACTIVE & cs) { -+ long int timeout = 10000; -+ -+ /* write 0 to the active bit - pause the DMA */ -+ writel(0, dma_chan_base + BCM2708_DMA_CS); -+ -+ /* wait for any current AXI transfer to complete */ -+ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) -+ cs = readl(dma_chan_base + BCM2708_DMA_CS); -+ -+ if (0 != (cs & BCM2708_DMA_ISPAUSED)) { -+ /* we'll un-pause when we set of our next DMA */ -+ rc = -ETIMEDOUT; -+ -+ } else if (BCM2708_DMA_ACTIVE & cs) { -+ /* terminate the control block chain */ -+ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); -+ -+ /* abort the whole DMA */ -+ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, -+ dma_chan_base + BCM2708_DMA_CS); -+ } -+ } -+ -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_abort); -+ -+ -+/***************************************************************************** \ -+ * * -+ * DMA Manager Device Methods * -+ * * -+\*****************************************************************************/ -+ -+struct vc_dmaman { -+ void __iomem *dma_base; -+ u32 chan_available; /* bitmap of available channels */ -+ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ -+}; -+ -+static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, -+ u32 chans_available) -+{ -+ dmaman->dma_base = dma_base; -+ dmaman->chan_available = chans_available; -+ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_BULK_ORD] = 0x01; /* chan 0 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_NORMAL_ORD] = 0xfe; /* chans 1 to 7 */ -+ dmaman->has_feature[BCM_DMA_FEATURE_LITE_ORD] = 0x7f00; /* chans 8 to 14 */ -+} -+ -+static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, -+ unsigned preferred_feature_set) -+{ -+ u32 chans; -+ int feature; -+ -+ chans = dmaman->chan_available; -+ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) -+ /* select the subset of available channels with the desired -+ feature so long as some of the candidate channels have that -+ feature */ -+ if ((preferred_feature_set & (1 << feature)) && -+ (chans & dmaman->has_feature[feature])) -+ chans &= dmaman->has_feature[feature]; -+ -+ if (chans) { -+ int chan = 0; -+ /* return the ordinal of the first channel in the bitmap */ -+ while (chans != 0 && (chans & 1) == 0) { -+ chans >>= 1; -+ chan++; -+ } -+ /* claim the channel */ -+ dmaman->chan_available &= ~(1 << chan); -+ return chan; -+ } else -+ return -ENOMEM; -+} -+ -+static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) -+{ -+ if (chan < 0) -+ return -EINVAL; -+ else if ((1 << chan) & dmaman->chan_available) -+ return -EIDRM; -+ else { -+ dmaman->chan_available |= (1 << chan); -+ return 0; -+ } -+} -+ -+/*****************************************************************************\ -+ * * -+ * DMA IRQs * -+ * * -+\*****************************************************************************/ -+ -+static unsigned char bcm_dma_irqs[] = { -+ IRQ_DMA0, -+ IRQ_DMA1, -+ IRQ_DMA2, -+ IRQ_DMA3, -+ IRQ_DMA4, -+ IRQ_DMA5, -+ IRQ_DMA6, -+ IRQ_DMA7, -+ IRQ_DMA8, -+ IRQ_DMA9, -+ IRQ_DMA10, -+ IRQ_DMA11, -+ IRQ_DMA12 -+}; -+ -+ -+/***************************************************************************** \ -+ * * -+ * DMA Manager Monitor * -+ * * -+\*****************************************************************************/ -+ -+static struct device *dmaman_dev; /* we assume there's only one! */ -+ -+extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, -+ void __iomem **out_dma_base, int *out_dma_irq) -+{ -+ if (!dmaman_dev) -+ return -ENODEV; -+ else { -+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); -+ int rc; -+ -+ device_lock(dmaman_dev); -+ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); -+ if (rc >= 0) { -+ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, -+ rc); -+ *out_dma_irq = bcm_dma_irqs[rc]; -+ } -+ device_unlock(dmaman_dev); -+ -+ return rc; -+ } -+} -+EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); -+ -+extern int bcm_dma_chan_free(int channel) -+{ -+ if (dmaman_dev) { -+ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); -+ int rc; -+ -+ device_lock(dmaman_dev); -+ rc = vc_dmaman_chan_free(dmaman, channel); -+ device_unlock(dmaman_dev); -+ -+ return rc; -+ } else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_dma_chan_free); -+ -+static int dev_dmaman_register(const char *dev_name, struct device *dev) -+{ -+ int rc = dmaman_dev ? -EINVAL : 0; -+ dmaman_dev = dev; -+ return rc; -+} -+ -+static void dev_dmaman_deregister(const char *dev_name, struct device *dev) -+{ -+ dmaman_dev = NULL; -+} -+ -+/*****************************************************************************\ -+ * * -+ * DMA Device * -+ * * -+\*****************************************************************************/ -+ -+static int dmachans = -1; /* module parameter */ -+ -+static int bcm_dmaman_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ struct vc_dmaman *dmaman; -+ struct resource *dma_res = NULL; -+ void __iomem *dma_base = NULL; -+ int have_dma_region = 0; -+ -+ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); -+ if (NULL == dmaman) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "DMA management memory\n"); -+ ret = -ENOMEM; -+ } else { -+ -+ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (dma_res == NULL) { -+ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " -+ "resource\n"); -+ ret = -ENODEV; -+ } else if (!request_mem_region(dma_res->start, -+ resource_size(dma_res), -+ DRIVER_NAME)) { -+ dev_err(&pdev->dev, "cannot obtain DMA region\n"); -+ ret = -EBUSY; -+ } else { -+ have_dma_region = 1; -+ dma_base = ioremap(dma_res->start, -+ resource_size(dma_res)); -+ if (!dma_base) { -+ dev_err(&pdev->dev, "cannot map DMA region\n"); -+ ret = -ENOMEM; -+ } else { -+ /* use module parameter if one was provided */ -+ if (dmachans > 0) -+ vc_dmaman_init(dmaman, dma_base, -+ dmachans); -+ else -+ vc_dmaman_init(dmaman, dma_base, -+ DEFAULT_DMACHAN_BITMAP); -+ -+ platform_set_drvdata(pdev, dmaman); -+ dev_dmaman_register(DRIVER_NAME, &pdev->dev); -+ -+ printk(KERN_INFO DRIVER_NAME ": DMA manager " -+ "at %p\n", dma_base); -+ } -+ } -+ } -+ if (ret != 0) { -+ if (dma_base) -+ iounmap(dma_base); -+ if (dma_res && have_dma_region) -+ release_mem_region(dma_res->start, -+ resource_size(dma_res)); -+ if (dmaman) -+ kfree(dmaman); -+ } -+ return ret; -+} -+ -+static int bcm_dmaman_remove(struct platform_device *pdev) -+{ -+ struct vc_dmaman *dmaman = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); -+ kfree(dmaman); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm_dmaman_driver = { -+ .probe = bcm_dmaman_probe, -+ .remove = bcm_dmaman_remove, -+ -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+/*****************************************************************************\ -+ * * -+ * Driver init/exit * -+ * * -+\*****************************************************************************/ -+ -+static int __init bcm_dmaman_drv_init(void) -+{ -+ int ret; -+ -+ ret = platform_driver_register(&bcm_dmaman_driver); -+ if (ret != 0) { -+ printk(KERN_ERR DRIVER_NAME ": failed to register " -+ "on platform\n"); -+ } -+ -+ return ret; -+} -+ -+static void __exit bcm_dmaman_drv_exit(void) -+{ -+ platform_driver_unregister(&bcm_dmaman_driver); -+} -+ -+module_init(bcm_dmaman_drv_init); -+module_exit(bcm_dmaman_drv_exit); -+ -+module_param(dmachans, int, 0644); -+ -+MODULE_AUTHOR("Gray Girling "); -+MODULE_DESCRIPTION("DMA channel manager driver"); -+MODULE_LICENSE("GPL"); -+ -+MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); -diff --git a/arch/arm/mach-bcm2709/dmaer.c b/arch/arm/mach-bcm2709/dmaer.c -new file mode 100755 -index 0000000..5b0f0ff ---- /dev/null -+++ b/arch/arm/mach-bcm2709/dmaer.c -@@ -0,0 +1,886 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#ifdef ECLIPSE_IGNORE -+ -+#define __user -+#define __init -+#define __exit -+#define __iomem -+#define KERN_DEBUG -+#define KERN_ERR -+#define KERN_WARNING -+#define KERN_INFO -+#define _IOWR(a, b, c) b -+#define _IOW(a, b, c) b -+#define _IO(a, b) b -+ -+#endif -+ -+//#define inline -+ -+#define PRINTK(args...) printk(args) -+//#define PRINTK_VERBOSE(args...) printk(args) -+//#define PRINTK(args...) -+#define PRINTK_VERBOSE(args...) -+ -+/***** TYPES ****/ -+#define PAGES_PER_LIST 500 -+struct PageList -+{ -+ struct page *m_pPages[PAGES_PER_LIST]; -+ unsigned int m_used; -+ struct PageList *m_pNext; -+}; -+ -+struct VmaPageList -+{ -+ //each vma has a linked list of pages associated with it -+ struct PageList *m_pPageHead; -+ struct PageList *m_pPageTail; -+ unsigned int m_refCount; -+}; -+ -+struct DmaControlBlock -+{ -+ unsigned int m_transferInfo; -+ void __user *m_pSourceAddr; -+ void __user *m_pDestAddr; -+ unsigned int m_xferLen; -+ unsigned int m_tdStride; -+ struct DmaControlBlock *m_pNext; -+ unsigned int m_blank1, m_blank2; -+}; -+ -+/***** DEFINES ******/ -+//magic number defining the module -+#define DMA_MAGIC 0xdd -+ -+//do user virtual to physical translation of the CB chain -+#define DMA_PREPARE _IOWR(DMA_MAGIC, 0, struct DmaControlBlock *) -+ -+//kick the pre-prepared CB chain -+#define DMA_KICK _IOW(DMA_MAGIC, 1, struct DmaControlBlock *) -+ -+//prepare it, kick it, wait for it -+#define DMA_PREPARE_KICK_WAIT _IOWR(DMA_MAGIC, 2, struct DmaControlBlock *) -+ -+//prepare it, kick it, don't wait for it -+#define DMA_PREPARE_KICK _IOWR(DMA_MAGIC, 3, struct DmaControlBlock *) -+ -+//not currently implemented -+#define DMA_WAIT_ONE _IO(DMA_MAGIC, 4, struct DmaControlBlock *) -+ -+//wait on all kicked CB chains -+#define DMA_WAIT_ALL _IO(DMA_MAGIC, 5) -+ -+//in order to discover the largest AXI burst that should be programmed into the transfer params -+#define DMA_MAX_BURST _IO(DMA_MAGIC, 6) -+ -+//set the address range through which the user address is assumed to already by a physical address -+#define DMA_SET_MIN_PHYS _IOW(DMA_MAGIC, 7, unsigned long) -+#define DMA_SET_MAX_PHYS _IOW(DMA_MAGIC, 8, unsigned long) -+#define DMA_SET_PHYS_OFFSET _IOW(DMA_MAGIC, 9, unsigned long) -+ -+//used to define the size for the CMA-based allocation *in pages*, can only be done once once the file is opened -+#define DMA_CMA_SET_SIZE _IOW(DMA_MAGIC, 10, unsigned long) -+ -+//used to get the version of the module, to test for a capability -+#define DMA_GET_VERSION _IO(DMA_MAGIC, 99) -+ -+#define VERSION_NUMBER 1 -+ -+#define VIRT_TO_BUS_CACHE_SIZE 8 -+ -+/***** FILE OPS *****/ -+static int Open(struct inode *pInode, struct file *pFile); -+static int Release(struct inode *pInode, struct file *pFile); -+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg); -+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp); -+static int Mmap(struct file *pFile, struct vm_area_struct *pVma); -+ -+/***** VMA OPS ****/ -+static void VmaOpen4k(struct vm_area_struct *pVma); -+static void VmaClose4k(struct vm_area_struct *pVma); -+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf); -+ -+/**** DMA PROTOTYPES */ -+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError); -+static int DmaKick(struct DmaControlBlock __user *pUserCB); -+static void DmaWaitAll(void); -+ -+/**** GENERIC ****/ -+static int __init dmaer_init(void); -+static void __exit dmaer_exit(void); -+ -+/*** OPS ***/ -+static struct vm_operations_struct g_vmOps4k = { -+ .open = VmaOpen4k, -+ .close = VmaClose4k, -+ .fault = VmaFault4k, -+}; -+ -+static struct file_operations g_fOps = { -+ .owner = THIS_MODULE, -+ .llseek = 0, -+ .read = Read, -+ .write = 0, -+ .unlocked_ioctl = Ioctl, -+ .open = Open, -+ .release = Release, -+ .mmap = Mmap, -+}; -+ -+/***** GLOBALS ******/ -+static dev_t g_majorMinor; -+ -+//tracking usage of the two files -+static atomic_t g_oneLock4k = ATOMIC_INIT(1); -+ -+//device operations -+static struct cdev g_cDev; -+static int g_trackedPages = 0; -+ -+//dma control -+static unsigned int *g_pDmaChanBase; -+static int g_dmaIrq; -+static int g_dmaChan; -+ -+//cma allocation -+static int g_cmaHandle; -+ -+//user virtual to bus address translation acceleration -+static unsigned long g_virtAddr[VIRT_TO_BUS_CACHE_SIZE]; -+static unsigned long g_busAddr[VIRT_TO_BUS_CACHE_SIZE]; -+static unsigned long g_cbVirtAddr; -+static unsigned long g_cbBusAddr; -+static int g_cacheInsertAt; -+static int g_cacheHit, g_cacheMiss; -+ -+//off by default -+static void __user *g_pMinPhys; -+static void __user *g_pMaxPhys; -+static unsigned long g_physOffset; -+ -+/****** CACHE OPERATIONS ********/ -+static inline void FlushAddrCache(void) -+{ -+ int count = 0; -+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) -+ g_virtAddr[count] = 0xffffffff; //never going to match as we always chop the bottom bits anyway -+ -+ g_cbVirtAddr = 0xffffffff; -+ -+ g_cacheInsertAt = 0; -+} -+ -+//translate from a user virtual address to a bus address by mapping the page -+//NB this won't lock a page in memory, so to avoid potential paging issues using kernel logical addresses -+static inline void __iomem *UserVirtualToBus(void __user *pUser) -+{ -+ int mapped; -+ struct page *pPage; -+ void *phys; -+ -+ //map it (requiring that the pointer points to something that does not hang off the page boundary) -+ mapped = get_user_pages(current, current->mm, -+ (unsigned long)pUser, 1, -+ 1, 0, -+ &pPage, -+ 0); -+ -+ if (mapped <= 0) //error -+ return 0; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "user virtual %p arm phys %p bus %p\n", -+ pUser, page_address(pPage), (void __iomem *)__virt_to_bus(page_address(pPage))); -+ -+ //get the arm physical address -+ phys = page_address(pPage) + offset_in_page(pUser); -+ page_cache_release(pPage); -+ -+ //and now the bus address -+ return (void __iomem *)__virt_to_bus(phys); -+} -+ -+static inline void __iomem *UserVirtualToBusViaCbCache(void __user *pUser) -+{ -+ unsigned long virtual_page = (unsigned long)pUser & ~4095; -+ unsigned long page_offset = (unsigned long)pUser & 4095; -+ unsigned long bus_addr; -+ -+ if (g_cbVirtAddr == virtual_page) -+ { -+ bus_addr = g_cbBusAddr + page_offset; -+ g_cacheHit++; -+ return (void __iomem *)bus_addr; -+ } -+ else -+ { -+ bus_addr = (unsigned long)UserVirtualToBus(pUser); -+ -+ if (!bus_addr) -+ return 0; -+ -+ g_cbVirtAddr = virtual_page; -+ g_cbBusAddr = bus_addr & ~4095; -+ g_cacheMiss++; -+ -+ return (void __iomem *)bus_addr; -+ } -+} -+ -+//do the same as above, by query our virt->bus cache -+static inline void __iomem *UserVirtualToBusViaCache(void __user *pUser) -+{ -+ int count; -+ //get the page and its offset -+ unsigned long virtual_page = (unsigned long)pUser & ~4095; -+ unsigned long page_offset = (unsigned long)pUser & 4095; -+ unsigned long bus_addr; -+ -+ if (pUser >= g_pMinPhys && pUser < g_pMaxPhys) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "user->phys passthrough on %p\n", pUser); -+ return (void __iomem *)((unsigned long)pUser + g_physOffset); -+ } -+ -+ //check the cache for our entry -+ for (count = 0; count < VIRT_TO_BUS_CACHE_SIZE; count++) -+ if (g_virtAddr[count] == virtual_page) -+ { -+ bus_addr = g_busAddr[count] + page_offset; -+ g_cacheHit++; -+ return (void __iomem *)bus_addr; -+ } -+ -+ //not found, look up manually and then insert its page address -+ bus_addr = (unsigned long)UserVirtualToBus(pUser); -+ -+ if (!bus_addr) -+ return 0; -+ -+ g_virtAddr[g_cacheInsertAt] = virtual_page; -+ g_busAddr[g_cacheInsertAt] = bus_addr & ~4095; -+ -+ //round robin -+ g_cacheInsertAt++; -+ if (g_cacheInsertAt == VIRT_TO_BUS_CACHE_SIZE) -+ g_cacheInsertAt = 0; -+ -+ g_cacheMiss++; -+ -+ return (void __iomem *)bus_addr; -+} -+ -+/***** FILE OPERATIONS ****/ -+static int Open(struct inode *pInode, struct file *pFile) -+{ -+ PRINTK(KERN_DEBUG "file opening: %d/%d\n", imajor(pInode), iminor(pInode)); -+ -+ //check which device we are -+ if (iminor(pInode) == 0) //4k -+ { -+ //only one at a time -+ if (!atomic_dec_and_test(&g_oneLock4k)) -+ { -+ atomic_inc(&g_oneLock4k); -+ return -EBUSY; -+ } -+ } -+ else -+ return -EINVAL; -+ -+ //todo there will be trouble if two different processes open the files -+ -+ //reset after any file is opened -+ g_pMinPhys = (void __user *)-1; -+ g_pMaxPhys = (void __user *)0; -+ g_physOffset = 0; -+ g_cmaHandle = 0; -+ -+ return 0; -+} -+ -+static int Release(struct inode *pInode, struct file *pFile) -+{ -+ PRINTK(KERN_DEBUG "file closing, %d pages tracked\n", g_trackedPages); -+ if (g_trackedPages) -+ PRINTK(KERN_ERR "we\'re leaking memory!\n"); -+ -+ //wait for any dmas to finish -+ DmaWaitAll(); -+ -+ //free this memory on the application closing the file or it crashing (implicitly closing the file) -+ if (g_cmaHandle) -+ { -+ PRINTK(KERN_DEBUG "unlocking vc memory\n"); -+ if (UnlockVcMemory(g_cmaHandle)) -+ PRINTK(KERN_ERR "uh-oh, unable to unlock vc memory!\n"); -+ PRINTK(KERN_DEBUG "releasing vc memory\n"); -+ if (ReleaseVcMemory(g_cmaHandle)) -+ PRINTK(KERN_ERR "uh-oh, unable to release vc memory!\n"); -+ } -+ -+ if (iminor(pInode) == 0) -+ atomic_inc(&g_oneLock4k); -+ else -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static struct DmaControlBlock __user *DmaPrepare(struct DmaControlBlock __user *pUserCB, int *pError) -+{ -+ struct DmaControlBlock kernCB; -+ struct DmaControlBlock __user *pUNext; -+ void __iomem *pSourceBus, __iomem *pDestBus; -+ -+ //get the control block into kernel memory so we can work on it -+ if (copy_from_user(&kernCB, pUserCB, sizeof(struct DmaControlBlock)) != 0) -+ { -+ PRINTK(KERN_ERR "copy_from_user failed for user cb %p\n", pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ if (kernCB.m_pSourceAddr == 0 || kernCB.m_pDestAddr == 0) -+ { -+ PRINTK(KERN_ERR "faulty source (%p) dest (%p) addresses for user cb %p\n", -+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ pSourceBus = UserVirtualToBusViaCache(kernCB.m_pSourceAddr); -+ pDestBus = UserVirtualToBusViaCache(kernCB.m_pDestAddr); -+ -+ if (!pSourceBus || !pDestBus) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for source/dest %p/%p->%p/%p\n", -+ kernCB.m_pSourceAddr, kernCB.m_pDestAddr, -+ pSourceBus, pDestBus); -+ *pError = 1; -+ return 0; -+ } -+ -+ //update the user structure with the new bus addresses -+ kernCB.m_pSourceAddr = pSourceBus; -+ kernCB.m_pDestAddr = pDestBus; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "final source %p dest %p\n", kernCB.m_pSourceAddr, kernCB.m_pDestAddr); -+ -+ //sort out the bus address for the next block -+ pUNext = kernCB.m_pNext; -+ -+ if (kernCB.m_pNext) -+ { -+ void __iomem *pNextBus; -+ pNextBus = UserVirtualToBusViaCbCache(kernCB.m_pNext); -+ -+ if (!pNextBus) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for m_pNext\n"); -+ *pError = 1; -+ return 0; -+ } -+ -+ //update the pointer with the bus address -+ kernCB.m_pNext = pNextBus; -+ } -+ -+ //write it back to user space -+ if (copy_to_user(pUserCB, &kernCB, sizeof(struct DmaControlBlock)) != 0) -+ { -+ PRINTK(KERN_ERR "copy_to_user failed for cb %p\n", pUserCB); -+ *pError = 1; -+ return 0; -+ } -+ -+ __cpuc_flush_dcache_area(pUserCB, 32); -+ -+ *pError = 0; -+ return pUNext; -+} -+ -+static int DmaKick(struct DmaControlBlock __user *pUserCB) -+{ -+ void __iomem *pBusCB; -+ -+ pBusCB = UserVirtualToBusViaCbCache(pUserCB); -+ if (!pBusCB) -+ { -+ PRINTK(KERN_ERR "virtual to bus translation failure for cb\n"); -+ return 1; -+ } -+ -+ //flush_cache_all(); -+ -+ bcm_dma_start(g_pDmaChanBase, (dma_addr_t)pBusCB); -+ -+ return 0; -+} -+ -+static void DmaWaitAll(void) -+{ -+ int counter = 0; -+ volatile int inner_count; -+ volatile unsigned int cs; -+ unsigned long time_before, time_after; -+ -+ time_before = jiffies; -+ //bcm_dma_wait_idle(g_pDmaChanBase); -+ dsb(); -+ -+ cs = readl(g_pDmaChanBase); -+ -+ while ((cs & 1) == 1) -+ { -+ cs = readl(g_pDmaChanBase); -+ counter++; -+ -+ for (inner_count = 0; inner_count < 32; inner_count++); -+ -+ asm volatile ("MCR p15,0,r0,c7,c0,4 \n"); -+ //cpu_do_idle(); -+ if (counter >= 1000000) -+ { -+ PRINTK(KERN_WARNING "DMA failed to finish in a timely fashion\n"); -+ break; -+ } -+ } -+ time_after = jiffies; -+ PRINTK_VERBOSE(KERN_DEBUG "done, counter %d, cs %08x", counter, cs); -+ PRINTK_VERBOSE(KERN_DEBUG "took %ld jiffies, %d HZ\n", time_after - time_before, HZ); -+} -+ -+static long Ioctl(struct file *pFile, unsigned int cmd, unsigned long arg) -+{ -+ int error = 0; -+ PRINTK_VERBOSE(KERN_DEBUG "ioctl cmd %x arg %lx\n", cmd, arg); -+ -+ switch (cmd) -+ { -+ case DMA_PREPARE: -+ case DMA_PREPARE_KICK: -+ case DMA_PREPARE_KICK_WAIT: -+ { -+ struct DmaControlBlock __user *pUCB = (struct DmaControlBlock *)arg; -+ int steps = 0; -+ unsigned long start_time = jiffies; -+ (void)start_time; -+ -+ //flush our address cache -+ FlushAddrCache(); -+ -+ PRINTK_VERBOSE(KERN_DEBUG "dma prepare\n"); -+ -+ //do virtual to bus translation for each entry -+ do -+ { -+ pUCB = DmaPrepare(pUCB, &error); -+ } while (error == 0 && ++steps && pUCB); -+ PRINTK_VERBOSE(KERN_DEBUG "prepare done in %d steps, %ld\n", steps, jiffies - start_time); -+ -+ //carry straight on if we want to kick too -+ if (cmd == DMA_PREPARE || error) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "falling out\n"); -+ return error ? -EINVAL : 0; -+ } -+ } -+ case DMA_KICK: -+ PRINTK_VERBOSE(KERN_DEBUG "dma begin\n"); -+ -+ if (cmd == DMA_KICK) -+ FlushAddrCache(); -+ -+ DmaKick((struct DmaControlBlock __user *)arg); -+ -+ if (cmd != DMA_PREPARE_KICK_WAIT) -+ break; -+/* case DMA_WAIT_ONE: -+ //PRINTK(KERN_DEBUG "dma wait one\n"); -+ break;*/ -+ case DMA_WAIT_ALL: -+ //PRINTK(KERN_DEBUG "dma wait all\n"); -+ DmaWaitAll(); -+ break; -+ case DMA_MAX_BURST: -+ if (g_dmaChan == 0) -+ return 10; -+ else -+ return 5; -+ case DMA_SET_MIN_PHYS: -+ g_pMinPhys = (void __user *)arg; -+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); -+ break; -+ case DMA_SET_MAX_PHYS: -+ g_pMaxPhys = (void __user *)arg; -+ PRINTK(KERN_DEBUG "min/max user/phys bypass set to %p %p\n", g_pMinPhys, g_pMaxPhys); -+ break; -+ case DMA_SET_PHYS_OFFSET: -+ g_physOffset = arg; -+ PRINTK(KERN_DEBUG "user/phys bypass offset set to %ld\n", g_physOffset); -+ break; -+ case DMA_CMA_SET_SIZE: -+ { -+ unsigned int pBusAddr; -+ -+ if (g_cmaHandle) -+ { -+ PRINTK(KERN_ERR "memory has already been allocated (handle %d)\n", g_cmaHandle); -+ return -EINVAL; -+ } -+ -+ PRINTK(KERN_INFO "allocating %ld bytes of VC memory\n", arg * 4096); -+ -+ //get the memory -+ if (AllocateVcMemory(&g_cmaHandle, arg * 4096, 4096, MEM_FLAG_L1_NONALLOCATING | MEM_FLAG_NO_INIT | MEM_FLAG_HINT_PERMALOCK)) -+ { -+ PRINTK(KERN_ERR "failed to allocate %ld bytes of VC memory\n", arg * 4096); -+ g_cmaHandle = 0; -+ return -EINVAL; -+ } -+ -+ //get an address for it -+ PRINTK(KERN_INFO "trying to map VC memory\n"); -+ -+ if (LockVcMemory(&pBusAddr, g_cmaHandle)) -+ { -+ PRINTK(KERN_ERR "failed to map CMA handle %d, releasing memory\n", g_cmaHandle); -+ ReleaseVcMemory(g_cmaHandle); -+ g_cmaHandle = 0; -+ } -+ -+ PRINTK(KERN_INFO "bus address for CMA memory is %x\n", pBusAddr); -+ return pBusAddr; -+ } -+ case DMA_GET_VERSION: -+ PRINTK(KERN_DEBUG "returning version number, %d\n", VERSION_NUMBER); -+ return VERSION_NUMBER; -+ default: -+ PRINTK(KERN_DEBUG "unknown ioctl: %d\n", cmd); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static ssize_t Read(struct file *pFile, char __user *pUser, size_t count, loff_t *offp) -+{ -+ return -EIO; -+} -+ -+static int Mmap(struct file *pFile, struct vm_area_struct *pVma) -+{ -+ struct PageList *pPages; -+ struct VmaPageList *pVmaList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "MMAP vma %p, length %ld (%s %d)\n", -+ pVma, pVma->vm_end - pVma->vm_start, -+ current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_DEBUG "MMAP %p %d (tracked %d)\n", pVma, current->pid, g_trackedPages); -+ -+ //make a new page list -+ pPages = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); -+ if (!pPages) -+ { -+ PRINTK(KERN_ERR "couldn\'t allocate a new page list (%s %d)\n", -+ current->comm, current->pid); -+ return -ENOMEM; -+ } -+ -+ //clear the page list -+ pPages->m_used = 0; -+ pPages->m_pNext = 0; -+ -+ //insert our vma and new page list somewhere -+ if (!pVma->vm_private_data) -+ { -+ struct VmaPageList *pList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "new vma list, making new one (%s %d)\n", -+ current->comm, current->pid); -+ -+ //make a new vma list -+ pList = (struct VmaPageList *)kmalloc(sizeof(struct VmaPageList), GFP_KERNEL); -+ if (!pList) -+ { -+ PRINTK(KERN_ERR "couldn\'t allocate vma page list (%s %d)\n", -+ current->comm, current->pid); -+ kfree(pPages); -+ return -ENOMEM; -+ } -+ -+ //clear this list -+ pVma->vm_private_data = (void *)pList; -+ pList->m_refCount = 0; -+ } -+ -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ //add it to the vma list -+ pVmaList->m_pPageHead = pPages; -+ pVmaList->m_pPageTail = pPages; -+ -+ pVma->vm_ops = &g_vmOps4k; -+ pVma->vm_flags |= VM_IO; -+ -+ VmaOpen4k(pVma); -+ -+ return 0; -+} -+ -+/****** VMA OPERATIONS ******/ -+ -+static void VmaOpen4k(struct vm_area_struct *pVma) -+{ -+ struct VmaPageList *pVmaList; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "vma open %p private %p (%s %d), %d live pages\n", pVma, pVma->vm_private_data, current->comm, current->pid, g_trackedPages); -+ PRINTK_VERBOSE(KERN_DEBUG "OPEN %p %d %ld pages (tracked pages %d)\n", -+ pVma, current->pid, (pVma->vm_end - pVma->vm_start) >> 12, -+ g_trackedPages); -+ -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ if (pVmaList) -+ { -+ pVmaList->m_refCount++; -+ PRINTK_VERBOSE(KERN_DEBUG "ref count is now %d\n", pVmaList->m_refCount); -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "err, open but no vma page list\n"); -+ } -+} -+ -+static void VmaClose4k(struct vm_area_struct *pVma) -+{ -+ struct VmaPageList *pVmaList; -+ int freed = 0; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "vma close %p private %p (%s %d)\n", pVma, pVma->vm_private_data, current->comm, current->pid); -+ -+ //wait for any dmas to finish -+ DmaWaitAll(); -+ -+ //find our vma in the list -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ //may be a fork -+ if (pVmaList) -+ { -+ struct PageList *pPages; -+ -+ pVmaList->m_refCount--; -+ -+ if (pVmaList->m_refCount == 0) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "found vma, freeing pages (%s %d)\n", -+ current->comm, current->pid); -+ -+ pPages = pVmaList->m_pPageHead; -+ -+ if (!pPages) -+ { -+ PRINTK(KERN_ERR "no page list (%s %d)!\n", -+ current->comm, current->pid); -+ return; -+ } -+ -+ while (pPages) -+ { -+ struct PageList *next; -+ int count; -+ -+ PRINTK_VERBOSE(KERN_DEBUG "page list (%s %d)\n", -+ current->comm, current->pid); -+ -+ next = pPages->m_pNext; -+ for (count = 0; count < pPages->m_used; count++) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "freeing page %p (%s %d)\n", -+ pPages->m_pPages[count], -+ current->comm, current->pid); -+ __free_pages(pPages->m_pPages[count], 0); -+ g_trackedPages--; -+ freed++; -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "freeing page list (%s %d)\n", -+ current->comm, current->pid); -+ kfree(pPages); -+ pPages = next; -+ } -+ -+ //remove our vma from the list -+ kfree(pVmaList); -+ pVma->vm_private_data = 0; -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "ref count is %d, not closing\n", pVmaList->m_refCount); -+ } -+ } -+ else -+ { -+ PRINTK_VERBOSE(KERN_ERR "uh-oh, vma %p not found (%s %d)!\n", pVma, current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_ERR "CLOSE ERR\n"); -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "CLOSE %p %d %d pages (tracked pages %d)", -+ pVma, current->pid, freed, g_trackedPages); -+ -+ PRINTK_VERBOSE(KERN_DEBUG "%d pages open\n", g_trackedPages); -+} -+ -+static int VmaFault4k(struct vm_area_struct *pVma, struct vm_fault *pVmf) -+{ -+ PRINTK_VERBOSE(KERN_DEBUG "vma fault for vma %p private %p at offset %ld (%s %d)\n", pVma, pVma->vm_private_data, pVmf->pgoff, -+ current->comm, current->pid); -+ PRINTK_VERBOSE(KERN_DEBUG "FAULT\n"); -+ pVmf->page = alloc_page(GFP_KERNEL); -+ -+ if (pVmf->page) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "alloc page virtual %p\n", page_address(pVmf->page)); -+ } -+ -+ if (!pVmf->page) -+ { -+ PRINTK(KERN_ERR "vma fault oom (%s %d)\n", current->comm, current->pid); -+ return VM_FAULT_OOM; -+ } -+ else -+ { -+ struct VmaPageList *pVmaList; -+ -+ get_page(pVmf->page); -+ g_trackedPages++; -+ -+ //find our vma in the list -+ pVmaList = (struct VmaPageList *)pVma->vm_private_data; -+ -+ if (pVmaList) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "vma found (%s %d)\n", current->comm, current->pid); -+ -+ if (pVmaList->m_pPageTail->m_used == PAGES_PER_LIST) -+ { -+ PRINTK_VERBOSE(KERN_DEBUG "making new page list (%s %d)\n", current->comm, current->pid); -+ //making a new page list -+ pVmaList->m_pPageTail->m_pNext = (struct PageList *)kmalloc(sizeof(struct PageList), GFP_KERNEL); -+ if (!pVmaList->m_pPageTail->m_pNext) -+ return -ENOMEM; -+ -+ //update the tail pointer -+ pVmaList->m_pPageTail = pVmaList->m_pPageTail->m_pNext; -+ pVmaList->m_pPageTail->m_used = 0; -+ pVmaList->m_pPageTail->m_pNext = 0; -+ } -+ -+ PRINTK_VERBOSE(KERN_DEBUG "adding page to list (%s %d)\n", current->comm, current->pid); -+ -+ pVmaList->m_pPageTail->m_pPages[pVmaList->m_pPageTail->m_used] = pVmf->page; -+ pVmaList->m_pPageTail->m_used++; -+ } -+ else -+ PRINTK(KERN_ERR "returned page for vma we don\'t know %p (%s %d)\n", pVma, current->comm, current->pid); -+ -+ return 0; -+ } -+} -+ -+/****** GENERIC FUNCTIONS ******/ -+static int __init dmaer_init(void) -+{ -+ int result = alloc_chrdev_region(&g_majorMinor, 0, 1, "dmaer"); -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "unable to get major device number\n"); -+ return result; -+ } -+ else -+ PRINTK(KERN_DEBUG "major device number %d\n", MAJOR(g_majorMinor)); -+ -+ PRINTK(KERN_DEBUG "vma list size %d, page list size %d, page size %ld\n", -+ sizeof(struct VmaPageList), sizeof(struct PageList), PAGE_SIZE); -+ -+ //get a dma channel to work with -+ result = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, (void **)&g_pDmaChanBase, &g_dmaIrq); -+ -+ //uncomment to force to channel 0 -+ //result = 0; -+ //g_pDmaChanBase = 0xce808000; -+ -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "failed to allocate dma channel\n"); -+ cdev_del(&g_cDev); -+ unregister_chrdev_region(g_majorMinor, 1); -+ } -+ -+ //reset the channel -+ PRINTK(KERN_DEBUG "allocated dma channel %d (%p), initial state %08x\n", result, g_pDmaChanBase, *g_pDmaChanBase); -+ *g_pDmaChanBase = 1 << 31; -+ PRINTK(KERN_DEBUG "post-reset %08x\n", *g_pDmaChanBase); -+ -+ g_dmaChan = result; -+ -+ //clear the cache stats -+ g_cacheHit = 0; -+ g_cacheMiss = 0; -+ -+ //register our device - after this we are go go go -+ cdev_init(&g_cDev, &g_fOps); -+ g_cDev.owner = THIS_MODULE; -+ g_cDev.ops = &g_fOps; -+ -+ result = cdev_add(&g_cDev, g_majorMinor, 1); -+ if (result < 0) -+ { -+ PRINTK(KERN_ERR "failed to add character device\n"); -+ unregister_chrdev_region(g_majorMinor, 1); -+ bcm_dma_chan_free(g_dmaChan); -+ return result; -+ } -+ -+ return 0; -+} -+ -+static void __exit dmaer_exit(void) -+{ -+ PRINTK(KERN_INFO "closing dmaer device, cache stats: %d hits %d misses\n", g_cacheHit, g_cacheMiss); -+ //unregister the device -+ cdev_del(&g_cDev); -+ unregister_chrdev_region(g_majorMinor, 1); -+ //free the dma channel -+ bcm_dma_chan_free(g_dmaChan); -+} -+ -+MODULE_LICENSE("Dual BSD/GPL"); -+MODULE_AUTHOR("Simon Hall"); -+module_init(dmaer_init); -+module_exit(dmaer_exit); -diff --git a/arch/arm/mach-bcm2709/include/mach/arm_control.h b/arch/arm/mach-bcm2709/include/mach/arm_control.h -new file mode 100644 -index 0000000..e346caf ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/arm_control.h -@@ -0,0 +1,493 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/arm_control.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __BCM2708_ARM_CONTROL_H -+#define __BCM2708_ARM_CONTROL_H -+ -+/* -+ * Definitions and addresses for the ARM CONTROL logic -+ * This file is manually generated. -+ */ -+ -+#define ARM_BASE 0x7E00B000 -+ -+/* Basic configuration */ -+#define ARM_CONTROL0 HW_REGISTER_RW(ARM_BASE+0x000) -+#define ARM_C0_SIZ128M 0x00000000 -+#define ARM_C0_SIZ256M 0x00000001 -+#define ARM_C0_SIZ512M 0x00000002 -+#define ARM_C0_SIZ1G 0x00000003 -+#define ARM_C0_BRESP0 0x00000000 -+#define ARM_C0_BRESP1 0x00000004 -+#define ARM_C0_BRESP2 0x00000008 -+#define ARM_C0_BOOTHI 0x00000010 -+#define ARM_C0_UNUSED05 0x00000020 /* free */ -+#define ARM_C0_FULLPERI 0x00000040 -+#define ARM_C0_UNUSED78 0x00000180 /* free */ -+#define ARM_C0_JTAGMASK 0x00000E00 -+#define ARM_C0_JTAGOFF 0x00000000 -+#define ARM_C0_JTAGBASH 0x00000800 /* Debug on GPIO off */ -+#define ARM_C0_JTAGGPIO 0x00000C00 /* Debug on GPIO on */ -+#define ARM_C0_APROTMSK 0x0000F000 -+#define ARM_C0_DBG0SYNC 0x00010000 /* VPU0 halt sync */ -+#define ARM_C0_DBG1SYNC 0x00020000 /* VPU1 halt sync */ -+#define ARM_C0_SWDBGREQ 0x00040000 /* HW debug request */ -+#define ARM_C0_PASSHALT 0x00080000 /* ARM halt passed to debugger */ -+#define ARM_C0_PRIO_PER 0x00F00000 /* per priority mask */ -+#define ARM_C0_PRIO_L2 0x0F000000 -+#define ARM_C0_PRIO_UC 0xF0000000 -+ -+#define ARM_C0_APROTPASS 0x0000A000 /* Translate 1:1 */ -+#define ARM_C0_APROTUSER 0x00000000 /* Only user mode */ -+#define ARM_C0_APROTSYST 0x0000F000 /* Only system mode */ -+ -+ -+#define ARM_CONTROL1 HW_REGISTER_RW(ARM_BASE+0x440) -+#define ARM_C1_TIMER 0x00000001 /* re-route timer IRQ to VC */ -+#define ARM_C1_MAIL 0x00000002 /* re-route Mail IRQ to VC */ -+#define ARM_C1_BELL0 0x00000004 /* re-route Doorbell 0 to VC */ -+#define ARM_C1_BELL1 0x00000008 /* re-route Doorbell 1 to VC */ -+#define ARM_C1_PERSON 0x00000100 /* peripherals on */ -+#define ARM_C1_REQSTOP 0x00000200 /* ASYNC bridge request stop */ -+ -+#define ARM_STATUS HW_REGISTER_RW(ARM_BASE+0x444) -+#define ARM_S_ACKSTOP 0x80000000 /* Bridge stopped */ -+#define ARM_S_READPEND 0x000003FF /* pending reads counter */ -+#define ARM_S_WRITPEND 0x000FFC00 /* pending writes counter */ -+ -+#define ARM_ERRHALT HW_REGISTER_RW(ARM_BASE+0x448) -+#define ARM_EH_PERIBURST 0x00000001 /* Burst write seen on peri bus */ -+#define ARM_EH_ILLADDRS1 0x00000002 /* Address bits 25-27 error */ -+#define ARM_EH_ILLADDRS2 0x00000004 /* Address bits 31-28 error */ -+#define ARM_EH_VPU0HALT 0x00000008 /* VPU0 halted & in debug mode */ -+#define ARM_EH_VPU1HALT 0x00000010 /* VPU1 halted & in debug mode */ -+#define ARM_EH_ARMHALT 0x00000020 /* ARM in halted debug mode */ -+ -+#define ARM_ID_SECURE HW_REGISTER_RW(ARM_BASE+0x00C) -+#define ARM_ID HW_REGISTER_RW(ARM_BASE+0x44C) -+#define ARM_IDVAL 0x364D5241 -+ -+/* Translation memory */ -+#define ARM_TRANSLATE HW_REGISTER_RW(ARM_BASE+0x100) -+/* 32 locations: 0x100.. 0x17F */ -+/* 32 spare means we CAN go to 64 pages.... */ -+ -+ -+/* Interrupts */ -+#define ARM_IRQ_PEND0 HW_REGISTER_RW(ARM_BASE+0x200) /* Top IRQ bits */ -+#define ARM_I0_TIMER 0x00000001 /* timer IRQ */ -+#define ARM_I0_MAIL 0x00000002 /* Mail IRQ */ -+#define ARM_I0_BELL0 0x00000004 /* Doorbell 0 */ -+#define ARM_I0_BELL1 0x00000008 /* Doorbell 1 */ -+#define ARM_I0_BANK1 0x00000100 /* Bank1 IRQ */ -+#define ARM_I0_BANK2 0x00000200 /* Bank2 IRQ */ -+ -+#define ARM_IRQ_PEND1 HW_REGISTER_RW(ARM_BASE+0x204) /* All bank1 IRQ bits */ -+/* todo: all I1_interrupt sources */ -+#define ARM_IRQ_PEND2 HW_REGISTER_RW(ARM_BASE+0x208) /* All bank2 IRQ bits */ -+/* todo: all I2_interrupt sources */ -+ -+#define ARM_IRQ_FAST HW_REGISTER_RW(ARM_BASE+0x20C) /* FIQ control */ -+#define ARM_IF_INDEX 0x0000007F /* FIQ select */ -+#define ARM_IF_ENABLE 0x00000080 /* FIQ enable */ -+#define ARM_IF_VCMASK 0x0000003F /* FIQ = (index from VC source) */ -+#define ARM_IF_TIMER 0x00000040 /* FIQ = ARM timer */ -+#define ARM_IF_MAIL 0x00000041 /* FIQ = ARM Mail */ -+#define ARM_IF_BELL0 0x00000042 /* FIQ = ARM Doorbell 0 */ -+#define ARM_IF_BELL1 0x00000043 /* FIQ = ARM Doorbell 1 */ -+#define ARM_IF_VP0HALT 0x00000044 /* FIQ = VPU0 Halt seen */ -+#define ARM_IF_VP1HALT 0x00000045 /* FIQ = VPU1 Halt seen */ -+#define ARM_IF_ILLEGAL 0x00000046 /* FIQ = Illegal access seen */ -+ -+#define ARM_IRQ_ENBL1 HW_REGISTER_RW(ARM_BASE+0x210) /* Bank1 enable bits */ -+#define ARM_IRQ_ENBL2 HW_REGISTER_RW(ARM_BASE+0x214) /* Bank2 enable bits */ -+#define ARM_IRQ_ENBL3 HW_REGISTER_RW(ARM_BASE+0x218) /* ARM irqs enable bits */ -+#define ARM_IRQ_DIBL1 HW_REGISTER_RW(ARM_BASE+0x21C) /* Bank1 disable bits */ -+#define ARM_IRQ_DIBL2 HW_REGISTER_RW(ARM_BASE+0x220) /* Bank2 disable bits */ -+#define ARM_IRQ_DIBL3 HW_REGISTER_RW(ARM_BASE+0x224) /* ARM irqs disable bits */ -+#define ARM_IE_TIMER 0x00000001 /* Timer IRQ */ -+#define ARM_IE_MAIL 0x00000002 /* Mail IRQ */ -+#define ARM_IE_BELL0 0x00000004 /* Doorbell 0 */ -+#define ARM_IE_BELL1 0x00000008 /* Doorbell 1 */ -+#define ARM_IE_VP0HALT 0x00000010 /* VPU0 Halt */ -+#define ARM_IE_VP1HALT 0x00000020 /* VPU1 Halt */ -+#define ARM_IE_ILLEGAL 0x00000040 /* Illegal access seen */ -+ -+/* Timer */ -+/* For reg. fields see sp804 spec. */ -+#define ARM_T_LOAD HW_REGISTER_RW(ARM_BASE+0x400) -+#define ARM_T_VALUE HW_REGISTER_RW(ARM_BASE+0x404) -+#define ARM_T_CONTROL HW_REGISTER_RW(ARM_BASE+0x408) -+#define ARM_T_IRQCNTL HW_REGISTER_RW(ARM_BASE+0x40C) -+#define ARM_T_RAWIRQ HW_REGISTER_RW(ARM_BASE+0x410) -+#define ARM_T_MSKIRQ HW_REGISTER_RW(ARM_BASE+0x414) -+#define ARM_T_RELOAD HW_REGISTER_RW(ARM_BASE+0x418) -+#define ARM_T_PREDIV HW_REGISTER_RW(ARM_BASE+0x41c) -+#define ARM_T_FREECNT HW_REGISTER_RW(ARM_BASE+0x420) -+ -+#define TIMER_CTRL_ONESHOT (1 << 0) -+#define TIMER_CTRL_32BIT (1 << 1) -+#define TIMER_CTRL_DIV1 (0 << 2) -+#define TIMER_CTRL_DIV16 (1 << 2) -+#define TIMER_CTRL_DIV256 (2 << 2) -+#define TIMER_CTRL_IE (1 << 5) -+#define TIMER_CTRL_PERIODIC (1 << 6) -+#define TIMER_CTRL_ENABLE (1 << 7) -+#define TIMER_CTRL_DBGHALT (1 << 8) -+#define TIMER_CTRL_ENAFREE (1 << 9) -+#define TIMER_CTRL_FREEDIV_SHIFT 16) -+#define TIMER_CTRL_FREEDIV_MASK 0xff -+ -+/* Semaphores, Doorbells, Mailboxes */ -+#define ARM_SBM_OWN0 (ARM_BASE+0x800) -+#define ARM_SBM_OWN1 (ARM_BASE+0x900) -+#define ARM_SBM_OWN2 (ARM_BASE+0xA00) -+#define ARM_SBM_OWN3 (ARM_BASE+0xB00) -+ -+/* MAILBOXES -+ * Register flags are common across all -+ * owner registers. See end of this section -+ * -+ * Semaphores, Doorbells, Mailboxes Owner 0 -+ * -+ */ -+ -+#define ARM_0_SEMS HW_REGISTER_RW(ARM_SBM_OWN0+0x00) -+#define ARM_0_SEM0 HW_REGISTER_RW(ARM_SBM_OWN0+0x00) -+#define ARM_0_SEM1 HW_REGISTER_RW(ARM_SBM_OWN0+0x04) -+#define ARM_0_SEM2 HW_REGISTER_RW(ARM_SBM_OWN0+0x08) -+#define ARM_0_SEM3 HW_REGISTER_RW(ARM_SBM_OWN0+0x0C) -+#define ARM_0_SEM4 HW_REGISTER_RW(ARM_SBM_OWN0+0x10) -+#define ARM_0_SEM5 HW_REGISTER_RW(ARM_SBM_OWN0+0x14) -+#define ARM_0_SEM6 HW_REGISTER_RW(ARM_SBM_OWN0+0x18) -+#define ARM_0_SEM7 HW_REGISTER_RW(ARM_SBM_OWN0+0x1C) -+#define ARM_0_BELL0 HW_REGISTER_RW(ARM_SBM_OWN0+0x40) -+#define ARM_0_BELL1 HW_REGISTER_RW(ARM_SBM_OWN0+0x44) -+#define ARM_0_BELL2 HW_REGISTER_RW(ARM_SBM_OWN0+0x48) -+#define ARM_0_BELL3 HW_REGISTER_RW(ARM_SBM_OWN0+0x4C) -+/* MAILBOX 0 access in Owner 0 area */ -+/* Some addresses should ONLY be used by owner 0 */ -+#define ARM_0_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) */ -+#define ARM_0_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) Normal read */ -+#define ARM_0_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN0+0x90) /* none-pop read */ -+#define ARM_0_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN0+0x94) /* Sender read (only LS 2 bits) */ -+#define ARM_0_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN0+0x98) /* Status read */ -+#define ARM_0_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0x9C) /* Config read/write */ -+/* MAILBOX 1 access in Owner 0 area */ -+/* Owner 0 should only WRITE to this mailbox */ -+#define ARM_0_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_0_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_0_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN0+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_0_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN0+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_0_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN0+0xB8) /* Status read */ -+/*#define ARM_0_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_0_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE0) /* semaphore clear/debug register */ -+#define ARM_0_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE4) /* Doorbells clear/debug register */ -+#define ARM_0_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xF8) /* ALL interrupts */ -+#define ARM_0_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xFC) /* IRQS pending for owner 0 */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 1 */ -+#define ARM_1_SEMS HW_REGISTER_RW(ARM_SBM_OWN1+0x00) -+#define ARM_1_SEM0 HW_REGISTER_RW(ARM_SBM_OWN1+0x00) -+#define ARM_1_SEM1 HW_REGISTER_RW(ARM_SBM_OWN1+0x04) -+#define ARM_1_SEM2 HW_REGISTER_RW(ARM_SBM_OWN1+0x08) -+#define ARM_1_SEM3 HW_REGISTER_RW(ARM_SBM_OWN1+0x0C) -+#define ARM_1_SEM4 HW_REGISTER_RW(ARM_SBM_OWN1+0x10) -+#define ARM_1_SEM5 HW_REGISTER_RW(ARM_SBM_OWN1+0x14) -+#define ARM_1_SEM6 HW_REGISTER_RW(ARM_SBM_OWN1+0x18) -+#define ARM_1_SEM7 HW_REGISTER_RW(ARM_SBM_OWN1+0x1C) -+#define ARM_1_BELL0 HW_REGISTER_RW(ARM_SBM_OWN1+0x40) -+#define ARM_1_BELL1 HW_REGISTER_RW(ARM_SBM_OWN1+0x44) -+#define ARM_1_BELL2 HW_REGISTER_RW(ARM_SBM_OWN1+0x48) -+#define ARM_1_BELL3 HW_REGISTER_RW(ARM_SBM_OWN1+0x4C) -+/* MAILBOX 0 access in Owner 0 area */ -+/* Owner 1 should only WRITE to this mailbox */ -+#define ARM_1_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_1_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN1+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_1_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN1+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_1_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN1+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_1_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN1+0x98) /* Status read */ -+/*#define ARM_1_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 0 area */ -+#define ARM_1_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) */ -+#define ARM_1_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) Normal read */ -+#define ARM_1_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN1+0xB0) /* none-pop read */ -+#define ARM_1_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN1+0xB4) /* Sender read (only LS 2 bits) */ -+#define ARM_1_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN1+0xB8) /* Status read */ -+#define ARM_1_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0xBC) -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_1_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE0) /* semaphore clear/debug register */ -+#define ARM_1_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE4) /* Doorbells clear/debug register */ -+#define ARM_1_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xFC) /* IRQS pending for owner 1 */ -+#define ARM_1_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xF8) /* ALL interrupts */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 2 */ -+#define ARM_2_SEMS HW_REGISTER_RW(ARM_SBM_OWN2+0x00) -+#define ARM_2_SEM0 HW_REGISTER_RW(ARM_SBM_OWN2+0x00) -+#define ARM_2_SEM1 HW_REGISTER_RW(ARM_SBM_OWN2+0x04) -+#define ARM_2_SEM2 HW_REGISTER_RW(ARM_SBM_OWN2+0x08) -+#define ARM_2_SEM3 HW_REGISTER_RW(ARM_SBM_OWN2+0x0C) -+#define ARM_2_SEM4 HW_REGISTER_RW(ARM_SBM_OWN2+0x10) -+#define ARM_2_SEM5 HW_REGISTER_RW(ARM_SBM_OWN2+0x14) -+#define ARM_2_SEM6 HW_REGISTER_RW(ARM_SBM_OWN2+0x18) -+#define ARM_2_SEM7 HW_REGISTER_RW(ARM_SBM_OWN2+0x1C) -+#define ARM_2_BELL0 HW_REGISTER_RW(ARM_SBM_OWN2+0x40) -+#define ARM_2_BELL1 HW_REGISTER_RW(ARM_SBM_OWN2+0x44) -+#define ARM_2_BELL2 HW_REGISTER_RW(ARM_SBM_OWN2+0x48) -+#define ARM_2_BELL3 HW_REGISTER_RW(ARM_SBM_OWN2+0x4C) -+/* MAILBOX 0 access in Owner 2 area */ -+/* Owner 2 should only WRITE to this mailbox */ -+#define ARM_2_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_2_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN2+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN2+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN2+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_2_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN2+0x98) /* Status read */ -+/*#define ARM_2_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 2 area */ -+/* Owner 2 should only WRITE to this mailbox */ -+#define ARM_2_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_2_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN2+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_2_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN2+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_2_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN2+0xB8) /* Status read */ -+/*#define ARM_2_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_2_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE0) /* semaphore clear/debug register */ -+#define ARM_2_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE4) /* Doorbells clear/debug register */ -+#define ARM_2_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xFC) /* IRQS pending for owner 2 */ -+#define ARM_2_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xF8) /* ALL interrupts */ -+ -+/* Semaphores, Doorbells, Mailboxes Owner 3 */ -+#define ARM_3_SEMS HW_REGISTER_RW(ARM_SBM_OWN3+0x00) -+#define ARM_3_SEM0 HW_REGISTER_RW(ARM_SBM_OWN3+0x00) -+#define ARM_3_SEM1 HW_REGISTER_RW(ARM_SBM_OWN3+0x04) -+#define ARM_3_SEM2 HW_REGISTER_RW(ARM_SBM_OWN3+0x08) -+#define ARM_3_SEM3 HW_REGISTER_RW(ARM_SBM_OWN3+0x0C) -+#define ARM_3_SEM4 HW_REGISTER_RW(ARM_SBM_OWN3+0x10) -+#define ARM_3_SEM5 HW_REGISTER_RW(ARM_SBM_OWN3+0x14) -+#define ARM_3_SEM6 HW_REGISTER_RW(ARM_SBM_OWN3+0x18) -+#define ARM_3_SEM7 HW_REGISTER_RW(ARM_SBM_OWN3+0x1C) -+#define ARM_3_BELL0 HW_REGISTER_RW(ARM_SBM_OWN3+0x40) -+#define ARM_3_BELL1 HW_REGISTER_RW(ARM_SBM_OWN3+0x44) -+#define ARM_3_BELL2 HW_REGISTER_RW(ARM_SBM_OWN3+0x48) -+#define ARM_3_BELL3 HW_REGISTER_RW(ARM_SBM_OWN3+0x4C) -+/* MAILBOX 0 access in Owner 3 area */ -+/* Owner 3 should only WRITE to this mailbox */ -+#define ARM_3_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0x80) /* .. 0x8C (4 locations) */ -+/*#define ARM_3_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN3+0x80) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN3+0x90) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN3+0x94) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_3_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN3+0x98) /* Status read */ -+/*#define ARM_3_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0x9C) */ /* DO NOT USE THIS !!!!! */ -+/* MAILBOX 1 access in Owner 3 area */ -+/* Owner 3 should only WRITE to this mailbox */ -+#define ARM_3_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) /* .. 0xAC (4 locations) */ -+/*#define ARM_3_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN3+0xB0) */ /* DO NOT USE THIS !!!!! */ -+/*#define ARM_3_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN3+0xB4) */ /* DO NOT USE THIS !!!!! */ -+#define ARM_3_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN3+0xB8) /* Status read */ -+/*#define ARM_3_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0xBC) */ /* DO NOT USE THIS !!!!! */ -+/* General SEM, BELL, MAIL config/status */ -+#define ARM_3_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE0) /* semaphore clear/debug register */ -+#define ARM_3_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE4) /* Doorbells clear/debug register */ -+#define ARM_3_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xFC) /* IRQS pending for owner 3 */ -+#define ARM_3_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xF8) /* ALL interrupts */ -+ -+ -+ -+/* Mailbox flags. Valid for all owners */ -+ -+/* Mailbox status register (...0x98) */ -+#define ARM_MS_FULL 0x80000000 -+#define ARM_MS_EMPTY 0x40000000 -+#define ARM_MS_LEVEL 0x400000FF /* Max. value depdnds on mailbox depth parameter */ -+ -+/* MAILBOX config/status register (...0x9C) */ -+/* ANY write to this register clears the error bits! */ -+#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mailbox irq enable: has data */ -+#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mailbox irq enable: has space */ -+#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mailbox irq enable: Opp. is empty */ -+#define ARM_MC_MAIL_CLEAR 0x00000008 /* mailbox clear write 1, then 0 */ -+#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mailbox irq pending: has space */ -+#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mailbox irq pending: Opp. is empty */ -+#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mailbox irq pending */ -+/* Bit 7 is unused */ -+#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ -+#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ -+#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ -+ -+/* Semaphore clear/debug register (...0xE0) */ -+#define ARM_SD_OWN0 0x00000003 /* Owner of sem 0 */ -+#define ARM_SD_OWN1 0x0000000C /* Owner of sem 1 */ -+#define ARM_SD_OWN2 0x00000030 /* Owner of sem 2 */ -+#define ARM_SD_OWN3 0x000000C0 /* Owner of sem 3 */ -+#define ARM_SD_OWN4 0x00000300 /* Owner of sem 4 */ -+#define ARM_SD_OWN5 0x00000C00 /* Owner of sem 5 */ -+#define ARM_SD_OWN6 0x00003000 /* Owner of sem 6 */ -+#define ARM_SD_OWN7 0x0000C000 /* Owner of sem 7 */ -+#define ARM_SD_SEM0 0x00010000 /* Status of sem 0 */ -+#define ARM_SD_SEM1 0x00020000 /* Status of sem 1 */ -+#define ARM_SD_SEM2 0x00040000 /* Status of sem 2 */ -+#define ARM_SD_SEM3 0x00080000 /* Status of sem 3 */ -+#define ARM_SD_SEM4 0x00100000 /* Status of sem 4 */ -+#define ARM_SD_SEM5 0x00200000 /* Status of sem 5 */ -+#define ARM_SD_SEM6 0x00400000 /* Status of sem 6 */ -+#define ARM_SD_SEM7 0x00800000 /* Status of sem 7 */ -+ -+/* Doorbells clear/debug register (...0xE4) */ -+#define ARM_BD_OWN0 0x00000003 /* Owner of doorbell 0 */ -+#define ARM_BD_OWN1 0x0000000C /* Owner of doorbell 1 */ -+#define ARM_BD_OWN2 0x00000030 /* Owner of doorbell 2 */ -+#define ARM_BD_OWN3 0x000000C0 /* Owner of doorbell 3 */ -+#define ARM_BD_BELL0 0x00000100 /* Status of doorbell 0 */ -+#define ARM_BD_BELL1 0x00000200 /* Status of doorbell 1 */ -+#define ARM_BD_BELL2 0x00000400 /* Status of doorbell 2 */ -+#define ARM_BD_BELL3 0x00000800 /* Status of doorbell 3 */ -+ -+/* MY IRQS register (...0xF8) */ -+#define ARM_MYIRQ_BELL 0x00000001 /* This owner has a doorbell IRQ */ -+#define ARM_MYIRQ_MAIL 0x00000002 /* This owner has a mailbox IRQ */ -+ -+/* ALL IRQS register (...0xF8) */ -+#define ARM_AIS_BELL0 0x00000001 /* Doorbell 0 IRQ pending */ -+#define ARM_AIS_BELL1 0x00000002 /* Doorbell 1 IRQ pending */ -+#define ARM_AIS_BELL2 0x00000004 /* Doorbell 2 IRQ pending */ -+#define ARM_AIS_BELL3 0x00000008 /* Doorbell 3 IRQ pending */ -+#define ARM_AIS0_HAVEDATA 0x00000010 /* MAIL 0 has data IRQ pending */ -+#define ARM_AIS0_HAVESPAC 0x00000020 /* MAIL 0 has space IRQ pending */ -+#define ARM_AIS0_OPPEMPTY 0x00000040 /* MAIL 0 opposite is empty IRQ */ -+#define ARM_AIS1_HAVEDATA 0x00000080 /* MAIL 1 has data IRQ pending */ -+#define ARM_AIS1_HAVESPAC 0x00000100 /* MAIL 1 has space IRQ pending */ -+#define ARM_AIS1_OPPEMPTY 0x00000200 /* MAIL 1 opposite is empty IRQ */ -+/* Note that bell-0, bell-1 and MAIL0 IRQ go only to the ARM */ -+/* Whilst that bell-2, bell-3 and MAIL1 IRQ go only to the VC */ -+/* */ -+/* ARM JTAG BASH */ -+/* */ -+#define AJB_BASE 0x7e2000c0 -+ -+#define AJBCONF HW_REGISTER_RW(AJB_BASE+0x00) -+#define AJB_BITS0 0x000000 -+#define AJB_BITS4 0x000004 -+#define AJB_BITS8 0x000008 -+#define AJB_BITS12 0x00000C -+#define AJB_BITS16 0x000010 -+#define AJB_BITS20 0x000014 -+#define AJB_BITS24 0x000018 -+#define AJB_BITS28 0x00001C -+#define AJB_BITS32 0x000020 -+#define AJB_BITS34 0x000022 -+#define AJB_OUT_MS 0x000040 -+#define AJB_OUT_LS 0x000000 -+#define AJB_INV_CLK 0x000080 -+#define AJB_D0_RISE 0x000100 -+#define AJB_D0_FALL 0x000000 -+#define AJB_D1_RISE 0x000200 -+#define AJB_D1_FALL 0x000000 -+#define AJB_IN_RISE 0x000400 -+#define AJB_IN_FALL 0x000000 -+#define AJB_ENABLE 0x000800 -+#define AJB_HOLD0 0x000000 -+#define AJB_HOLD1 0x001000 -+#define AJB_HOLD2 0x002000 -+#define AJB_HOLD3 0x003000 -+#define AJB_RESETN 0x004000 -+#define AJB_CLKSHFT 16 -+#define AJB_BUSY 0x80000000 -+#define AJBTMS HW_REGISTER_RW(AJB_BASE+0x04) -+#define AJBTDI HW_REGISTER_RW(AJB_BASE+0x08) -+#define AJBTDO HW_REGISTER_RW(AJB_BASE+0x0c) -+ -+#define ARM_LOCAL_BASE 0x40000000 -+#define ARM_LOCAL_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x000) -+#define ARM_LOCAL_PRESCALER HW_REGISTER_RW(ARM_LOCAL_BASE+0x008) -+#define ARM_LOCAL_GPU_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x00C) -+#define ARM_LOCAL_PM_ROUTING_SET HW_REGISTER_RW(ARM_LOCAL_BASE+0x010) -+#define ARM_LOCAL_PM_ROUTING_CLR HW_REGISTER_RW(ARM_LOCAL_BASE+0x014) -+#define ARM_LOCAL_TIMER_LS HW_REGISTER_RW(ARM_LOCAL_BASE+0x01C) -+#define ARM_LOCAL_TIMER_MS HW_REGISTER_RW(ARM_LOCAL_BASE+0x020) -+#define ARM_LOCAL_INT_ROUTING HW_REGISTER_RW(ARM_LOCAL_BASE+0x024) -+#define ARM_LOCAL_AXI_COUNT HW_REGISTER_RW(ARM_LOCAL_BASE+0x02C) -+#define ARM_LOCAL_AXI_IRQ HW_REGISTER_RW(ARM_LOCAL_BASE+0x030) -+#define ARM_LOCAL_TIMER_CONTROL HW_REGISTER_RW(ARM_LOCAL_BASE+0x034) -+#define ARM_LOCAL_TIMER_WRITE HW_REGISTER_RW(ARM_LOCAL_BASE+0x038) -+ -+#define ARM_LOCAL_TIMER_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x040) -+#define ARM_LOCAL_TIMER_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x044) -+#define ARM_LOCAL_TIMER_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x048) -+#define ARM_LOCAL_TIMER_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x04C) -+ -+#define ARM_LOCAL_MAILBOX_INT_CONTROL0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x050) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x054) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x058) -+#define ARM_LOCAL_MAILBOX_INT_CONTROL3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x05C) -+ -+#define ARM_LOCAL_IRQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x060) -+#define ARM_LOCAL_IRQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x064) -+#define ARM_LOCAL_IRQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x068) -+#define ARM_LOCAL_IRQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x06C) -+ -+#define ARM_LOCAL_FIQ_PENDING0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x070) -+#define ARM_LOCAL_FIQ_PENDING1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x074) -+#define ARM_LOCAL_FIQ_PENDING2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x078) -+#define ARM_LOCAL_FIQ_PENDING3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x07C) -+ -+#define ARM_LOCAL_MAILBOX0_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x080) -+#define ARM_LOCAL_MAILBOX1_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x084) -+#define ARM_LOCAL_MAILBOX2_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x088) -+#define ARM_LOCAL_MAILBOX3_SET0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x08C) -+ -+#define ARM_LOCAL_MAILBOX0_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x090) -+#define ARM_LOCAL_MAILBOX1_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x094) -+#define ARM_LOCAL_MAILBOX2_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x098) -+#define ARM_LOCAL_MAILBOX3_SET1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x09C) -+ -+#define ARM_LOCAL_MAILBOX0_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A0) -+#define ARM_LOCAL_MAILBOX1_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A4) -+#define ARM_LOCAL_MAILBOX2_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0A8) -+#define ARM_LOCAL_MAILBOX3_SET2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0AC) -+ -+#define ARM_LOCAL_MAILBOX0_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B0) -+#define ARM_LOCAL_MAILBOX1_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B4) -+#define ARM_LOCAL_MAILBOX2_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0B8) -+#define ARM_LOCAL_MAILBOX3_SET3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0BC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C0) -+#define ARM_LOCAL_MAILBOX1_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C4) -+#define ARM_LOCAL_MAILBOX2_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0C8) -+#define ARM_LOCAL_MAILBOX3_CLR0 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0CC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D0) -+#define ARM_LOCAL_MAILBOX1_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D4) -+#define ARM_LOCAL_MAILBOX2_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0D8) -+#define ARM_LOCAL_MAILBOX3_CLR1 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0DC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E0) -+#define ARM_LOCAL_MAILBOX1_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E4) -+#define ARM_LOCAL_MAILBOX2_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0E8) -+#define ARM_LOCAL_MAILBOX3_CLR2 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0EC) -+ -+#define ARM_LOCAL_MAILBOX0_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F0) -+#define ARM_LOCAL_MAILBOX1_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F4) -+#define ARM_LOCAL_MAILBOX2_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0F8) -+#define ARM_LOCAL_MAILBOX3_CLR3 HW_REGISTER_RW(ARM_LOCAL_BASE+0x0FC) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/arm_power.h b/arch/arm/mach-bcm2709/include/mach/arm_power.h -new file mode 100644 -index 0000000..d3bf245 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/arm_power.h -@@ -0,0 +1,62 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/include/mach/arm_power.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _ARM_POWER_H -+#define _ARM_POWER_H -+ -+/* Use meaningful names on each side */ -+#ifdef __VIDEOCORE__ -+#define PREFIX(x) ARM_##x -+#else -+#define PREFIX(x) BCM_##x -+#endif -+ -+enum { -+ PREFIX(POWER_SDCARD_BIT), -+ PREFIX(POWER_UART_BIT), -+ PREFIX(POWER_MINIUART_BIT), -+ PREFIX(POWER_USB_BIT), -+ PREFIX(POWER_I2C0_BIT), -+ PREFIX(POWER_I2C1_BIT), -+ PREFIX(POWER_I2C2_BIT), -+ PREFIX(POWER_SPI_BIT), -+ PREFIX(POWER_CCP2TX_BIT), -+ PREFIX(POWER_DSI_BIT), -+ -+ PREFIX(POWER_MAX) -+}; -+ -+enum { -+ PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)), -+ PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)), -+ PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)), -+ PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)), -+ PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)), -+ PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)), -+ PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)), -+ PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)), -+ PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)), -+ PREFIX(POWER_DSI) = (1 << PREFIX(POWER_DSI_BIT)), -+ -+ PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1, -+ PREFIX(POWER_NONE) = 0 -+}; -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/barriers.h b/arch/arm/mach-bcm2709/include/mach/barriers.h -new file mode 100644 -index 0000000..723cdad ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/barriers.h -@@ -0,0 +1,3 @@ -+#define mb() dsb() -+#define rmb() dsb() -+#define wmb() mb() -diff --git a/arch/arm/mach-bcm2709/include/mach/clkdev.h b/arch/arm/mach-bcm2709/include/mach/clkdev.h -new file mode 100644 -index 0000000..04b37a8 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/clkdev.h -@@ -0,0 +1,7 @@ -+#ifndef __ASM_MACH_CLKDEV_H -+#define __ASM_MACH_CLKDEV_H -+ -+#define __clk_get(clk) ({ 1; }) -+#define __clk_put(clk) do { } while (0) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/debug-macro.S b/arch/arm/mach-bcm2709/include/mach/debug-macro.S -new file mode 100644 -index 0000000..b24304a ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/debug-macro.S -@@ -0,0 +1,22 @@ -+/* arch/arm/mach-bcm2708/include/mach/debug-macro.S -+ * -+ * Debugging macro include header -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 1994-1999 Russell King -+ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks -+ * -+ * 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 -+ -+ .macro addruart, rp, rv, tmp -+ ldr \rp, =UART0_BASE -+ ldr \rv, =IO_ADDRESS(UART0_BASE) -+ .endm -+ -+#include -diff --git a/arch/arm/mach-bcm2709/include/mach/dma.h b/arch/arm/mach-bcm2709/include/mach/dma.h -new file mode 100644 -index 0000000..d03e7b5 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/dma.h -@@ -0,0 +1,94 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/include/mach/dma.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ */ -+ -+ -+#ifndef _MACH_BCM2708_DMA_H -+#define _MACH_BCM2708_DMA_H -+ -+#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" -+ -+/* DMA CS Control and Status bits */ -+#define BCM2708_DMA_ACTIVE (1 << 0) -+#define BCM2708_DMA_INT (1 << 2) -+#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ -+#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ -+#define BCM2708_DMA_ERR (1 << 8) -+#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ -+#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ -+ -+/* DMA control block "info" field bits */ -+#define BCM2708_DMA_INT_EN (1 << 0) -+#define BCM2708_DMA_TDMODE (1 << 1) -+#define BCM2708_DMA_WAIT_RESP (1 << 3) -+#define BCM2708_DMA_D_INC (1 << 4) -+#define BCM2708_DMA_D_WIDTH (1 << 5) -+#define BCM2708_DMA_D_DREQ (1 << 6) -+#define BCM2708_DMA_S_INC (1 << 8) -+#define BCM2708_DMA_S_WIDTH (1 << 9) -+#define BCM2708_DMA_S_DREQ (1 << 10) -+ -+#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) -+#define BCM2708_DMA_PER_MAP(x) ((x) << 16) -+#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) -+ -+#define BCM2708_DMA_DREQ_EMMC 11 -+#define BCM2708_DMA_DREQ_SDHOST 13 -+ -+#define BCM2708_DMA_CS 0x00 /* Control and Status */ -+#define BCM2708_DMA_ADDR 0x04 -+/* the current control block appears in the following registers - read only */ -+#define BCM2708_DMA_INFO 0x08 -+#define BCM2708_DMA_SOURCE_AD 0x0c -+#define BCM2708_DMA_DEST_AD 0x10 -+#define BCM2708_DMA_NEXTCB 0x1C -+#define BCM2708_DMA_DEBUG 0x20 -+ -+#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) -+#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) -+ -+#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) -+ -+struct bcm2708_dma_cb { -+ unsigned long info; -+ unsigned long src; -+ unsigned long dst; -+ unsigned long length; -+ unsigned long stride; -+ unsigned long next; -+ unsigned long pad[2]; -+}; -+struct scatterlist; -+ -+extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); -+extern void bcm_dma_start(void __iomem *dma_chan_base, -+ dma_addr_t control_block); -+extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); -+extern bool bcm_dma_is_busy(void __iomem *dma_chan_base); -+extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); -+ -+/* When listing features we can ask for when allocating DMA channels give -+ those with higher priority smaller ordinal numbers */ -+#define BCM_DMA_FEATURE_FAST_ORD 0 -+#define BCM_DMA_FEATURE_BULK_ORD 1 -+#define BCM_DMA_FEATURE_NORMAL_ORD 2 -+#define BCM_DMA_FEATURE_LITE_ORD 3 -+#define BCM_DMA_FEATURE_FAST (1< -+#include -+ -+ .macro disable_fiq -+ .endm -+ -+ .macro get_irqnr_preamble, base, tmp -+ ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE) -+ .endm -+ -+ .macro arch_ret_to_user, tmp1, tmp2 -+ .endm -+ -+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ -+ /* get core's local interrupt controller */ -+ ldr \irqstat, = __io_address(ARM_LOCAL_IRQ_PENDING0) @ local interrupt source -+ add \irqstat, \irqstat, \tmp, lsl #2 -+ ldr \tmp, [\irqstat] -+ /* ignore gpu interrupt */ -+ bic \tmp, #0x100 -+ /* ignore mailbox interrupts */ -+ bics \tmp, #0xf0 -+ beq 1005f -+ -+ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) -+ @ N.B. CLZ is an ARM5 instruction. -+ mov \irqnr, #(ARM_IRQ_LOCAL_BASE + 31) -+ sub \irqstat, \tmp, #1 -+ eor \irqstat, \irqstat, \tmp -+ clz \tmp, \irqstat -+ sub \irqnr, \tmp -+ b 1020f -+1005: -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ -+ cmp \tmp, #1 -+ beq 1020f -+ cmp \tmp, #2 -+ beq 1020f -+ cmp \tmp, #3 -+ beq 1020f -+ -+ /* get masked status */ -+ ldr \irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] -+ mov \irqnr, #(ARM_IRQ0_BASE + 31) -+ and \tmp, \irqstat, #0x300 @ save bits 8 and 9 -+ /* clear bits 8 and 9, and test */ -+ bics \irqstat, \irqstat, #0x300 -+ bne 1010f -+ -+ tst \tmp, #0x100 -+ ldrne \irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)] -+ movne \irqnr, #(ARM_IRQ1_BASE + 31) -+ @ Mask out the interrupts also present in PEND0 - see SW-5809 -+ bicne \irqstat, #((1<<7) | (1<<9) | (1<<10)) -+ bicne \irqstat, #((1<<18) | (1<<19)) -+ bne 1010f -+ -+ tst \tmp, #0x200 -+ ldrne \irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)] -+ movne \irqnr, #(ARM_IRQ2_BASE + 31) -+ @ Mask out the interrupts also present in PEND0 - see SW-5809 -+ bicne \irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25)) -+ bicne \irqstat, #((1<<30)) -+ beq 1020f -+ -+1010: -+ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) -+ @ N.B. CLZ is an ARM5 instruction. -+ sub \tmp, \irqstat, #1 -+ eor \irqstat, \irqstat, \tmp -+ clz \tmp, \irqstat -+ sub \irqnr, \tmp -+ -+1020: @ EQ will be set if no irqs pending -+ -+ .endm -+ -+ .macro test_for_ipi, irqnr, irqstat, base, tmp -+ /* get core number */ -+ mrc p15, 0, \tmp, c0, c0, 5 -+ ubfx \tmp, \tmp, #0, #2 -+ /* get core's mailbox interrupt control */ -+ ldr \irqstat, = __io_address(ARM_LOCAL_MAILBOX0_CLR0) @ mbox_clr -+ add \irqstat, \irqstat, \tmp, lsl #4 -+ ldr \tmp, [\irqstat] -+ cmp \tmp, #0 -+ beq 1030f -+ clz \tmp, \tmp -+ rsb \irqnr, \tmp, #31 -+ mov \tmp, #1 -+ lsl \tmp, \irqnr -+ str \tmp, [\irqstat] @ clear interrupt source -+ dsb -+1030: @ EQ will be set if no irqs pending -+ .endm -diff --git a/arch/arm/mach-bcm2709/include/mach/frc.h b/arch/arm/mach-bcm2709/include/mach/frc.h -new file mode 100644 -index 0000000..dd51e07 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/frc.h -@@ -0,0 +1,38 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/timex.h -+ * -+ * BCM2708 free running counter (timer) -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _MACH_FRC_H -+#define _MACH_FRC_H -+ -+#define FRC_TICK_RATE (1000000) -+ -+/*! Free running counter incrementing at the CLOCK_TICK_RATE -+ (slightly faster than frc_clock_ticks63() -+ */ -+extern unsigned long frc_clock_ticks32(void); -+ -+/*! Free running counter incrementing at the CLOCK_TICK_RATE -+ * Note - top bit should be ignored (see cnt32_to_63) -+ */ -+extern unsigned long long frc_clock_ticks63(void); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/gpio.h b/arch/arm/mach-bcm2709/include/mach/gpio.h -new file mode 100644 -index 0000000..7965a97 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/gpio.h -@@ -0,0 +1,17 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/gpio.h -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#ifndef __ASM_ARCH_GPIO_H -+#define __ASM_ARCH_GPIO_H -+ -+#define BCM2708_NR_GPIOS 54 // number of gpio lines -+ -+#define gpio_to_irq(x) ((x) + GPIO_IRQ_START) -+#define irq_to_gpio(x) ((x) - GPIO_IRQ_START) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/hardware.h b/arch/arm/mach-bcm2709/include/mach/hardware.h -new file mode 100644 -index 0000000..c2954e8 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/hardware.h -@@ -0,0 +1,28 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/hardware.h -+ * -+ * This file contains the hardware definitions of the BCM2708 devices. -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_HARDWARE_H -+#define __ASM_ARCH_HARDWARE_H -+ -+#include -+#include -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/io.h b/arch/arm/mach-bcm2709/include/mach/io.h -new file mode 100644 -index 0000000..e6eb84d ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/io.h -@@ -0,0 +1,27 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/io.h -+ * -+ * Copyright (C) 2003 ARM Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARM_ARCH_IO_H -+#define __ASM_ARM_ARCH_IO_H -+ -+#define IO_SPACE_LIMIT 0xffffffff -+ -+#define __io(a) __typesafe_io(a) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/irqs.h b/arch/arm/mach-bcm2709/include/mach/irqs.h -new file mode 100644 -index 0000000..d301f06c ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/irqs.h -@@ -0,0 +1,225 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/irqs.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * Copyright (C) 2000 Deep Blue Solutions Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _BCM2708_IRQS_H_ -+#define _BCM2708_IRQS_H_ -+ -+#include -+ -+/* -+ * IRQ interrupts definitions are the same as the INT definitions -+ * held within platform.h -+ */ -+#define IRQ_ARMCTRL_START 0 -+#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) -+#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) -+#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) -+#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) -+#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) -+#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) -+#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) -+#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) -+#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) -+#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) -+#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) -+#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) -+#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) -+#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) -+#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) -+#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) -+#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) -+#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) -+#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) -+#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) -+#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) -+#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) -+#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) -+#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) -+#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) -+#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) -+#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) -+#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) -+#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) -+#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) -+#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) -+#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) -+#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) -+#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) -+#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) -+#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) -+#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) -+#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) -+#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) -+#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) -+#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) -+#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) -+#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) -+#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) -+#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) -+#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) -+#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) -+#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) -+#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) -+#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) -+#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) -+#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) -+#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) -+#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) -+#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) -+#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) -+#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) -+#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) -+#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) -+#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) -+#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) -+#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) -+#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) -+#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) -+ -+#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) -+#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) -+#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) -+#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) -+#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) -+#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) -+#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) -+#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) -+#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) -+#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) -+ -+#define IRQ_ARM_LOCAL_CNTPSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) -+#define IRQ_ARM_LOCAL_CNTPNSIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) -+#define IRQ_ARM_LOCAL_CNTHPIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) -+#define IRQ_ARM_LOCAL_CNTVIRQ (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) -+#define IRQ_ARM_LOCAL_MAILBOX0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) -+#define IRQ_ARM_LOCAL_MAILBOX1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) -+#define IRQ_ARM_LOCAL_MAILBOX2 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) -+#define IRQ_ARM_LOCAL_MAILBOX3 (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) -+#define IRQ_ARM_LOCAL_GPU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) -+#define IRQ_ARM_LOCAL_PMU_FAST (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) -+#define IRQ_ARM_LOCAL_ZERO (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) -+#define IRQ_ARM_LOCAL_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) -+ -+#define FIQ_START HARD_IRQS -+ -+/* -+ * FIQ interrupts definitions are the same as the INT definitions. -+ */ -+#define FIQ_TIMER0 (FIQ_START+INTERRUPT_TIMER0) -+#define FIQ_TIMER1 (FIQ_START+INTERRUPT_TIMER1) -+#define FIQ_TIMER2 (FIQ_START+INTERRUPT_TIMER2) -+#define FIQ_TIMER3 (FIQ_START+INTERRUPT_TIMER3) -+#define FIQ_CODEC0 (FIQ_START+INTERRUPT_CODEC0) -+#define FIQ_CODEC1 (FIQ_START+INTERRUPT_CODEC1) -+#define FIQ_CODEC2 (FIQ_START+INTERRUPT_CODEC2) -+#define FIQ_JPEG (FIQ_START+INTERRUPT_JPEG) -+#define FIQ_ISP (FIQ_START+INTERRUPT_ISP) -+#define FIQ_USB (FIQ_START+INTERRUPT_USB) -+#define FIQ_3D (FIQ_START+INTERRUPT_3D) -+#define FIQ_TRANSPOSER (FIQ_START+INTERRUPT_TRANSPOSER) -+#define FIQ_MULTICORESYNC0 (FIQ_START+INTERRUPT_MULTICORESYNC0) -+#define FIQ_MULTICORESYNC1 (FIQ_START+INTERRUPT_MULTICORESYNC1) -+#define FIQ_MULTICORESYNC2 (FIQ_START+INTERRUPT_MULTICORESYNC2) -+#define FIQ_MULTICORESYNC3 (FIQ_START+INTERRUPT_MULTICORESYNC3) -+#define FIQ_DMA0 (FIQ_START+INTERRUPT_DMA0) -+#define FIQ_DMA1 (FIQ_START+INTERRUPT_DMA1) -+#define FIQ_DMA2 (FIQ_START+INTERRUPT_DMA2) -+#define FIQ_DMA3 (FIQ_START+INTERRUPT_DMA3) -+#define FIQ_DMA4 (FIQ_START+INTERRUPT_DMA4) -+#define FIQ_DMA5 (FIQ_START+INTERRUPT_DMA5) -+#define FIQ_DMA6 (FIQ_START+INTERRUPT_DMA6) -+#define FIQ_DMA7 (FIQ_START+INTERRUPT_DMA7) -+#define FIQ_DMA8 (FIQ_START+INTERRUPT_DMA8) -+#define FIQ_DMA9 (FIQ_START+INTERRUPT_DMA9) -+#define FIQ_DMA10 (FIQ_START+INTERRUPT_DMA10) -+#define FIQ_DMA11 (FIQ_START+INTERRUPT_DMA11) -+#define FIQ_DMA12 (FIQ_START+INTERRUPT_DMA12) -+#define FIQ_AUX (FIQ_START+INTERRUPT_AUX) -+#define FIQ_ARM (FIQ_START+INTERRUPT_ARM) -+#define FIQ_VPUDMA (FIQ_START+INTERRUPT_VPUDMA) -+#define FIQ_HOSTPORT (FIQ_START+INTERRUPT_HOSTPORT) -+#define FIQ_VIDEOSCALER (FIQ_START+INTERRUPT_VIDEOSCALER) -+#define FIQ_CCP2TX (FIQ_START+INTERRUPT_CCP2TX) -+#define FIQ_SDC (FIQ_START+INTERRUPT_SDC) -+#define FIQ_DSI0 (FIQ_START+INTERRUPT_DSI0) -+#define FIQ_AVE (FIQ_START+INTERRUPT_AVE) -+#define FIQ_CAM0 (FIQ_START+INTERRUPT_CAM0) -+#define FIQ_CAM1 (FIQ_START+INTERRUPT_CAM1) -+#define FIQ_HDMI0 (FIQ_START+INTERRUPT_HDMI0) -+#define FIQ_HDMI1 (FIQ_START+INTERRUPT_HDMI1) -+#define FIQ_PIXELVALVE1 (FIQ_START+INTERRUPT_PIXELVALVE1) -+#define FIQ_I2CSPISLV (FIQ_START+INTERRUPT_I2CSPISLV) -+#define FIQ_DSI1 (FIQ_START+INTERRUPT_DSI1) -+#define FIQ_PWA0 (FIQ_START+INTERRUPT_PWA0) -+#define FIQ_PWA1 (FIQ_START+INTERRUPT_PWA1) -+#define FIQ_CPR (FIQ_START+INTERRUPT_CPR) -+#define FIQ_SMI (FIQ_START+INTERRUPT_SMI) -+#define FIQ_GPIO0 (FIQ_START+INTERRUPT_GPIO0) -+#define FIQ_GPIO1 (FIQ_START+INTERRUPT_GPIO1) -+#define FIQ_GPIO2 (FIQ_START+INTERRUPT_GPIO2) -+#define FIQ_GPIO3 (FIQ_START+INTERRUPT_GPIO3) -+#define FIQ_I2C (FIQ_START+INTERRUPT_I2C) -+#define FIQ_SPI (FIQ_START+INTERRUPT_SPI) -+#define FIQ_I2SPCM (FIQ_START+INTERRUPT_I2SPCM) -+#define FIQ_SDIO (FIQ_START+INTERRUPT_SDIO) -+#define FIQ_UART (FIQ_START+INTERRUPT_UART) -+#define FIQ_SLIMBUS (FIQ_START+INTERRUPT_SLIMBUS) -+#define FIQ_VEC (FIQ_START+INTERRUPT_VEC) -+#define FIQ_CPG (FIQ_START+INTERRUPT_CPG) -+#define FIQ_RNG (FIQ_START+INTERRUPT_RNG) -+#define FIQ_ARASANSDIO (FIQ_START+INTERRUPT_ARASANSDIO) -+#define FIQ_AVSPMON (FIQ_START+INTERRUPT_AVSPMON) -+ -+#define FIQ_ARM_TIMER (FIQ_START+INTERRUPT_ARM_TIMER) -+#define FIQ_ARM_MAILBOX (FIQ_START+INTERRUPT_ARM_MAILBOX) -+#define FIQ_ARM_DOORBELL_0 (FIQ_START+INTERRUPT_ARM_DOORBELL_0) -+#define FIQ_ARM_DOORBELL_1 (FIQ_START+INTERRUPT_ARM_DOORBELL_1) -+#define FIQ_VPU0_HALTED (FIQ_START+INTERRUPT_VPU0_HALTED) -+#define FIQ_VPU1_HALTED (FIQ_START+INTERRUPT_VPU1_HALTED) -+#define FIQ_ILLEGAL_TYPE0 (FIQ_START+INTERRUPT_ILLEGAL_TYPE0) -+#define FIQ_ILLEGAL_TYPE1 (FIQ_START+INTERRUPT_ILLEGAL_TYPE1) -+#define FIQ_PENDING1 (FIQ_START+INTERRUPT_PENDING1) -+#define FIQ_PENDING2 (FIQ_START+INTERRUPT_PENDING2) -+ -+#define FIQ_ARM_LOCAL_CNTPSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPSIRQ) -+#define FIQ_ARM_LOCAL_CNTPNSIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTPNSIRQ) -+#define FIQ_ARM_LOCAL_CNTHPIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTHPIRQ) -+#define FIQ_ARM_LOCAL_CNTVIRQ (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_CNTVIRQ) -+#define FIQ_ARM_LOCAL_MAILBOX0 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX0) -+#define FIQ_ARM_LOCAL_MAILBOX1 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX1) -+#define FIQ_ARM_LOCAL_MAILBOX2 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX2) -+#define FIQ_ARM_LOCAL_MAILBOX3 (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_MAILBOX3) -+#define FIQ_ARM_LOCAL_GPU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_GPU_FAST) -+#define FIQ_ARM_LOCAL_PMU_FAST (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_PMU_FAST) -+#define FIQ_ARM_LOCAL_ZERO (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_ZERO) -+#define FIQ_ARM_LOCAL_TIMER (FIQ_ARMCTRL_START + INTERRUPT_ARM_LOCAL_TIMER) -+ -+#define HARD_IRQS (128) -+#define FIQ_IRQS (128) -+#define GPIO_IRQ_START (HARD_IRQS + FIQ_IRQS) -+#define GPIO_IRQS (32*5) -+#define SPARE_ALLOC_IRQS 64 -+#define BCM2708_ALLOC_IRQS (HARD_IRQS+FIQ_IRQS+GPIO_IRQS+SPARE_ALLOC_IRQS) -+#define FREE_IRQS 128 -+#define NR_IRQS (BCM2708_ALLOC_IRQS+FREE_IRQS) -+ -+#endif /* _BCM2708_IRQS_H_ */ -diff --git a/arch/arm/mach-bcm2709/include/mach/memory.h b/arch/arm/mach-bcm2709/include/mach/memory.h -new file mode 100644 -index 0000000..7548a52 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/memory.h -@@ -0,0 +1,57 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/memory.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_MEMORY_H -+#define __ASM_ARCH_MEMORY_H -+ -+/* Memory overview: -+ -+ [ARMcore] <--virtual addr--> -+ [ARMmmu] <--physical addr--> -+ [GERTmap] <--bus add--> -+ [VCperiph] -+ -+*/ -+ -+/* -+ * Physical DRAM offset. -+ */ -+#define BCM_PLAT_PHYS_OFFSET UL(0x00000000) -+#define VC_ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ -+ -+#ifdef CONFIG_BCM2708_NOL2CACHE -+ #define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ -+#else -+ #define _REAL_BUS_OFFSET UL(0x40000000) /* use L2 cache */ -+#endif -+ -+/* We're using the memory at 64M in the VideoCore for Linux - this adjustment -+ * will provide the offset into this area as well as setting the bits that -+ * stop the L1 and L2 cache from being used -+ * -+ * WARNING: this only works because the ARM is given memory at a fixed location -+ * (ARMMEM_OFFSET) -+ */ -+#define BUS_OFFSET (VC_ARMMEM_OFFSET + _REAL_BUS_OFFSET) -+#define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) -+#define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) -+#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) -+#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - BCM_PLAT_PHYS_OFFSET)) -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/platform.h b/arch/arm/mach-bcm2709/include/mach/platform.h -new file mode 100644 -index 0000000..7157f38 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/platform.h -@@ -0,0 +1,225 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/platform.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef _BCM2708_PLATFORM_H -+#define _BCM2708_PLATFORM_H -+ -+ -+/* macros to get at IO space when running virtually */ -+#define IO_ADDRESS(x) (((x) & 0x00ffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) -+ -+#define __io_address(n) IOMEM(IO_ADDRESS(n)) -+ -+ -+/* -+ * SDRAM -+ */ -+#define BCM2708_SDRAM_BASE 0x00000000 -+ -+/* -+ * Logic expansion modules -+ * -+ */ -+ -+ -+/* ------------------------------------------------------------------------ -+ * BCM2708 ARMCTRL Registers -+ * ------------------------------------------------------------------------ -+ */ -+ -+#define HW_REGISTER_RW(addr) (addr) -+#define HW_REGISTER_RO(addr) (addr) -+ -+#include "arm_control.h" -+#undef ARM_BASE -+ -+/* -+ * Definitions and addresses for the ARM CONTROL logic -+ * This file is manually generated. -+ */ -+ -+#define BCM2708_PERI_BASE 0x3F000000 -+#define IC0_BASE (BCM2708_PERI_BASE + 0x2000) -+#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ -+#define MPHI_BASE (BCM2708_PERI_BASE + 0x6000) /* Message -based Parallel Host Interface */ -+#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ -+#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ -+#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ -+#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ -+#define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ -+#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ -+#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ -+#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ -+#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */ -+#define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */ -+#define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */ -+#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ -+#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ -+#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ -+#define BSC1_BASE (BCM2708_PERI_BASE + 0x804000) /* BSC1 I2C/TWI */ -+#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ -+#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device (actually the multicore sync block*/ -+ -+#define ARMCTRL_BASE (ARM_BASE + 0x000) -+#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ -+#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ -+#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ -+ -+ -+/* -+ * Interrupt assignments -+ */ -+ -+#define ARM_IRQ1_BASE 0 -+#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) -+#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) -+#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) -+#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) -+#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) -+#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) -+#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) -+#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) -+#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) -+#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) -+#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) -+#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) -+#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) -+#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) -+#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) -+#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) -+#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) -+#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) -+#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) -+#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) -+#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) -+#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) -+#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) -+#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) -+#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) -+#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) -+#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) -+#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) -+#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) -+#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) -+#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) -+#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) -+ -+#define ARM_IRQ2_BASE 32 -+#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) -+#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) -+#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) -+#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) -+#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) -+#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) -+#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) -+#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) -+#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) -+#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) -+#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) -+#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) -+#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) -+#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) -+#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) -+#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) -+#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) -+#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) -+#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) -+#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) -+#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) -+#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) -+#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) -+#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) -+#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) -+#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) -+#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) -+#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) -+#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) -+#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) -+#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) -+#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) -+ -+#define ARM_IRQ0_BASE 64 -+#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) -+#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) -+#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) -+#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) -+#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) -+#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) -+#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) -+#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) -+#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) -+#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) -+#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) -+#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) -+#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) -+#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) -+#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) -+#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) -+#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) -+#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) -+#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) -+#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) -+#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) -+ -+#define ARM_IRQ_LOCAL_BASE 96 -+#define INTERRUPT_ARM_LOCAL_CNTPSIRQ (ARM_IRQ_LOCAL_BASE + 0) -+#define INTERRUPT_ARM_LOCAL_CNTPNSIRQ (ARM_IRQ_LOCAL_BASE + 1) -+#define INTERRUPT_ARM_LOCAL_CNTHPIRQ (ARM_IRQ_LOCAL_BASE + 2) -+#define INTERRUPT_ARM_LOCAL_CNTVIRQ (ARM_IRQ_LOCAL_BASE + 3) -+#define INTERRUPT_ARM_LOCAL_MAILBOX0 (ARM_IRQ_LOCAL_BASE + 4) -+#define INTERRUPT_ARM_LOCAL_MAILBOX1 (ARM_IRQ_LOCAL_BASE + 5) -+#define INTERRUPT_ARM_LOCAL_MAILBOX2 (ARM_IRQ_LOCAL_BASE + 6) -+#define INTERRUPT_ARM_LOCAL_MAILBOX3 (ARM_IRQ_LOCAL_BASE + 7) -+#define INTERRUPT_ARM_LOCAL_GPU_FAST (ARM_IRQ_LOCAL_BASE + 8) -+#define INTERRUPT_ARM_LOCAL_PMU_FAST (ARM_IRQ_LOCAL_BASE + 9) -+#define INTERRUPT_ARM_LOCAL_ZERO (ARM_IRQ_LOCAL_BASE + 10) -+#define INTERRUPT_ARM_LOCAL_TIMER (ARM_IRQ_LOCAL_BASE + 11) -+ -+/* -+ * Watchdog -+ */ -+#define PM_RSTC (PM_BASE+0x1c) -+#define PM_RSTS (PM_BASE+0x20) -+#define PM_WDOG (PM_BASE+0x24) -+ -+#define PM_WDOG_RESET 0000000000 -+#define PM_PASSWORD 0x5a000000 -+#define PM_WDOG_TIME_SET 0x000fffff -+#define PM_RSTC_WRCFG_CLR 0xffffffcf -+#define PM_RSTC_WRCFG_SET 0x00000030 -+#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -+#define PM_RSTC_RESET 0x00000102 -+ -+#define PM_RSTS_HADPOR_SET 0x00001000 -+#define PM_RSTS_HADSRH_SET 0x00000400 -+#define PM_RSTS_HADSRF_SET 0x00000200 -+#define PM_RSTS_HADSRQ_SET 0x00000100 -+#define PM_RSTS_HADWRH_SET 0x00000040 -+#define PM_RSTS_HADWRF_SET 0x00000020 -+#define PM_RSTS_HADWRQ_SET 0x00000010 -+#define PM_RSTS_HADDRH_SET 0x00000004 -+#define PM_RSTS_HADDRF_SET 0x00000002 -+#define PM_RSTS_HADDRQ_SET 0x00000001 -+ -+#define UART0_CLOCK 3000000 -+ -+#endif -+ -+/* END */ -diff --git a/arch/arm/mach-bcm2709/include/mach/power.h b/arch/arm/mach-bcm2709/include/mach/power.h -new file mode 100644 -index 0000000..52b3b02 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/power.h -@@ -0,0 +1,26 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/power.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for controlling the power to -+ * VideoCore subsystems. -+ */ -+ -+#ifndef _MACH_BCM2708_POWER_H -+#define _MACH_BCM2708_POWER_H -+ -+#include -+#include -+ -+typedef unsigned int BCM_POWER_HANDLE_T; -+ -+extern int bcm_power_open(BCM_POWER_HANDLE_T *handle); -+extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request); -+extern int bcm_power_close(BCM_POWER_HANDLE_T handle); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/system.h b/arch/arm/mach-bcm2709/include/mach/system.h -new file mode 100644 -index 0000000..2d0b821 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/system.h -@@ -0,0 +1,38 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/system.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * Copyright (C) 2000 Deep Blue Solutions Ltd -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef __ASM_ARCH_SYSTEM_H -+#define __ASM_ARCH_SYSTEM_H -+ -+#include -+#include -+#include -+ -+static inline void arch_idle(void) -+{ -+ /* -+ * This should do all the clock switching -+ * and wait for interrupt tricks -+ */ -+ cpu_do_idle(); -+} -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/timex.h b/arch/arm/mach-bcm2709/include/mach/timex.h -new file mode 100644 -index 0000000..64a660c ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/timex.h -@@ -0,0 +1,23 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/timex.h -+ * -+ * BCM2708 sysem clock frequency -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#define CLOCK_TICK_RATE (1000000) -diff --git a/arch/arm/mach-bcm2709/include/mach/uncompress.h b/arch/arm/mach-bcm2709/include/mach/uncompress.h -new file mode 100644 -index 0000000..d634813 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/uncompress.h -@@ -0,0 +1,84 @@ -+/* -+ * arch/arm/mach-bcn2708/include/mach/uncompress.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * Copyright (C) 2003 ARM Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include -+#include -+#include -+ -+#define UART_BAUD 115200 -+ -+#define BCM2708_UART_DR __io(UART0_BASE + UART01x_DR) -+#define BCM2708_UART_FR __io(UART0_BASE + UART01x_FR) -+#define BCM2708_UART_IBRD __io(UART0_BASE + UART011_IBRD) -+#define BCM2708_UART_FBRD __io(UART0_BASE + UART011_FBRD) -+#define BCM2708_UART_LCRH __io(UART0_BASE + UART011_LCRH) -+#define BCM2708_UART_CR __io(UART0_BASE + UART011_CR) -+ -+/* -+ * This does not append a newline -+ */ -+static inline void putc(int c) -+{ -+ while (__raw_readl(BCM2708_UART_FR) & UART01x_FR_TXFF) -+ barrier(); -+ -+ __raw_writel(c, BCM2708_UART_DR); -+} -+ -+static inline void flush(void) -+{ -+ int fr; -+ -+ do { -+ fr = __raw_readl(BCM2708_UART_FR); -+ barrier(); -+ } while ((fr & (UART011_FR_TXFE | UART01x_FR_BUSY)) != UART011_FR_TXFE); -+} -+ -+static inline void arch_decomp_setup(void) -+{ -+ int temp, div, rem, frac; -+ -+ temp = 16 * UART_BAUD; -+ div = UART0_CLOCK / temp; -+ rem = UART0_CLOCK % temp; -+ temp = (8 * rem) / UART_BAUD; -+ frac = (temp >> 1) + (temp & 1); -+ -+ /* Make sure the UART is disabled before we start */ -+ __raw_writel(0, BCM2708_UART_CR); -+ -+ /* Set the baud rate */ -+ __raw_writel(div, BCM2708_UART_IBRD); -+ __raw_writel(frac, BCM2708_UART_FBRD); -+ -+ /* Set the UART to 8n1, FIFO enabled */ -+ __raw_writel(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, BCM2708_UART_LCRH); -+ -+ /* Enable the UART */ -+ __raw_writel(UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_RXE, -+ BCM2708_UART_CR); -+} -+ -+/* -+ * nothing to do -+ */ -+#define arch_decomp_wdog() -diff --git a/arch/arm/mach-bcm2709/include/mach/vc_mem.h b/arch/arm/mach-bcm2709/include/mach/vc_mem.h -new file mode 100644 -index 0000000..4a4a338 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vc_mem.h -@@ -0,0 +1,35 @@ -+/***************************************************************************** -+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#if !defined( VC_MEM_H ) -+#define VC_MEM_H -+ -+#include -+ -+#define VC_MEM_IOC_MAGIC 'v' -+ -+#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) -+#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) -+#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int ) -+#define VC_MEM_IOC_MEM_LOAD _IOR( VC_MEM_IOC_MAGIC, 3, unsigned int ) -+ -+#if defined( __KERNEL__ ) -+#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF -+ -+extern unsigned long mm_vc_mem_phys_addr; -+extern unsigned int mm_vc_mem_size; -+extern int vc_mem_get_current_size( void ); -+#endif -+ -+#endif /* VC_MEM_H */ -diff --git a/arch/arm/mach-bcm2709/include/mach/vc_support.h b/arch/arm/mach-bcm2709/include/mach/vc_support.h -new file mode 100755 -index 0000000..70e809f ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vc_support.h -@@ -0,0 +1,69 @@ -+#ifndef _VC_SUPPORT_H_ -+#define _VC_SUPPORT_H_ -+ -+/* -+ * vc_support.h -+ * -+ * Created on: 25 Nov 2012 -+ * Author: Simon -+ */ -+ -+enum { -+/* -+ If a MEM_HANDLE_T is discardable, the memory manager may resize it to size -+ 0 at any time when it is not locked or retained. -+ */ -+ MEM_FLAG_DISCARDABLE = 1 << 0, -+ -+ /* -+ If a MEM_HANDLE_T is allocating (or normal), its block of memory will be -+ accessed in an allocating fashion through the cache. -+ */ -+ MEM_FLAG_NORMAL = 0 << 2, -+ MEM_FLAG_ALLOCATING = MEM_FLAG_NORMAL, -+ -+ /* -+ If a MEM_HANDLE_T is direct, its block of memory will be accessed -+ directly, bypassing the cache. -+ */ -+ MEM_FLAG_DIRECT = 1 << 2, -+ -+ /* -+ If a MEM_HANDLE_T is coherent, its block of memory will be accessed in a -+ non-allocating fashion through the cache. -+ */ -+ MEM_FLAG_COHERENT = 2 << 2, -+ -+ /* -+ If a MEM_HANDLE_T is L1-nonallocating, its block of memory will be accessed by -+ the VPU in a fashion which is allocating in L2, but only coherent in L1. -+ */ -+ MEM_FLAG_L1_NONALLOCATING = (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT), -+ -+ /* -+ If a MEM_HANDLE_T is zero'd, its contents are set to 0 rather than -+ MEM_HANDLE_INVALID on allocation and resize up. -+ */ -+ MEM_FLAG_ZERO = 1 << 4, -+ -+ /* -+ If a MEM_HANDLE_T is uninitialised, it will not be reset to a defined value -+ (either zero, or all 1's) on allocation. -+ */ -+ MEM_FLAG_NO_INIT = 1 << 5, -+ -+ /* -+ Hints. -+ */ -+ MEM_FLAG_HINT_PERMALOCK = 1 << 6, /* Likely to be locked for long periods of time. */ -+}; -+ -+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags); -+unsigned int ReleaseVcMemory(unsigned int handle); -+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle); -+unsigned int UnlockVcMemory(unsigned int handle); -+ -+unsigned int ExecuteVcCode(unsigned int code, -+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5); -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/vcio.h b/arch/arm/mach-bcm2709/include/mach/vcio.h -new file mode 100644 -index 0000000..8e11d67 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vcio.h -@@ -0,0 +1,165 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/vcio.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#ifndef _MACH_BCM2708_VCIO_H -+#define _MACH_BCM2708_VCIO_H -+ -+/* Routines to handle I/O via the VideoCore "ARM control" registers -+ * (semaphores, doorbells, mailboxes) -+ */ -+ -+#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio" -+ -+/* Constants shared with the ARM identifying separate mailbox channels */ -+#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ -+#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ -+#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ -+#define MBOX_CHAN_PROPERTY 8 /* for use by the property channel */ -+#define MBOX_CHAN_COUNT 9 -+ -+enum { -+ VCMSG_PROCESS_REQUEST = 0x00000000 -+}; -+enum { -+ VCMSG_REQUEST_SUCCESSFUL = 0x80000000, -+ VCMSG_REQUEST_FAILED = 0x80000001 -+}; -+/* Mailbox property tags */ -+enum { -+ VCMSG_PROPERTY_END = 0x00000000, -+ VCMSG_GET_FIRMWARE_REVISION = 0x00000001, -+ VCMSG_GET_BOARD_MODEL = 0x00010001, -+ VCMSG_GET_BOARD_REVISION = 0x00010002, -+ VCMSG_GET_BOARD_MAC_ADDRESS = 0x00010003, -+ VCMSG_GET_BOARD_SERIAL = 0x00010004, -+ VCMSG_GET_ARM_MEMORY = 0x00010005, -+ VCMSG_GET_VC_MEMORY = 0x00010006, -+ VCMSG_GET_CLOCKS = 0x00010007, -+ VCMSG_GET_COMMAND_LINE = 0x00050001, -+ VCMSG_GET_DMA_CHANNELS = 0x00060001, -+ VCMSG_GET_POWER_STATE = 0x00020001, -+ VCMSG_GET_TIMING = 0x00020002, -+ VCMSG_SET_POWER_STATE = 0x00028001, -+ VCMSG_GET_CLOCK_STATE = 0x00030001, -+ VCMSG_SET_CLOCK_STATE = 0x00038001, -+ VCMSG_GET_CLOCK_RATE = 0x00030002, -+ VCMSG_SET_CLOCK_RATE = 0x00038002, -+ VCMSG_GET_VOLTAGE = 0x00030003, -+ VCMSG_SET_VOLTAGE = 0x00038003, -+ VCMSG_GET_MAX_CLOCK = 0x00030004, -+ VCMSG_GET_MAX_VOLTAGE = 0x00030005, -+ VCMSG_GET_TEMPERATURE = 0x00030006, -+ VCMSG_GET_MIN_CLOCK = 0x00030007, -+ VCMSG_GET_MIN_VOLTAGE = 0x00030008, -+ VCMSG_GET_TURBO = 0x00030009, -+ VCMSG_GET_MAX_TEMPERATURE = 0x0003000a, -+ VCMSG_GET_STC = 0x0003000b, -+ VCMSG_SET_TURBO = 0x00038009, -+ VCMSG_SET_ALLOCATE_MEM = 0x0003000c, -+ VCMSG_SET_LOCK_MEM = 0x0003000d, -+ VCMSG_SET_UNLOCK_MEM = 0x0003000e, -+ VCMSG_SET_RELEASE_MEM = 0x0003000f, -+ VCMSG_SET_EXECUTE_CODE = 0x00030010, -+ VCMSG_SET_EXECUTE_QPU = 0x00030011, -+ VCMSG_SET_ENABLE_QPU = 0x00030012, -+ VCMSG_GET_RESOURCE_HANDLE = 0x00030014, -+ VCMSG_GET_EDID_BLOCK = 0x00030020, -+ VCMSG_GET_CUSTOMER_OTP = 0x00030021, -+ VCMSG_SET_CUSTOMER_OTP = 0x00038021, -+ VCMSG_SET_ALLOCATE_BUFFER = 0x00040001, -+ VCMSG_SET_RELEASE_BUFFER = 0x00048001, -+ VCMSG_SET_BLANK_SCREEN = 0x00040002, -+ VCMSG_TST_BLANK_SCREEN = 0x00044002, -+ VCMSG_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, -+ VCMSG_TST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, -+ VCMSG_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, -+ VCMSG_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, -+ VCMSG_TST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, -+ VCMSG_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, -+ VCMSG_GET_DEPTH = 0x00040005, -+ VCMSG_TST_DEPTH = 0x00044005, -+ VCMSG_SET_DEPTH = 0x00048005, -+ VCMSG_GET_PIXEL_ORDER = 0x00040006, -+ VCMSG_TST_PIXEL_ORDER = 0x00044006, -+ VCMSG_SET_PIXEL_ORDER = 0x00048006, -+ VCMSG_GET_ALPHA_MODE = 0x00040007, -+ VCMSG_TST_ALPHA_MODE = 0x00044007, -+ VCMSG_SET_ALPHA_MODE = 0x00048007, -+ VCMSG_GET_PITCH = 0x00040008, -+ VCMSG_TST_PITCH = 0x00044008, -+ VCMSG_SET_PITCH = 0x00048008, -+ VCMSG_GET_VIRTUAL_OFFSET = 0x00040009, -+ VCMSG_TST_VIRTUAL_OFFSET = 0x00044009, -+ VCMSG_SET_VIRTUAL_OFFSET = 0x00048009, -+ VCMSG_GET_OVERSCAN = 0x0004000a, -+ VCMSG_TST_OVERSCAN = 0x0004400a, -+ VCMSG_SET_OVERSCAN = 0x0004800a, -+ VCMSG_GET_PALETTE = 0x0004000b, -+ VCMSG_TST_PALETTE = 0x0004400b, -+ VCMSG_SET_PALETTE = 0x0004800b, -+ VCMSG_GET_LAYER = 0x0004000c, -+ VCMSG_TST_LAYER = 0x0004400c, -+ VCMSG_SET_LAYER = 0x0004800c, -+ VCMSG_GET_TRANSFORM = 0x0004000d, -+ VCMSG_TST_TRANSFORM = 0x0004400d, -+ VCMSG_SET_TRANSFORM = 0x0004800d, -+ VCMSG_TST_VSYNC = 0x0004400e, -+ VCMSG_SET_VSYNC = 0x0004800e, -+ VCMSG_SET_CURSOR_INFO = 0x00008010, -+ VCMSG_SET_CURSOR_STATE = 0x00008011, -+}; -+ -+extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28); -+extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28); -+extern int /*rc*/ bcm_mailbox_property(void *data, int size); -+ -+#include -+ -+/* -+ * The major device number. We can't rely on dynamic -+ * registration any more, because ioctls need to know -+ * it. -+ */ -+#define MAJOR_NUM 100 -+ -+/* -+ * Set the message of the device driver -+ */ -+#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *) -+/* -+ * _IOWR means that we're creating an ioctl command -+ * number for passing information from a user process -+ * to the kernel module and from the kernel module to user process -+ * -+ * The first arguments, MAJOR_NUM, is the major device -+ * number we're using. -+ * -+ * The second argument is the number of the command -+ * (there could be several with different meanings). -+ * -+ * The third argument is the type we want to get from -+ * the process to the kernel. -+ */ -+ -+/* -+ * The name of the device file -+ */ -+#define DEVICE_FILE_NAME "vcio" -+ -+#endif -diff --git a/arch/arm/mach-bcm2709/include/mach/vmalloc.h b/arch/arm/mach-bcm2709/include/mach/vmalloc.h -new file mode 100644 -index 0000000..6aa6826 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/include/mach/vmalloc.h -@@ -0,0 +1,20 @@ -+/* -+ * arch/arm/mach-bcm2708/include/mach/vmalloc.h -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+#define VMALLOC_END (0xff000000) -diff --git a/arch/arm/mach-bcm2709/power.c b/arch/arm/mach-bcm2709/power.c -new file mode 100644 -index 0000000..3421057 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/power.c -@@ -0,0 +1,195 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/power.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for controlling the power to -+ * VideoCore subsystems. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "bcm2708_power" -+ -+#define BCM_POWER_MAXCLIENTS 4 -+#define BCM_POWER_NOCLIENT (1<<31) -+ -+/* Some drivers expect there devices to be permanently powered */ -+#ifdef CONFIG_USB -+#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB) -+#endif -+ -+#if 1 -+#define DPRINTK printk -+#else -+#define DPRINTK if (0) printk -+#endif -+ -+struct state_struct { -+ uint32_t global_request; -+ uint32_t client_request[BCM_POWER_MAXCLIENTS]; -+ struct semaphore client_mutex; -+ struct semaphore mutex; -+} g_state; -+ -+int bcm_power_open(BCM_POWER_HANDLE_T *handle) -+{ -+ BCM_POWER_HANDLE_T i; -+ int ret = -EBUSY; -+ -+ down(&g_state.client_mutex); -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { -+ if (g_state.client_request[i] == BCM_POWER_NOCLIENT) { -+ g_state.client_request[i] = BCM_POWER_NONE; -+ *handle = i; -+ ret = 0; -+ break; -+ } -+ } -+ -+ up(&g_state.client_mutex); -+ -+ DPRINTK("bcm_power_open() -> %d\n", *handle); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(bcm_power_open); -+ -+int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request) -+{ -+ int rc = 0; -+ -+ DPRINTK("bcm_power_request(%d, %x)\n", handle, request); -+ -+ if ((handle < BCM_POWER_MAXCLIENTS) && -+ (g_state.client_request[handle] != BCM_POWER_NOCLIENT)) { -+ if (down_interruptible(&g_state.mutex) != 0) { -+ DPRINTK("bcm_power_request -> interrupted\n"); -+ return -EINTR; -+ } -+ -+ if (request != g_state.client_request[handle]) { -+ uint32_t others_request = 0; -+ uint32_t global_request; -+ BCM_POWER_HANDLE_T i; -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { -+ if (i != handle) -+ others_request |= -+ g_state.client_request[i]; -+ } -+ others_request &= ~BCM_POWER_NOCLIENT; -+ -+ global_request = request | others_request; -+ if (global_request != g_state.global_request) { -+ uint32_t actual; -+ -+ /* Send a request to VideoCore */ -+ bcm_mailbox_write(MBOX_CHAN_POWER, -+ global_request << 4); -+ -+ /* Wait for a response during power-up */ -+ if (global_request & ~g_state.global_request) { -+ rc = bcm_mailbox_read(MBOX_CHAN_POWER, -+ &actual); -+ DPRINTK -+ ("bcm_mailbox_read -> %08x, %d\n", -+ actual, rc); -+ actual >>= 4; -+ } else { -+ rc = 0; -+ actual = global_request; -+ } -+ -+ if (rc == 0) { -+ if (actual != global_request) { -+ printk(KERN_ERR -+ "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", -+ __func__, -+ g_state.global_request, -+ global_request, actual, request, others_request); -+ /* A failure */ -+ BUG_ON((others_request & actual) -+ != others_request); -+ request &= actual; -+ rc = -EIO; -+ } -+ -+ g_state.global_request = actual; -+ g_state.client_request[handle] = -+ request; -+ } -+ } -+ } -+ up(&g_state.mutex); -+ } else { -+ rc = -EINVAL; -+ } -+ DPRINTK("bcm_power_request -> %d\n", rc); -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_power_request); -+ -+int bcm_power_close(BCM_POWER_HANDLE_T handle) -+{ -+ int rc; -+ -+ DPRINTK("bcm_power_close(%d)\n", handle); -+ -+ rc = bcm_power_request(handle, BCM_POWER_NONE); -+ if (rc == 0) -+ g_state.client_request[handle] = BCM_POWER_NOCLIENT; -+ -+ return rc; -+} -+EXPORT_SYMBOL_GPL(bcm_power_close); -+ -+static int __init bcm_power_init(void) -+{ -+#if defined(BCM_POWER_ALWAYS_ON) -+ BCM_POWER_HANDLE_T always_on_handle; -+#endif -+ int rc = 0; -+ int i; -+ -+ printk(KERN_INFO "bcm_power: Broadcom power driver\n"); -+ bcm_mailbox_write(MBOX_CHAN_POWER, 0); -+ -+ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) -+ g_state.client_request[i] = BCM_POWER_NOCLIENT; -+ -+ sema_init(&g_state.client_mutex, 1); -+ sema_init(&g_state.mutex, 1); -+ -+ g_state.global_request = 0; -+#if defined(BCM_POWER_ALWAYS_ON) -+ if (BCM_POWER_ALWAYS_ON) { -+ bcm_power_open(&always_on_handle); -+ bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON); -+ } -+#endif -+ -+ return rc; -+} -+ -+static void __exit bcm_power_exit(void) -+{ -+ bcm_mailbox_write(MBOX_CHAN_POWER, 0); -+} -+ -+arch_initcall(bcm_power_init); /* Initialize early */ -+module_exit(bcm_power_exit); -+ -+MODULE_AUTHOR("Phil Elwell"); -+MODULE_DESCRIPTION("Interface to BCM2708 power management"); -+MODULE_LICENSE("GPL"); -diff --git a/arch/arm/mach-bcm2709/vc_mem.c b/arch/arm/mach-bcm2709/vc_mem.c -new file mode 100644 -index 0000000..ac578db ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vc_mem.c -@@ -0,0 +1,431 @@ -+/***************************************************************************** -+* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. -+* -+* Unless you and Broadcom execute a separate written software license -+* agreement governing use of this software, this software is licensed to you -+* under the terms of the GNU General Public License version 2, available at -+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). -+* -+* Notwithstanding the above, under no circumstances may you combine this -+* software in any way with any other Broadcom software provided under a -+* license other than the GPL, without Broadcom's express prior written -+* consent. -+*****************************************************************************/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef CONFIG_ARCH_KONA -+#include -+#elif defined(CONFIG_ARCH_BCM2708) || defined(CONFIG_ARCH_BCM2709) -+#else -+#include -+#endif -+ -+#include "mach/vc_mem.h" -+#include -+ -+#define DRIVER_NAME "vc-mem" -+ -+// Device (/dev) related variables -+static dev_t vc_mem_devnum = 0; -+static struct class *vc_mem_class = NULL; -+static struct cdev vc_mem_cdev; -+static int vc_mem_inited = 0; -+ -+#ifdef CONFIG_DEBUG_FS -+static struct dentry *vc_mem_debugfs_entry; -+#endif -+ -+/* -+ * Videocore memory addresses and size -+ * -+ * Drivers that wish to know the videocore memory addresses and sizes should -+ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in -+ * headers. This allows the other drivers to not be tied down to a a certain -+ * address/size at compile time. -+ * -+ * In the future, the goal is to have the videocore memory virtual address and -+ * size be calculated at boot time rather than at compile time. The decision of -+ * where the videocore memory resides and its size would be in the hands of the -+ * bootloader (and/or kernel). When that happens, the values of these variables -+ * would be calculated and assigned in the init function. -+ */ -+// in the 2835 VC in mapped above ARM, but ARM has full access to VC space -+unsigned long mm_vc_mem_phys_addr = 0x00000000; -+unsigned int mm_vc_mem_size = 0; -+unsigned int mm_vc_mem_base = 0; -+ -+EXPORT_SYMBOL(mm_vc_mem_phys_addr); -+EXPORT_SYMBOL(mm_vc_mem_size); -+EXPORT_SYMBOL(mm_vc_mem_base); -+ -+static uint phys_addr = 0; -+static uint mem_size = 0; -+static uint mem_base = 0; -+ -+ -+/**************************************************************************** -+* -+* vc_mem_open -+* -+***************************************************************************/ -+ -+static int -+vc_mem_open(struct inode *inode, struct file *file) -+{ -+ (void) inode; -+ (void) file; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_release -+* -+***************************************************************************/ -+ -+static int -+vc_mem_release(struct inode *inode, struct file *file) -+{ -+ (void) inode; -+ (void) file; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ return 0; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_size -+* -+***************************************************************************/ -+ -+static void -+vc_mem_get_size(void) -+{ -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_base -+* -+***************************************************************************/ -+ -+static void -+vc_mem_get_base(void) -+{ -+} -+ -+/**************************************************************************** -+* -+* vc_mem_get_current_size -+* -+***************************************************************************/ -+ -+int -+vc_mem_get_current_size(void) -+{ -+ return mm_vc_mem_size; -+} -+ -+EXPORT_SYMBOL_GPL(vc_mem_get_current_size); -+ -+/**************************************************************************** -+* -+* vc_mem_ioctl -+* -+***************************************************************************/ -+ -+static long -+vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ int rc = 0; -+ -+ (void) cmd; -+ (void) arg; -+ -+ pr_debug("%s: called file = 0x%p\n", __func__, file); -+ -+ switch (cmd) { -+ case VC_MEM_IOC_MEM_PHYS_ADDR: -+ { -+ pr_debug("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p\n", -+ __func__, (void *) mm_vc_mem_phys_addr); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr, -+ sizeof (mm_vc_mem_phys_addr)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_SIZE: -+ { -+ // Get the videocore memory size first -+ vc_mem_get_size(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_SIZE=%u\n", __func__, -+ mm_vc_mem_size); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_size, -+ sizeof (mm_vc_mem_size)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_BASE: -+ { -+ // Get the videocore memory base -+ vc_mem_get_base(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_BASE=%u\n", __func__, -+ mm_vc_mem_base); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_base, -+ sizeof (mm_vc_mem_base)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ case VC_MEM_IOC_MEM_LOAD: -+ { -+ // Get the videocore memory base -+ vc_mem_get_base(); -+ -+ pr_debug("%s: VC_MEM_IOC_MEM_LOAD=%u\n", __func__, -+ mm_vc_mem_base); -+ -+ if (copy_to_user((void *) arg, &mm_vc_mem_base, -+ sizeof (mm_vc_mem_base)) != 0) { -+ rc = -EFAULT; -+ } -+ break; -+ } -+ default: -+ { -+ return -ENOTTY; -+ } -+ } -+ pr_debug("%s: file = 0x%p returning %d\n", __func__, file, rc); -+ -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_mmap -+* -+***************************************************************************/ -+ -+static int -+vc_mem_mmap(struct file *filp, struct vm_area_struct *vma) -+{ -+ int rc = 0; -+ unsigned long length = vma->vm_end - vma->vm_start; -+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; -+ -+ pr_debug("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx\n", -+ __func__, (long) vma->vm_start, (long) vma->vm_end, -+ (long) vma->vm_pgoff); -+ -+ if (offset + length > mm_vc_mem_size) { -+ pr_err("%s: length %ld is too big\n", __func__, length); -+ return -EINVAL; -+ } -+ // Do not cache the memory map -+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); -+ -+ rc = remap_pfn_range(vma, vma->vm_start, -+ (mm_vc_mem_phys_addr >> PAGE_SHIFT) + -+ vma->vm_pgoff, length, vma->vm_page_prot); -+ if (rc != 0) { -+ pr_err("%s: remap_pfn_range failed (rc=%d)\n", __func__, rc); -+ } -+ -+ return rc; -+} -+ -+/**************************************************************************** -+* -+* File Operations for the driver. -+* -+***************************************************************************/ -+ -+static const struct file_operations vc_mem_fops = { -+ .owner = THIS_MODULE, -+ .open = vc_mem_open, -+ .release = vc_mem_release, -+ .unlocked_ioctl = vc_mem_ioctl, -+ .mmap = vc_mem_mmap, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static void vc_mem_debugfs_deinit(void) -+{ -+ debugfs_remove_recursive(vc_mem_debugfs_entry); -+ vc_mem_debugfs_entry = NULL; -+} -+ -+ -+static int vc_mem_debugfs_init( -+ struct device *dev) -+{ -+ vc_mem_debugfs_entry = debugfs_create_dir(DRIVER_NAME, NULL); -+ if (!vc_mem_debugfs_entry) { -+ dev_warn(dev, "could not create debugfs entry\n"); -+ return -EFAULT; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_phys_addr", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_phys_addr)) { -+ dev_warn(dev, "%s:could not create vc_mem_phys entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_size", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_size)) { -+ dev_warn(dev, "%s:could not create vc_mem_size entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ if (!debugfs_create_x32("vc_mem_base", -+ 0444, -+ vc_mem_debugfs_entry, -+ (u32 *)&mm_vc_mem_base)) { -+ dev_warn(dev, "%s:could not create vc_mem_base entry\n", -+ __func__); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ vc_mem_debugfs_deinit(); -+ return -EFAULT; -+} -+ -+#endif /* CONFIG_DEBUG_FS */ -+ -+ -+/**************************************************************************** -+* -+* vc_mem_init -+* -+***************************************************************************/ -+ -+static int __init -+vc_mem_init(void) -+{ -+ int rc = -EFAULT; -+ struct device *dev; -+ -+ pr_debug("%s: called\n", __func__); -+ -+ mm_vc_mem_phys_addr = phys_addr; -+ mm_vc_mem_size = mem_size; -+ mm_vc_mem_base = mem_base; -+ -+ vc_mem_get_size(); -+ -+ pr_info("vc-mem: phys_addr:0x%08lx mem_base=0x%08x mem_size:0x%08x(%u MiB)\n", -+ mm_vc_mem_phys_addr, mm_vc_mem_base, mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024)); -+ -+ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) { -+ pr_err("%s: alloc_chrdev_region failed (rc=%d)\n", -+ __func__, rc); -+ goto out_err; -+ } -+ -+ cdev_init(&vc_mem_cdev, &vc_mem_fops); -+ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) { -+ pr_err("%s: cdev_add failed (rc=%d)\n", __func__, rc); -+ goto out_unregister; -+ } -+ -+ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME); -+ if (IS_ERR(vc_mem_class)) { -+ rc = PTR_ERR(vc_mem_class); -+ pr_err("%s: class_create failed (rc=%d)\n", __func__, rc); -+ goto out_cdev_del; -+ } -+ -+ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL, -+ DRIVER_NAME); -+ if (IS_ERR(dev)) { -+ rc = PTR_ERR(dev); -+ pr_err("%s: device_create failed (rc=%d)\n", __func__, rc); -+ goto out_class_destroy; -+ } -+ -+#ifdef CONFIG_DEBUG_FS -+ /* don't fail if the debug entries cannot be created */ -+ vc_mem_debugfs_init(dev); -+#endif -+ -+ vc_mem_inited = 1; -+ return 0; -+ -+ device_destroy(vc_mem_class, vc_mem_devnum); -+ -+ out_class_destroy: -+ class_destroy(vc_mem_class); -+ vc_mem_class = NULL; -+ -+ out_cdev_del: -+ cdev_del(&vc_mem_cdev); -+ -+ out_unregister: -+ unregister_chrdev_region(vc_mem_devnum, 1); -+ -+ out_err: -+ return -1; -+} -+ -+/**************************************************************************** -+* -+* vc_mem_exit -+* -+***************************************************************************/ -+ -+static void __exit -+vc_mem_exit(void) -+{ -+ pr_debug("%s: called\n", __func__); -+ -+ if (vc_mem_inited) { -+#if CONFIG_DEBUG_FS -+ vc_mem_debugfs_deinit(); -+#endif -+ device_destroy(vc_mem_class, vc_mem_devnum); -+ class_destroy(vc_mem_class); -+ cdev_del(&vc_mem_cdev); -+ unregister_chrdev_region(vc_mem_devnum, 1); -+ } -+} -+ -+module_init(vc_mem_init); -+module_exit(vc_mem_exit); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Broadcom Corporation"); -+ -+module_param(phys_addr, uint, 0644); -+module_param(mem_size, uint, 0644); -+module_param(mem_base, uint, 0644); -diff --git a/arch/arm/mach-bcm2709/vc_support.c b/arch/arm/mach-bcm2709/vc_support.c -new file mode 100755 -index 0000000..0bc41c4 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vc_support.c -@@ -0,0 +1,318 @@ -+/* -+ * vc_support.c -+ * -+ * Created on: 25 Nov 2012 -+ * Author: Simon -+ */ -+ -+#include -+#include -+ -+#ifdef ECLIPSE_IGNORE -+ -+#define __user -+#define __init -+#define __exit -+#define __iomem -+#define KERN_DEBUG -+#define KERN_ERR -+#define KERN_WARNING -+#define KERN_INFO -+#define _IOWR(a, b, c) b -+#define _IOW(a, b, c) b -+#define _IO(a, b) b -+ -+#endif -+ -+/****** VC MAILBOX FUNCTIONALITY ******/ -+unsigned int AllocateVcMemory(unsigned int *pHandle, unsigned int size, unsigned int alignment, unsigned int flags) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_size; -+ unsigned int m_handle; -+ }; -+ unsigned int m_alignment; -+ unsigned int m_flags; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the allocation command -+ msg.m_tag.m_tagId = 0x3000c; -+ msg.m_tag.m_sendBufferSize = 12; -+ msg.m_tag.m_sendDataSize = 12; -+ -+ //fill in our args -+ msg.m_tag.m_args.m_size = size; -+ msg.m_tag.m_args.m_alignment = alignment; -+ msg.m_tag.m_args.m_flags = flags; -+ -+ //run the command -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ { -+ *pHandle = msg.m_tag.m_args.m_handle; -+ return 0; -+ } -+ else -+ { -+ printk(KERN_ERR "failed to allocate vc memory: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -+ -+unsigned int ReleaseVcMemory(unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_error; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the release command -+ msg.m_tag.m_tagId = 0x3000f; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) -+ return 0; -+ else -+ { -+ printk(KERN_ERR "failed to release vc memory: s=%d response=%08x recv data size=%08x error=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); -+ return 1; -+ } -+} -+ -+unsigned int LockVcMemory(unsigned int *pBusAddress, unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_busAddress; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the lock command -+ msg.m_tag.m_tagId = 0x3000d; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ { -+ //pick out the bus address -+ *pBusAddress = msg.m_tag.m_args.m_busAddress; -+ return 0; -+ } -+ else -+ { -+ printk(KERN_ERR "failed to lock vc memory: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -+ -+unsigned int UnlockVcMemory(unsigned int handle) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_handle; -+ unsigned int m_error; -+ }; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the unlock command -+ msg.m_tag.m_tagId = 0x3000e; -+ msg.m_tag.m_sendBufferSize = 4; -+ msg.m_tag.m_sendDataSize = 4; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_handle = handle; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ //check the error code too -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004 && msg.m_tag.m_args.m_error == 0) -+ return 0; -+ else -+ { -+ printk(KERN_ERR "failed to unlock vc memory: s=%d response=%08x recv data size=%08x error%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize, msg.m_tag.m_args.m_error); -+ return 1; -+ } -+} -+ -+unsigned int ExecuteVcCode(unsigned int code, -+ unsigned int r0, unsigned int r1, unsigned int r2, unsigned int r3, unsigned int r4, unsigned int r5) -+{ -+ struct vc_msg -+ { -+ unsigned int m_msgSize; -+ unsigned int m_response; -+ -+ struct vc_tag -+ { -+ unsigned int m_tagId; -+ unsigned int m_sendBufferSize; -+ union { -+ unsigned int m_sendDataSize; -+ unsigned int m_recvDataSize; -+ }; -+ -+ struct args -+ { -+ union { -+ unsigned int m_pCode; -+ unsigned int m_return; -+ }; -+ unsigned int m_r0; -+ unsigned int m_r1; -+ unsigned int m_r2; -+ unsigned int m_r3; -+ unsigned int m_r4; -+ unsigned int m_r5; -+ } m_args; -+ } m_tag; -+ -+ unsigned int m_endTag; -+ } msg; -+ int s; -+ -+ msg.m_msgSize = sizeof(msg); -+ msg.m_response = 0; -+ msg.m_endTag = 0; -+ -+ //fill in the tag for the unlock command -+ msg.m_tag.m_tagId = 0x30010; -+ msg.m_tag.m_sendBufferSize = 28; -+ msg.m_tag.m_sendDataSize = 28; -+ -+ //pass across the handle -+ msg.m_tag.m_args.m_pCode = code; -+ msg.m_tag.m_args.m_r0 = r0; -+ msg.m_tag.m_args.m_r1 = r1; -+ msg.m_tag.m_args.m_r2 = r2; -+ msg.m_tag.m_args.m_r3 = r3; -+ msg.m_tag.m_args.m_r4 = r4; -+ msg.m_tag.m_args.m_r5 = r5; -+ -+ s = bcm_mailbox_property(&msg, sizeof(msg)); -+ -+ //check the error code too -+ if (s == 0 && msg.m_response == 0x80000000 && msg.m_tag.m_recvDataSize == 0x80000004) -+ return msg.m_tag.m_args.m_return; -+ else -+ { -+ printk(KERN_ERR "failed to execute: s=%d response=%08x recv data size=%08x\n", -+ s, msg.m_response, msg.m_tag.m_recvDataSize); -+ return 1; -+ } -+} -diff --git a/arch/arm/mach-bcm2709/vcio.c b/arch/arm/mach-bcm2709/vcio.c -new file mode 100644 -index 0000000..5e43e85 ---- /dev/null -+++ b/arch/arm/mach-bcm2709/vcio.c -@@ -0,0 +1,474 @@ -+/* -+ * linux/arch/arm/mach-bcm2708/vcio.c -+ * -+ * Copyright (C) 2010 Broadcom -+ * -+ * 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. -+ * -+ * This device provides a shared mechanism for writing to the mailboxes, -+ * semaphores, doorbells etc. that are shared between the ARM and the -+ * VideoCore processor -+ */ -+ -+#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -+#define SUPPORT_SYSRQ -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+ -+#define DRIVER_NAME BCM_VCIO_DRIVER_NAME -+ -+/* ---------------------------------------------------------------------- -+ * Mailbox -+ * -------------------------------------------------------------------- */ -+ -+/* offsets from a mail box base address */ -+#define MAIL_WRT 0x00 /* write - and next 4 words */ -+#define MAIL_RD 0x00 /* read - and next 4 words */ -+#define MAIL_POL 0x10 /* read without popping the fifo */ -+#define MAIL_SND 0x14 /* sender ID (bottom two bits) */ -+#define MAIL_STA 0x18 /* status */ -+#define MAIL_CNF 0x1C /* configuration */ -+ -+#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) -+#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) -+#define MBOX_CHAN(msg) ((msg) & 0xf) -+#define MBOX_DATA28(msg) ((msg) & ~0xf) -+#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) -+ -+#define MBOX_MAGIC 0xd0d0c0de -+ -+struct vc_mailbox { -+ struct device *dev; /* parent device */ -+ void __iomem *status; -+ void __iomem *config; -+ void __iomem *read; -+ void __iomem *write; -+ uint32_t msg[MBOX_CHAN_COUNT]; -+ struct semaphore sema[MBOX_CHAN_COUNT]; -+ uint32_t magic; -+}; -+ -+static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev, -+ uint32_t addr_mbox) -+{ -+ int i; -+ -+ mbox_out->dev = dev; -+ mbox_out->status = __io_address(addr_mbox + MAIL_STA); -+ mbox_out->config = __io_address(addr_mbox + MAIL_CNF); -+ mbox_out->read = __io_address(addr_mbox + MAIL_RD); -+ /* Write to the other mailbox */ -+ mbox_out->write = -+ __io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) + -+ MAIL_WRT); -+ -+ for (i = 0; i < MBOX_CHAN_COUNT; i++) { -+ mbox_out->msg[i] = 0; -+ sema_init(&mbox_out->sema[i], 0); -+ } -+ -+ /* Enable the interrupt on data reception */ -+ writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->config); -+ -+ mbox_out->magic = MBOX_MAGIC; -+} -+ -+static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) -+{ -+ int rc; -+ -+ if (mbox->magic != MBOX_MAGIC) -+ rc = -EINVAL; -+ else { -+ /* wait for the mailbox FIFO to have some space in it */ -+ while (0 != (readl(mbox->status) & ARM_MS_FULL)) -+ cpu_relax(); -+ -+ writel(MBOX_MSG(chan, data28), mbox->write); -+ rc = 0; -+ } -+ return rc; -+} -+ -+static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) -+{ -+ int rc; -+ -+ if (mbox->magic != MBOX_MAGIC) -+ rc = -EINVAL; -+ else { -+ down(&mbox->sema[chan]); -+ *data28 = MBOX_DATA28(mbox->msg[chan]); -+ mbox->msg[chan] = 0; -+ rc = 0; -+ } -+ return rc; -+} -+ -+static irqreturn_t mbox_irq(int irq, void *dev_id) -+{ -+ /* wait for the mailbox FIFO to have some data in it */ -+ struct vc_mailbox *mbox = (struct vc_mailbox *) dev_id; -+ int status = readl(mbox->status); -+ int ret = IRQ_NONE; -+ -+ while (!(status & ARM_MS_EMPTY)) { -+ uint32_t msg = readl(mbox->read); -+ int chan = MBOX_CHAN(msg); -+ if (chan < MBOX_CHAN_COUNT) { -+ if (mbox->msg[chan]) { -+ /* Overflow */ -+ printk(KERN_ERR DRIVER_NAME -+ ": mbox chan %d overflow - drop %08x\n", -+ chan, msg); -+ } else { -+ mbox->msg[chan] = (msg | 0xf); -+ up(&mbox->sema[chan]); -+ } -+ } else { -+ printk(KERN_ERR DRIVER_NAME -+ ": invalid channel selector (msg %08x)\n", msg); -+ } -+ ret = IRQ_HANDLED; -+ status = readl(mbox->status); -+ } -+ return ret; -+} -+ -+static struct irqaction mbox_irqaction = { -+ .name = "ARM Mailbox IRQ", -+ .flags = IRQF_DISABLED | IRQF_IRQPOLL, -+ .handler = mbox_irq, -+}; -+ -+/* ---------------------------------------------------------------------- -+ * Mailbox Methods -+ * -------------------------------------------------------------------- */ -+ -+static struct device *mbox_dev; /* we assume there's only one! */ -+ -+static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) -+{ -+ int rc; -+ -+ struct vc_mailbox *mailbox = dev_get_drvdata(dev); -+ device_lock(dev); -+ rc = mbox_write(mailbox, chan, data28); -+ device_unlock(dev); -+ -+ return rc; -+} -+ -+static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) -+{ -+ int rc; -+ -+ struct vc_mailbox *mailbox = dev_get_drvdata(dev); -+ device_lock(dev); -+ rc = mbox_read(mailbox, chan, data28); -+ device_unlock(dev); -+ -+ return rc; -+} -+ -+extern int bcm_mailbox_write(unsigned chan, uint32_t data28) -+{ -+ if (mbox_dev) -+ return dev_mbox_write(mbox_dev, chan, data28); -+ else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_write); -+ -+extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) -+{ -+ if (mbox_dev) -+ return dev_mbox_read(mbox_dev, chan, data28); -+ else -+ return -ENODEV; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_read); -+ -+static void dev_mbox_register(const char *dev_name, struct device *dev) -+{ -+ mbox_dev = dev; -+} -+ -+static int mbox_copy_from_user(void *dst, const void *src, int size) -+{ -+ if ( (uint32_t)src < TASK_SIZE) -+ { -+ return copy_from_user(dst, src, size); -+ } -+ else -+ { -+ memcpy( dst, src, size ); -+ return 0; -+ } -+} -+ -+static int mbox_copy_to_user(void *dst, const void *src, int size) -+{ -+ if ( (uint32_t)dst < TASK_SIZE) -+ { -+ return copy_to_user(dst, src, size); -+ } -+ else -+ { -+ memcpy( dst, src, size ); -+ return 0; -+ } -+} -+ -+static DEFINE_MUTEX(mailbox_lock); -+extern int bcm_mailbox_property(void *data, int size) -+{ -+ uint32_t success; -+ dma_addr_t mem_bus; /* the memory address accessed from videocore */ -+ void *mem_kern; /* the memory address accessed from driver */ -+ int s = 0; -+ -+ mutex_lock(&mailbox_lock); -+ /* allocate some memory for the messages communicating with GPU */ -+ mem_kern = dma_alloc_coherent(NULL, PAGE_ALIGN(size), &mem_bus, GFP_ATOMIC); -+ if (mem_kern) { -+ /* create the message */ -+ mbox_copy_from_user(mem_kern, data, size); -+ -+ /* send the message */ -+ wmb(); -+ s = bcm_mailbox_write(MBOX_CHAN_PROPERTY, (uint32_t)mem_bus); -+ if (s == 0) { -+ s = bcm_mailbox_read(MBOX_CHAN_PROPERTY, &success); -+ } -+ if (s == 0) { -+ /* copy the response */ -+ rmb(); -+ mbox_copy_to_user(data, mem_kern, size); -+ } -+ dma_free_coherent(NULL, PAGE_ALIGN(size), mem_kern, mem_bus); -+ } else { -+ s = -ENOMEM; -+ } -+ if (s != 0) -+ printk(KERN_ERR DRIVER_NAME ": %s failed (%d)\n", __func__, s); -+ -+ mutex_unlock(&mailbox_lock); -+ return s; -+} -+EXPORT_SYMBOL_GPL(bcm_mailbox_property); -+ -+/* ---------------------------------------------------------------------- -+ * Platform Device for Mailbox -+ * -------------------------------------------------------------------- */ -+ -+/* -+ * Is the device open right now? Used to prevent -+ * concurent access into the same device -+ */ -+static int Device_Open = 0; -+ -+/* -+ * This is called whenever a process attempts to open the device file -+ */ -+static int device_open(struct inode *inode, struct file *file) -+{ -+ /* -+ * We don't want to talk to two processes at the same time -+ */ -+ if (Device_Open) -+ return -EBUSY; -+ -+ Device_Open++; -+ /* -+ * Initialize the message -+ */ -+ try_module_get(THIS_MODULE); -+ return 0; -+} -+ -+static int device_release(struct inode *inode, struct file *file) -+{ -+ /* -+ * We're now ready for our next caller -+ */ -+ Device_Open--; -+ -+ module_put(THIS_MODULE); -+ return 0; -+} -+ -+/* -+ * This function is called whenever a process tries to do an ioctl on our -+ * device file. We get two extra parameters (additional to the inode and file -+ * structures, which all device functions get): the number of the ioctl called -+ * and the parameter given to the ioctl function. -+ * -+ * If the ioctl is write or read/write (meaning output is returned to the -+ * calling process), the ioctl call returns the output of this function. -+ * -+ */ -+static long device_ioctl(struct file *file, /* see include/linux/fs.h */ -+ unsigned int ioctl_num, /* number and param for ioctl */ -+ unsigned long ioctl_param) -+{ -+ unsigned size; -+ /* -+ * Switch according to the ioctl called -+ */ -+ switch (ioctl_num) { -+ case IOCTL_MBOX_PROPERTY: -+ /* -+ * Receive a pointer to a message (in user space) and set that -+ * to be the device's message. Get the parameter given to -+ * ioctl by the process. -+ */ -+ mbox_copy_from_user(&size, (void *)ioctl_param, sizeof size); -+ return bcm_mailbox_property((void *)ioctl_param, size); -+ break; -+ default: -+ printk(KERN_ERR DRIVER_NAME "unknown ioctl: %d\n", ioctl_num); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* Module Declarations */ -+ -+/* -+ * This structure will hold the functions to be called -+ * when a process does something to the device we -+ * created. Since a pointer to this structure is kept in -+ * the devices table, it can't be local to -+ * init_module. NULL is for unimplemented functios. -+ */ -+struct file_operations fops = { -+ .unlocked_ioctl = device_ioctl, -+ .open = device_open, -+ .release = device_release, /* a.k.a. close */ -+}; -+ -+static int bcm_vcio_probe(struct platform_device *pdev) -+{ -+ int ret = 0; -+ struct vc_mailbox *mailbox; -+ -+ mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); -+ if (NULL == mailbox) { -+ printk(KERN_ERR DRIVER_NAME ": failed to allocate " -+ "mailbox memory\n"); -+ ret = -ENOMEM; -+ } else { -+ struct resource *res; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (res == NULL) { -+ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " -+ "resource\n"); -+ ret = -ENODEV; -+ kfree(mailbox); -+ } else { -+ /* should be based on the registers from res really */ -+ mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD); -+ -+ platform_set_drvdata(pdev, mailbox); -+ dev_mbox_register(DRIVER_NAME, &pdev->dev); -+ -+ mbox_irqaction.dev_id = mailbox; -+ setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction); -+ printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n", -+ __io_address(ARM_0_MAIL0_RD)); -+ } -+ } -+ -+ if (ret == 0) { -+ /* -+ * Register the character device -+ */ -+ ret = register_chrdev(MAJOR_NUM, DEVICE_FILE_NAME, &fops); -+ -+ /* -+ * Negative values signify an error -+ */ -+ if (ret < 0) { -+ printk(KERN_ERR DRIVER_NAME -+ "Failed registering the character device %d\n", ret); -+ return ret; -+ } -+ } -+ return ret; -+} -+ -+static int bcm_vcio_remove(struct platform_device *pdev) -+{ -+ struct vc_mailbox *mailbox = platform_get_drvdata(pdev); -+ -+ platform_set_drvdata(pdev, NULL); -+ kfree(mailbox); -+ -+ return 0; -+} -+ -+static struct platform_driver bcm_mbox_driver = { -+ .probe = bcm_vcio_probe, -+ .remove = bcm_vcio_remove, -+ -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ }, -+}; -+ -+static int __init bcm_mbox_init(void) -+{ -+ int ret; -+ -+ printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n"); -+ -+ ret = platform_driver_register(&bcm_mbox_driver); -+ if (ret != 0) { -+ printk(KERN_ERR DRIVER_NAME ": failed to register " -+ "on platform\n"); -+ } -+ -+ return ret; -+} -+ -+static void __exit bcm_mbox_exit(void) -+{ -+ platform_driver_unregister(&bcm_mbox_driver); -+} -+ -+arch_initcall(bcm_mbox_init); /* Initialize early */ -+module_exit(bcm_mbox_exit); -+ -+MODULE_AUTHOR("Gray Girling"); -+MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); -+MODULE_LICENSE("GPL"); -+MODULE_ALIAS("platform:bcm-mbox"); -diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S -index 8b4ee5e..80fa74b 100644 ---- a/arch/arm/mm/proc-v7.S -+++ b/arch/arm/mm/proc-v7.S -@@ -441,6 +441,7 @@ __v7_setup: - orr r0, r0, r6 @ set them - THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions - ret lr @ return to head.S:__ret -+ .space 256 - ENDPROC(__v7_setup) - - .align 2 -diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types -index c9ddd87..bfc397c 100644 ---- a/arch/arm/tools/mach-types -+++ b/arch/arm/tools/mach-types -@@ -523,6 +523,7 @@ prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103 - paz00 MACH_PAZ00 PAZ00 3128 - acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129 - bcm2708 MACH_BCM2708 BCM2708 3138 -+bcm2709 MACH_BCM2709 BCM2709 3139 - ag5evm MACH_AG5EVM AG5EVM 3189 - ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206 - wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207 -diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig -index 2a24bec..1c97093 100644 ---- a/drivers/char/hw_random/Kconfig -+++ b/drivers/char/hw_random/Kconfig -@@ -322,7 +322,7 @@ config HW_RANDOM_TPM - - config HW_RANDOM_BCM2708 - tristate "BCM2708 generic true random number generator support" -- depends on HW_RANDOM && ARCH_BCM2708 -+ depends on HW_RANDOM && (ARCH_BCM2708 || ARCH_BCM2709) - ---help--- - This driver provides the kernel-side support for the BCM2708 hardware. - -diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c -index 095c177..47d96dd 100644 ---- a/drivers/clocksource/arm_arch_timer.c -+++ b/drivers/clocksource/arm_arch_timer.c -@@ -803,3 +803,39 @@ static void __init arch_timer_mem_init(struct device_node *np) - } - CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", - arch_timer_mem_init); -+ -+int __init dc4_arch_timer_init(void) -+{ -+ if (arch_timers_present & ARCH_CP15_TIMER) { -+ pr_warn("arch_timer: multiple nodes in dt, skipping\n"); -+ return -1; -+ } -+ -+ arch_timers_present |= ARCH_CP15_TIMER; -+ -+ /* Try to determine the frequency from the device tree or CNTFRQ */ -+ arch_timer_rate = 19200000; -+ -+ arch_timer_ppi[PHYS_SECURE_PPI] = IRQ_ARM_LOCAL_CNTPSIRQ; -+ arch_timer_ppi[PHYS_NONSECURE_PPI] = IRQ_ARM_LOCAL_CNTPNSIRQ; -+ arch_timer_ppi[VIRT_PPI] = IRQ_ARM_LOCAL_CNTVIRQ; -+ arch_timer_ppi[HYP_PPI] = IRQ_ARM_LOCAL_CNTHPIRQ; -+ -+ /* -+ * If HYP mode is available, we know that the physical timer -+ * has been configured to be accessible from PL1. Use it, so -+ * that a guest can use the virtual timer instead. -+ * -+ * If no interrupt provided for virtual timer, we'll have to -+ * stick to the physical timer. It'd better be accessible... -+ */ -+ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) { -+ arch_timer_use_virtual = false; -+ } -+ -+ arch_timer_c3stop = 0; -+ -+ arch_timer_register(); -+ arch_timer_common_init(); -+ return 0; -+} -diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig -index 5b9e196..b3d8b8e 100644 ---- a/drivers/dma/Kconfig -+++ b/drivers/dma/Kconfig -@@ -339,7 +339,7 @@ config DMA_BCM2835 - - config DMA_BCM2708 - tristate "BCM2708 DMA engine support" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - select DMA_ENGINE - select DMA_VIRTUAL_CHANNELS - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 7b7ba6f..a77b009 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -362,7 +362,7 @@ config I2C_AXXIA - - config I2C_BCM2835 - tristate "Broadcom BCM2835 I2C controller" -- depends on ARCH_BCM2835 || ARCH_BCM2708 -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 - help - If you say yes to this option, support will be included for the - BCM2835 I2C controller. -@@ -374,7 +374,7 @@ config I2C_BCM2835 - - config I2C_BCM2708 - tristate "BCM2708 BSC" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - help - Enabling this option will add BSC (Broadcom Serial Controller) - support for the BCM2708. BSC is a Broadcom proprietary bus compatible -diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig -index a8fd172..2cb1a68 100644 ---- a/drivers/media/platform/bcm2835/Kconfig -+++ b/drivers/media/platform/bcm2835/Kconfig -@@ -2,7 +2,7 @@ - - config VIDEO_BCM2835 - bool "Broadcom BCM2835 camera interface driver" -- depends on VIDEO_V4L2 && ARCH_BCM2708 -+ depends on VIDEO_V4L2 && (ARCH_BCM2708 || ARCH_BCM2709) - ---help--- - Say Y here to enable camera host interface devices for - Broadcom BCM2835 SoC. This operates over the VCHIQ interface -diff --git a/drivers/misc/vc04_services/Kconfig b/drivers/misc/vc04_services/Kconfig -index 2663933..b94e6cd 100644 ---- a/drivers/misc/vc04_services/Kconfig -+++ b/drivers/misc/vc04_services/Kconfig -@@ -1,6 +1,6 @@ - config BCM2708_VCHIQ - tristate "Videocore VCHIQ" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - default y - help - Kernel to VideoCore communication interface for the -diff --git a/drivers/misc/vc04_services/Makefile b/drivers/misc/vc04_services/Makefile -index 0c82520..8d038fe 100644 ---- a/drivers/misc/vc04_services/Makefile -+++ b/drivers/misc/vc04_services/Makefile -@@ -1,5 +1,3 @@ --ifeq ($(CONFIG_MACH_BCM2708),y) -- - obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o - - vchiq-objs := \ -@@ -14,4 +12,3 @@ vchiq-objs := \ - - ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 - --endif -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index b6077b5..e2b73a4 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -304,7 +304,7 @@ config MMC_SDHCI_ST - - config MMC_BCM2835 - tristate "MMC support on BCM2835" -- depends on MACH_BCM2708 -+ depends on (MACH_BCM2708 || MACH_BCM2709) - help - This selects the MMC Interface on BCM2835. - -diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig -index 9f202dd..c18385c 100644 ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -77,7 +77,7 @@ config SPI_ATMEL - - config SPI_BCM2835 - tristate "BCM2835 SPI controller" -- depends on ARCH_BCM2835 || ARCH_BCM2708 || COMPILE_TEST -+ depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST - help - This selects a driver for the Broadcom BCM2835 SPI master. - -@@ -88,7 +88,7 @@ config SPI_BCM2835 - - config SPI_BCM2708 - tristate "BCM2708 SPI controller driver (SPI0)" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - help - This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This - driver is not compatible with the "Universal SPI Master" or the SPI slave -diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig -index 3bc50f2..cd504ee 100644 ---- a/drivers/watchdog/Kconfig -+++ b/drivers/watchdog/Kconfig -@@ -454,7 +454,7 @@ config RETU_WATCHDOG - - config BCM2708_WDT - tristate "BCM2708 Watchdog" -- depends on ARCH_BCM2708 -+ depends on ARCH_BCM2708 || ARCH_BCM2709 - help - Enables BCM2708 watchdog support. - -diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig -index f7ceafd..ada7ba2 100644 ---- a/sound/arm/Kconfig -+++ b/sound/arm/Kconfig -@@ -41,7 +41,7 @@ config SND_PXA2XX_AC97 - - config SND_BCM2835 - tristate "BCM2835 ALSA driver" -- depends on ARCH_BCM2708 && BCM2708_VCHIQ && SND -+ depends on (ARCH_BCM2708 || ARCH_BCM2709) && BCM2708_VCHIQ && SND - select SND_PCM - help - Say Y or M if you want to support BCM2835 Alsa pcm card driver -diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig -index a562ddf..40d27c1 100644 ---- a/sound/soc/bcm/Kconfig -+++ b/sound/soc/bcm/Kconfig -@@ -10,7 +10,7 @@ config SND_BCM2835_SOC_I2S - - config SND_BCM2708_SOC_I2S - tristate "SoC Audio support for the Broadcom BCM2708 I2S module" -- depends on MACH_BCM2708 -+ depends on MACH_BCM2708 || MACH_BCM2709 - select REGMAP_MMIO - select SND_SOC_DMAENGINE_PCM - select SND_SOC_GENERIC_DMAENGINE_PCM - -From 3e5e6153b4da56ee47cb1becb41f40ad61d569b2 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Fri, 6 Feb 2015 13:50:57 +0000 -Subject: [PATCH 086/172] BCM270x_DT: Add pwr_led, and the required "input" - trigger - -The "input" trigger makes the associated GPIO an input. This is to support -the Raspberry Pi PWR LED, which is driven by external hardware in normal use. - -N.B. pwr_led is not available on Model A or B boards. ---- - arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 18 ++++++++- - arch/arm/boot/dts/bcm2708-rpi-b.dts | 8 +++- - arch/arm/boot/dts/bcm2708.dtsi | 5 --- - arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 18 ++++++++- - arch/arm/boot/dts/bcm2709.dtsi | 5 --- - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - drivers/leds/trigger/Kconfig | 7 ++++ - drivers/leds/trigger/Makefile | 1 + - drivers/leds/trigger/ledtrig-input.c | 65 ++++++++++++++++++++++++++++++++ - 10 files changed, 113 insertions(+), 16 deletions(-) - create mode 100644 drivers/leds/trigger/ledtrig-input.c - -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -index 95f03ba..b409c2c 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts -@@ -83,8 +83,18 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 47 0>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+ -+ pwr_led: pwr { -+ label = "led1"; -+ linux,default-trigger = "input"; -+ gpios = <&gpio 35 0>; -+ }; - }; - - / { -@@ -97,5 +107,9 @@ - act_led_gpio = <&act_led>,"gpios:4"; - act_led_activelow = <&act_led>,"gpios:8"; - act_led_trigger = <&act_led>,"linux,default-trigger"; + + pwr_led_gpio = <&pwr_led>,"gpios:4"; + pwr_led_activelow = <&pwr_led>,"gpios:8"; + pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; - }; - }; -diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts -index 0631f45..1ecd1a1 100644 ---- a/arch/arm/boot/dts/bcm2708-rpi-b.dts -+++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts -@@ -83,8 +83,12 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 16 1>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 16 1>; + }; - }; - - / { -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index af6b1d9..aeeabae 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -79,11 +79,6 @@ - - leds: leds { - compatible = "gpio-leds"; -- -- act_led: act { -- label = "led0"; -- linux,default-trigger = "mmc0"; -- }; - }; - }; - -diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -index 0cdff31..46f4908 100644 ---- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -+++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts -@@ -83,8 +83,18 @@ - pinctrl-0 = <&i2s_pins>; - }; - --&act_led { -- gpios = <&gpio 47 0>; -+&leds { -+ act_led: act { -+ label = "led0"; -+ linux,default-trigger = "mmc0"; -+ gpios = <&gpio 47 0>; -+ }; -+ -+ pwr_led: pwr { -+ label = "led1"; -+ linux,default-trigger = "input"; -+ gpios = <&gpio 35 0>; -+ }; - }; - - / { -@@ -97,5 +107,9 @@ - act_led_gpio = <&act_led>,"gpios:4"; - act_led_activelow = <&act_led>,"gpios:8"; - act_led_trigger = <&act_led>,"linux,default-trigger"; -+ -+ pwr_led_gpio = <&pwr_led>,"gpios:4"; -+ pwr_led_activelow = <&pwr_led>,"gpios:8"; -+ pwr_led_trigger = <&pwr_led>,"linux,default-trigger"; - }; - }; ++}; diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -index fee2053..6986455 100644 +index 15c7e65..34d2226 100644 --- a/arch/arm/boot/dts/bcm2709.dtsi +++ b/arch/arm/boot/dts/bcm2709.dtsi @@ -79,11 +79,6 @@ @@ -136665,32 +125466,8 @@ index fee2053..6986455 100644 - linux,default-trigger = "mmc0"; - }; }; - }; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index 62f33d6..d4c18fa 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -962,6 +962,7 @@ CONFIG_LEDS_TRIGGER_GPIO=y - CONFIG_LEDS_TRIGGER_DEFAULT_ON=y - CONFIG_LEDS_TRIGGER_TRANSIENT=m - CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_LEDS_TRIGGER_INPUT=y - CONFIG_RTC_CLASS=y - # CONFIG_RTC_HCTOSYS is not set - CONFIG_RTC_DRV_DS1307=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index c1621fa..898ebd2 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -955,6 +955,7 @@ CONFIG_LEDS_TRIGGER_GPIO=y - CONFIG_LEDS_TRIGGER_DEFAULT_ON=y - CONFIG_LEDS_TRIGGER_TRANSIENT=m - CONFIG_LEDS_TRIGGER_CAMERA=m -+CONFIG_LEDS_TRIGGER_INPUT=y - CONFIG_RTC_CLASS=y - # CONFIG_RTC_HCTOSYS is not set - CONFIG_RTC_DRV_DS1307=m + arm-pmu { diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig index 49794b4..640756b 100644 --- a/drivers/leds/trigger/Kconfig @@ -136788,10 +125565,10 @@ index 0000000..2ca2b98 +MODULE_DESCRIPTION("Set LED GPIO to Input \"trigger\""); +MODULE_LICENSE("GPL"); -From d3db7dad4705421430c8dc3ba6853038a3988f99 Mon Sep 17 00:00:00 2001 +From 97a041da533b36ce09dc0a7d7d49017a71d2ac76 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 20 Jun 2014 17:19:27 +0100 -Subject: [PATCH 087/172] bcm2709: Simplify and strip down IRQ handler +Subject: [PATCH 072/104] bcm2709: Simplify and strip down IRQ handler --- arch/arm/include/asm/entry-macro-multi.S | 2 + @@ -137008,823 +125785,10 @@ index d08591b..101d9f1 100644 +1: get_irqnr_and_base r0, r2, r6, lr + .endm -From b54978ca2faf286b4a3836a232139140f514362f Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 24 Sep 2014 11:57:51 +0100 -Subject: [PATCH 088/172] dwc_otg: FIQ support on SMP. Set up FIQ stack and - handler on Core 0 only. - ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 96 ++++++++++++++-------------- - 1 file changed, 49 insertions(+), 47 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index 98e1dc5..4d8dd95 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -397,7 +397,55 @@ static struct fiq_handler fh = { - .name = "usb_fiq", - }; - -+static void hcd_init_fiq(void *cookie) -+{ -+ dwc_otg_device_t *otg_dev = cookie; -+ dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; -+ struct pt_regs regs; -+ -+ if (claim_fiq(&fh)) { -+ DWC_ERROR("Can't claim FIQ"); -+ BUG(); -+ } -+ DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -+ set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -+ memset(®s,0,sizeof(regs)); -+ -+ regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -+ if (fiq_fsm_enable) { -+ regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -+ //regs.ARM_r10 = dwc_otg_hcd->dma; -+ regs.ARM_fp = (long) dwc_otg_fiq_fsm; -+ } else { -+ regs.ARM_fp = (long) dwc_otg_fiq_nop; -+ } - -+ regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -+ -+// __show_regs(®s); -+ set_fiq_regs(®s); -+ -+ //Set the mphi periph to the required registers -+ dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -+ dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -+ dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -+ dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -+ dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -+ dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -+ DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -+ //Enable mphi peripheral -+ writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); -+#ifdef DEBUG -+ if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -+ DWC_WARN("MPHI periph has been enabled"); -+ else -+ DWC_WARN("MPHI periph has NOT been enabled"); -+#endif -+ // Enable FIQ interrupt from USB peripheral -+ enable_fiq(INTERRUPT_VC_USB); -+ local_fiq_enable(); -+} - - /** - * Initializes the HCD. This function allocates memory for and initializes the -@@ -412,7 +460,6 @@ int hcd_init(dwc_bus_dev_t *_dev) - dwc_otg_device_t *otg_dev = DWC_OTG_BUSDRVDATA(_dev); - int retval = 0; - u64 dmamask; -- struct pt_regs regs; - - DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); - -@@ -464,52 +511,7 @@ int hcd_init(dwc_bus_dev_t *_dev) - } - - if (fiq_enable) -- { -- if (claim_fiq(&fh)) { -- DWC_ERROR("Can't claim FIQ"); -- goto error2; -- } -- -- DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -- DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); -- -- set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); -- memset(®s,0,sizeof(regs)); -- -- regs.ARM_r8 = (long) dwc_otg_hcd->fiq_state; -- if (fiq_fsm_enable) { -- regs.ARM_r9 = dwc_otg_hcd->core_if->core_params->host_channels; -- //regs.ARM_r10 = dwc_otg_hcd->dma; -- regs.ARM_fp = (long) dwc_otg_fiq_fsm; -- } else { -- regs.ARM_fp = (long) dwc_otg_fiq_nop; -- } -- -- regs.ARM_sp = (long) dwc_otg_hcd->fiq_stack + (sizeof(struct fiq_stack) - 4); -- --// __show_regs(®s); -- set_fiq_regs(®s); -- -- //Set the mphi periph to the required registers -- dwc_otg_hcd->fiq_state->mphi_regs.base = otg_dev->os_dep.mphi_base; -- dwc_otg_hcd->fiq_state->mphi_regs.ctrl = otg_dev->os_dep.mphi_base + 0x4c; -- dwc_otg_hcd->fiq_state->mphi_regs.outdda = otg_dev->os_dep.mphi_base + 0x28; -- dwc_otg_hcd->fiq_state->mphi_regs.outddb = otg_dev->os_dep.mphi_base + 0x2c; -- dwc_otg_hcd->fiq_state->mphi_regs.intstat = otg_dev->os_dep.mphi_base + 0x50; -- dwc_otg_hcd->fiq_state->dwc_regs_base = otg_dev->os_dep.base; -- DWC_WARN("MPHI regs_base at 0x%08x", (int)dwc_otg_hcd->fiq_state->mphi_regs.base); -- //Enable mphi peripheral -- writel((1<<31),dwc_otg_hcd->fiq_state->mphi_regs.ctrl); --#ifdef DEBUG -- if (readl(dwc_otg_hcd->fiq_state->mphi_regs.ctrl) & 0x80000000) -- DWC_WARN("MPHI periph has been enabled"); -- else -- DWC_WARN("MPHI periph has NOT been enabled"); --#endif -- // Enable FIQ interrupt from USB peripheral -- enable_fiq(INTERRUPT_VC_USB); -- local_fiq_enable(); -- } -+ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); - - - otg_dev->hcd->otg_dev = otg_dev; - -From e012a834d4a47ffe38f00d1a48da854680d51be6 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Fri, 26 Sep 2014 11:32:09 +0100 -Subject: [PATCH 089/172] dwc_otg: introduce fiq_fsm_spin(un|)lock() - -SMP safety for the FIQ relies on register read-modify write cycles being -completed in the correct order. Several places in the DWC code modify -registers also touched by the FIQ. Protect these by a bare-bones lock -mechanism. - -This also makes it possible to run the FIQ and IRQ handlers on different -cores. ---- - .../usb/host/dwc_common_port/dwc_common_linux.c | 6 --- - drivers/usb/host/dwc_otg/dwc_otg_cil.c | 10 ----- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 46 +++++++++++++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 16 +++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 23 ++++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 9 ++++- - 6 files changed, 88 insertions(+), 22 deletions(-) - -diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -index 5c50a8b..b802042 100644 ---- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c -+++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c -@@ -580,13 +580,7 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) - - void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) - { -- unsigned long flags; -- -- local_irq_save(flags); -- local_fiq_disable(); - writel((readl(reg) & ~clear_mask) | set_mask, reg); -- local_fiq_enable(); -- local_irq_restore(flags); - } - - #if 0 -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil.c b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -index 6a32c5c..e40060f 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_cil.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c -@@ -2244,9 +2244,7 @@ void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if) - */ - void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) - { -- uint32_t intr_enable; - hcintmsk_data_t hc_intr_mask; -- gintmsk_data_t gintmsk = {.d32 = 0 }; - hcchar_data_t hcchar; - hcsplt_data_t hcsplt; - -@@ -2348,14 +2346,6 @@ void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) - } - DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32); - -- /* Enable the top level host channel interrupt. */ -- intr_enable = (1 << hc_num); -- DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable); -- -- /* Make sure host channel interrupts are enabled. */ -- gintmsk.b.hcintr = 1; -- DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -- - /* - * Program the HCCHARn register with the endpoint characteristics for - * the current transfer. -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 284f902..84618a5 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -75,6 +75,46 @@ void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state - } - - /** -+ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock -+ * Must be called with local interrupts and FIQ disabled. -+ */ -+inline void fiq_fsm_spin_lock(fiq_lock_t *lock) -+{ -+ unsigned long tmp; -+ uint32_t newval; -+ fiq_lock_t lockval; -+ smp_mb__before_spinlock(); -+ /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable -+ * will be sufficient. If we are on a different CPU, then the lock protects us. */ -+ prefetchw(&lock->slock); -+ asm volatile ( -+ "1: ldrex %0, [%3]\n" -+ " add %1, %0, %4\n" -+ " strex %2, %1, [%3]\n" -+ " teq %2, #0\n" -+ " bne 1b" -+ : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) -+ : "r" (&lock->slock), "I" (1 << 16) -+ : "cc"); -+ -+ while (lockval.tickets.next != lockval.tickets.owner) { -+ wfe(); -+ lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); -+ } -+ smp_mb(); -+} -+ -+/** -+ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock -+ */ -+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) -+{ -+ smp_mb(); -+ lock->tickets.owner++; -+ dsb_sev(); -+} -+ -+/** - * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction - * @channel: channel to re-enable - */ -@@ -1142,6 +1182,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - gintsts_handled.d32 = 0; - haint_handled.d32 = 0; - -+ fiq_fsm_spin_lock(&state->lock); - gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); - gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); - gintsts.d32 &= gintmsk.d32; -@@ -1231,7 +1272,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - - } - state->fiq_done++; -- mb(); -+ fiq_fsm_spin_unlock(&state->lock); - } - - -@@ -1253,6 +1294,7 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - gintmsk_data_t gintmsk; - hfnum_data_t hfnum; - -+ fiq_fsm_spin_lock(&state->lock); - hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM); - gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS); - gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK); -@@ -1290,5 +1332,5 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - - } - state->fiq_done++; -- mb(); -+ fiq_fsm_spin_unlock(&state->lock); - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -index 5c7707f..8455324 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h -@@ -120,7 +120,6 @@ typedef struct { - volatile void* intstat; - } mphi_regs_t; - -- - enum fiq_debug_level { - FIQDBG_SCHED = (1 << 0), - FIQDBG_INT = (1 << 1), -@@ -128,6 +127,16 @@ enum fiq_debug_level { - FIQDBG_PORTHUB = (1 << 3), - }; - -+typedef struct { -+ union { -+ uint32_t slock; -+ struct _tickets { -+ uint16_t owner; -+ uint16_t next; -+ } tickets; -+ }; -+} fiq_lock_t; -+ - struct fiq_state; - - extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...); -@@ -324,6 +333,7 @@ struct fiq_channel_state { - * It contains top-level state information. - */ - struct fiq_state { -+ fiq_lock_t lock; - mphi_regs_t mphi_regs; - void *dwc_regs_base; - dma_addr_t dma_base; -@@ -342,6 +352,10 @@ struct fiq_state { - struct fiq_channel_state channel[0]; - }; - -+extern void fiq_fsm_spin_lock(fiq_lock_t *lock); -+ -+extern void fiq_fsm_spin_unlock(fiq_lock_t *lock); -+ - extern int fiq_fsm_too_late(struct fiq_state *st, int n); - - extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n); -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 68d4f3b..124ac16 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1184,6 +1184,9 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - dwc_otg_qtd_t *qtd; - dwc_otg_hcd_urb_t *urb; - void* ptr = NULL; -+ uint32_t intr_enable; -+ unsigned long flags; -+ gintmsk_data_t gintmsk = { .d32 = 0, }; - - qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); - -@@ -1409,6 +1412,20 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hc->desc_list_addr = qh->desc_list_dma; - - dwc_otg_hc_init(hcd->core_if, hc); -+ -+ local_irq_save(flags); -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ /* Enable the top level host channel interrupt. */ -+ intr_enable = (1 << hc->hc_num); -+ DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable); -+ -+ /* Make sure host channel interrupts are enabled. */ -+ gintmsk.b.hcintr = 1; -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ local_irq_restore(flags); - hc->qh = qh; - } - -@@ -1659,6 +1676,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32); - hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); - local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); - DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32); - DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32); -@@ -1676,6 +1694,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - } - mb(); - st->hcchar_copy.b.chen = 0; -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - } -@@ -1811,7 +1830,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32); - - local_fiq_disable(); -- mb(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); - - if (hc->ep_type & 0x1) { - hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum); -@@ -1909,7 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcchar_copy.b.chen = 1; - DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); - } -- mb(); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index a5566bc..ee35196 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -101,6 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - if (dwc_otg_is_host_mode(core_if)) { - if (fiq_enable) { - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - /* Pull in from the FIQ's disabled mask */ - gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32); - dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0; -@@ -116,8 +117,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - } - gintsts.d32 &= gintmsk.d32; - -- if (fiq_enable) -+ if (fiq_enable) { -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); -+ } - - if (!gintsts.d32) { - goto exit_handler_routine; -@@ -200,6 +203,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - gintmsk_data_t gintmsk_new; - haintmsk_data_t haintmsk_new; - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32; - if(fiq_fsm_enable) - haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32; -@@ -222,6 +226,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk); - /* Re-enable interrupts that the FIQ masked (first time round) */ - FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32); -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); - - if ((jiffies / HZ) > last_time) { -@@ -633,8 +638,10 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) - { - /* check the mask? */ - local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); - haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint); - dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0; -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); - local_fiq_enable(); - } - - -From 2180b4febd684a72e2ef217fdb87a26dcc4073dc Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 11:59:41 +0000 -Subject: [PATCH 090/172] fiq_fsm: fix build on bcm2708 and bcm2709 platforms - ---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 84618a5..0d2b04e 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -78,6 +78,7 @@ void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state - * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock - * Must be called with local interrupts and FIQ disabled. - */ -+#ifdef CONFIG_ARCH_BCM2709 - inline void fiq_fsm_spin_lock(fiq_lock_t *lock) - { - unsigned long tmp; -@@ -103,16 +104,23 @@ inline void fiq_fsm_spin_lock(fiq_lock_t *lock) - } - smp_mb(); - } -+#else -+inline void fiq_fsm_spin_lock(fiq_lock_t *lock) { } -+#endif - - /** - * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock - */ -+#ifdef CONFIG_ARCH_BCM2709 - inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) - { - smp_mb(); - lock->tickets.owner++; - dsb_sev(); - } -+#else -+inline void fiq_fsm_spin_unlock(fiq_lock_t *lock) { } -+#endif - - /** - * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction - -From e4d3c223f680bb7a75fc99d53603923afb28b1dd Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 17:49:23 +0000 -Subject: [PATCH 091/172] dwc_otg: put some barriers back where they should be - for UP - ---- - drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 2 ++ - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 1 + - 2 files changed, 3 insertions(+) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -index 0d2b04e..276ad0c7 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c -@@ -1280,6 +1280,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels) - - } - state->fiq_done++; -+ mb(); - fiq_fsm_spin_unlock(&state->lock); - } - -@@ -1340,5 +1341,6 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state) - - } - state->fiq_done++; -+ mb(); - fiq_fsm_spin_unlock(&state->lock); - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index 124ac16..ac70f1d 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -1928,6 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) - st->hcchar_copy.b.chen = 1; - DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32); - } -+ mb(); - fiq_fsm_spin_unlock(&hcd->fiq_state->lock); - local_fiq_enable(); - return 0; - -From e9c0e5a0de4a65238b03ba6729a3d3611e0e0d8a Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 18:02:44 +0000 -Subject: [PATCH 092/172] mach_bcm2709: Add Mailbox resources to USB driver - ---- - arch/arm/mach-bcm2709/bcm2709.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 3055e72d..3cb3756 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -338,6 +338,16 @@ static struct resource bcm2708_usb_resources[] = { - .end = IRQ_USB, - .flags = IORESOURCE_IRQ, - }, -+ [4] = { -+ .start = ARM_LOCAL_BASE, -+ .end = ARM_LOCAL_BASE + SZ_4K - 1, -+ .flags = IORESOURCE_MEM, -+ }, -+ [5] = { -+ .start = IRQ_ARM_LOCAL_MAILBOX1, -+ .end = IRQ_ARM_LOCAL_MAILBOX1, -+ .flags = IORESOURCE_IRQ -+ }, - }; - - - -From ec73fdffb06f1c2751229834465ffd3152bdfd3f Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 22 Jan 2015 18:45:23 +0000 -Subject: [PATCH 093/172] bcm2709/dwc_otg: Setup FIQ on core 1 if >1 core - active - ---- - arch/arm/mach-bcm2709/armctrl.c | 10 +++++++++- - drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 15 +++++++++++---- - 2 files changed, 20 insertions(+), 5 deletions(-) - -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -index 8e69a813..6e3f0f2ff 100644 ---- a/arch/arm/mach-bcm2709/armctrl.c -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -89,7 +89,15 @@ static void armctrl_unmask_irq(struct irq_data *d) - }; - int i; - if (d->irq >= FIQ_START) { -- unsigned int data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; -+ unsigned int data; -+ if (num_online_cpus() > 1) { -+ data = readl(__io_address(ARM_LOCAL_GPU_INT_ROUTING)); -+ data &= ~0xc; -+ data |= (1 << 2); -+ writel(data, __io_address(ARM_LOCAL_GPU_INT_ROUTING)); -+ } -+ /* Unmask in ARMCTRL block after routing it properly */ -+ data = (unsigned int)irq_get_chip_data(d->irq) - FIQ_START; - writel(0x80 | data, __io_address(ARM_IRQ_FAST)); - } else if (d->irq >= IRQ_ARM_LOCAL_CNTPSIRQ && d->irq < IRQ_ARM_LOCAL_CNTPSIRQ + 4) { - #if 1 -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -index 4d8dd95..1d28459 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c -@@ -407,7 +407,9 @@ static void hcd_init_fiq(void *cookie) - DWC_ERROR("Can't claim FIQ"); - BUG(); - } -- DWC_WARN("FIQ at 0x%08x", (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); -+ DWC_WARN("FIQ on core %d at 0x%08x", -+ smp_processor_id(), -+ (fiq_fsm_enable ? (int)&dwc_otg_fiq_fsm : (int)&dwc_otg_fiq_nop)); - DWC_WARN("FIQ ASM at 0x%08x length %d", (int)&_dwc_otg_fiq_stub, (int)(&_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub)); - set_fiq_handler((void *) &_dwc_otg_fiq_stub, &_dwc_otg_fiq_stub_end - &_dwc_otg_fiq_stub); - memset(®s,0,sizeof(regs)); -@@ -510,9 +512,14 @@ int hcd_init(dwc_bus_dev_t *_dev) - goto error2; - } - -- if (fiq_enable) -- smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); -- -+ if (fiq_enable) { -+ if (num_online_cpus() > 1) { -+ /* bcm2709: can run the FIQ on a separate core to IRQs */ -+ smp_call_function_single(1, hcd_init_fiq, otg_dev, 1); -+ } else { -+ smp_call_function_single(0, hcd_init_fiq, otg_dev, 1); -+ } -+ } - - otg_dev->hcd->otg_dev = otg_dev; - hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); - -From f1c713f051a95787416095a6dbfe5846a15c9f8e Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 26 Jan 2015 17:40:22 +0000 -Subject: [PATCH 094/172] bcm2709: Port pps-gpio and i2c patches - ---- - arch/arm/mach-bcm2709/bcm2709.c | 41 +++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 39 insertions(+), 2 deletions(-) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 3cb3756..8936427 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -37,6 +37,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -95,6 +96,8 @@ static unsigned disk_led_active_low = 1; - static unsigned reboot_part = 0; - static unsigned w1_gpio_pin = W1_GPIO; - static unsigned w1_gpio_pullup = W1_PULLUP; -+static int pps_gpio_pin = -1; -+static bool vc_i2c_override = false; - - static unsigned use_dt = 0; - -@@ -283,6 +286,19 @@ static struct platform_device w1_device = { - }; - #endif - -+static struct pps_gpio_platform_data pps_gpio_info = { -+ .assert_falling_edge = false, -+ .capture_clear = false, -+ .gpio_pin = -1, -+ .gpio_label = "PPS", -+}; -+ -+static struct platform_device pps_gpio_device = { -+ .name = "pps-gpio", -+ .id = PLATFORM_DEVID_NONE, -+ .dev.platform_data = &pps_gpio_info, -+}; -+ - static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); - - static struct platform_device bcm2708_fb_device = { -@@ -862,6 +878,16 @@ void __init bcm2709_init(void) - #ifdef CONFIG_BCM2708_GPIO - bcm_register_device_dt(&bcm2708_gpio_device); - #endif -+ -+#if defined(CONFIG_PPS_CLIENT_GPIO) || defined(CONFIG_PPS_CLIENT_GPIO_MODULE) -+ if (!use_dt && (pps_gpio_pin >= 0)) { -+ pr_info("bcm2709: GPIO %d setup as pps-gpio device\n", pps_gpio_pin); -+ pps_gpio_info.gpio_pin = pps_gpio_pin; -+ pps_gpio_device.id = pps_gpio_pin; -+ bcm_register_device(&pps_gpio_device); -+ } -+#endif -+ - #if defined(CONFIG_W1_MASTER_GPIO) || defined(CONFIG_W1_MASTER_GPIO_MODULE) - w1_gpio_pdata.pin = w1_gpio_pin; - w1_gpio_pdata.ext_pullup_enable_pin = w1_gpio_pullup; -@@ -886,8 +912,15 @@ void __init bcm2709_init(void) - bcm_register_device(&bcm2835_thermal_device); - - bcm_register_device_dt(&bcm2708_spi_device); -- bcm_register_device_dt(&bcm2708_bsc0_device); -- bcm_register_device_dt(&bcm2708_bsc1_device); -+ -+ if (vc_i2c_override) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } else if ((boardrev & 0xffffff) == 0x2 || (boardrev & 0xffffff) == 0x3) { -+ bcm_register_device_dt(&bcm2708_bsc0_device); -+ } else { -+ bcm_register_device_dt(&bcm2708_bsc1_device); -+ } - - #if defined(CONFIG_SND_BCM2708_SOC_I2S) || defined(CONFIG_SND_BCM2708_SOC_I2S_MODULE) - bcm_register_device_dt(&bcm2708_i2s_device); -@@ -1242,3 +1275,7 @@ module_param(disk_led_active_low, uint, 0644); - module_param(reboot_part, uint, 0644); - module_param(w1_gpio_pin, uint, 0644); - module_param(w1_gpio_pullup, uint, 0644); -+module_param(pps_gpio_pin, int, 0644); -+MODULE_PARM_DESC(pps_gpio_pin, "Set GPIO pin to reserve for PPS"); -+module_param(vc_i2c_override, bool, 0644); -+MODULE_PARM_DESC(vc_i2c_override, "Allow the use of VC's I2C peripheral."); - -From 30a40bd6d577e4b4b4b13c3bb249a99aa8216def Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 28 Jan 2015 17:57:23 +0000 -Subject: [PATCH 095/172] bcm2709: Also accept the 2708 machine ID - ---- - arch/arm/mach-bcm2709/bcm2709.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c -index 8936427..55c21a5 100644 ---- a/arch/arm/mach-bcm2709/bcm2709.c -+++ b/arch/arm/mach-bcm2709/bcm2709.c -@@ -1267,6 +1267,19 @@ MACHINE_START(BCM2709, "BCM2709") - .dt_compat = bcm2709_compat, - MACHINE_END - -+MACHINE_START(BCM2708, "BCM2709") -+ /* Maintainer: Broadcom Europe Ltd. */ -+ .smp = smp_ops(bcm2709_smp_ops), -+ .map_io = bcm2709_map_io, -+ .init_irq = bcm2709_init_irq, -+ .init_time = bcm2709_timer_init, -+ .init_machine = bcm2709_init, -+ .init_early = bcm2709_init_early, -+ .reserve = board_reserve, -+ .restart = bcm2709_restart, -+ .dt_compat = bcm2709_compat, -+MACHINE_END -+ - module_param(boardrev, uint, 0644); - module_param(serial, uint, 0644); - module_param(uart_clock, uint, 0644); - -From 24097ac1917183b1b10b2315a1a3db527fcf8dfb Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 2 Feb 2015 12:45:07 +0000 -Subject: [PATCH 096/172] BCM2709_DT: Fix bad merge - ---- - arch/arm/boot/dts/Makefile | 25 +++++++++++++------------ - 1 file changed, 13 insertions(+), 12 deletions(-) - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 83dceff..f3b6364 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -63,18 +63,19 @@ ifeq ($(CONFIG_BCM2709_DT),y) - endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-dac-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-dacplus-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-digi-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += hifiberry-amp-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += iqaudio-dac-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += iqaudio-dacplus-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += lirc-rpi-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += pcf8523-rtc-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += pps-gpio-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += ds1307-rtc-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += w1-gpio-overlay.dtb --dtb-$(CONFIG_BCM2708_DT) += w1-gpio-pullup-overlay.dtb -+dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb -+dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb -+dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ - -From f3e38d36525f6f1f59dacddd9595ad860ae2eacd Mon Sep 17 00:00:00 2001 +From f28cbc4c87034ca6cec586aab72550ed2ec873b4 Mon Sep 17 00:00:00 2001 From: Daniel Matuschek Date: Tue, 3 Feb 2015 07:15:19 +0100 -Subject: [PATCH 097/172] HiFiBerry Amp: fix device-tree problems +Subject: [PATCH 073/104] HiFiBerry Amp: fix device-tree problems Some code to load the driver based on device-tree-overlays was missing. This is added by this patch. --- @@ -137832,7 +125796,7 @@ Some code to load the driver based on device-tree-overlays was missing. This is 1 file changed, 21 insertions(+) diff --git a/sound/soc/bcm/hifiberry_amp.c b/sound/soc/bcm/hifiberry_amp.c -index 1e87ee0..5903915 100644 +index 1e87ee06..5903915 100644 --- a/sound/soc/bcm/hifiberry_amp.c +++ b/sound/soc/bcm/hifiberry_amp.c @@ -65,6 +65,12 @@ static struct snd_soc_card snd_rpi_hifiberry_amp = { @@ -137878,10 +125842,10 @@ index 1e87ee0..5903915 100644 .probe = snd_rpi_hifiberry_amp_probe, .remove = snd_rpi_hifiberry_amp_remove, -From 93addb736b05475b6ed41035b4dab1eee9bf35e4 Mon Sep 17 00:00:00 2001 +From a1cdf290a0f57e8bd06f9dcd231e0e75900eb91f Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 3 Feb 2015 11:41:38 +0000 -Subject: [PATCH 098/172] BCM270x_DT: Add i2c0_baudrate and i2c1_baudrate +Subject: [PATCH 074/104] BCM270x_DT: Add i2c0_baudrate and i2c1_baudrate parameters --- @@ -137930,10 +125894,10 @@ index 46f4908..75c222f 100644 act_led_gpio = <&act_led>,"gpios:4"; act_led_activelow = <&act_led>,"gpios:8"; -From 83bef71ee725251abe65c91dd6ae76547acbc5ca Mon Sep 17 00:00:00 2001 +From 8d1370ff81ebc22ee80a61d66991ee2e4a5fdc48 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 4 Feb 2015 10:02:24 +0000 -Subject: [PATCH 099/172] pinctrl-bcm2835: bcm2835_gpio_direction_output must +Subject: [PATCH 075/104] pinctrl-bcm2835: bcm2835_gpio_direction_output must set the value --- @@ -137961,10 +125925,10 @@ index 9d1149e..d9c727b 100644 static void bcm2835_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -From 3e2353455d485d72a0bfbfefdfd7ce3dea783b93 Mon Sep 17 00:00:00 2001 +From 680333e666e7b63d41168b0b1a8ff22a4cf6095e Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 4 Feb 2015 12:59:36 +0000 -Subject: [PATCH 100/172] w1-gpio: Sort out the pullup/parasitic power tangle +Subject: [PATCH 076/104] w1-gpio: Sort out the pullup/parasitic power tangle --- arch/arm/boot/dts/w1-gpio-overlay.dts | 4 +++- @@ -138137,143 +126101,10 @@ index d58594a..feae942 100644 unsigned int ext_pullup_enable_pin; unsigned int pullup_duration; -From ca08a2747acc5e2033abdbae1696d00769754fda Mon Sep 17 00:00:00 2001 -From: P33M -Date: Wed, 4 Feb 2015 12:16:50 +0000 -Subject: [PATCH 101/172] dwc_otg: fixup read-modify-write in critical paths - -Be more careful about read-modify-write on registers that the FIQ -also touches. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 13 +++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 30 +++++++++++++++++++++++++--- - drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 22 ++++++++++++++++---- - 3 files changed, 55 insertions(+), 10 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -index ac70f1d..1782d65 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c -@@ -2447,9 +2447,16 @@ void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, - */ - gintmsk_data_t gintmsk = {.d32 = 0 }; - gintmsk.b.nptxfempty = 1; -- DWC_MODIFY_REG32(&hcd->core_if-> -- core_global_regs->gintmsk, gintmsk.d32, -- 0); -+ -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); -+ } - } - } - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index ee35196..6266661 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -165,7 +165,15 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) - - gintmsk_data_t gintmsk = { .b.portintr = 1}; - retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); -- DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32); -+ } - } - if (gintsts.b.hcintr) { - retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); -@@ -1069,7 +1077,15 @@ static void halt_channel(dwc_otg_hcd_t * hcd, - * be processed. - */ - gintmsk.b.nptxfempty = 1; -- DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ } - } else { - /* - * Move the QH from the periodic queued schedule to -@@ -1086,7 +1102,15 @@ static void halt_channel(dwc_otg_hcd_t * hcd, - * processed. - */ - gintmsk.b.ptxfempty = 1; -- DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&global_regs->gintmsk, 0, gintmsk.d32); -+ } - } - } - } -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -index 17d3030..acd0dd7 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c -@@ -683,8 +683,15 @@ int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - status = schedule_periodic(hcd, qh); - if ( !hcd->periodic_qh_count ) { - intr_mask.b.sofintr = 1; -- DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -- intr_mask.d32, intr_mask.d32); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, intr_mask.d32); -+ } - } - hcd->periodic_qh_count++; - } -@@ -745,8 +752,15 @@ void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) - hcd->periodic_qh_count--; - if( !hcd->periodic_qh_count && !fiq_fsm_enable ) { - intr_mask.b.sofintr = 1; -- DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, -- intr_mask.d32, 0); -+ if (fiq_enable) { -+ local_fiq_disable(); -+ fiq_fsm_spin_lock(&hcd->fiq_state->lock); -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); -+ fiq_fsm_spin_unlock(&hcd->fiq_state->lock); -+ local_fiq_enable(); -+ } else { -+ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, intr_mask.d32, 0); -+ } - } - } - } - -From 14a8bf59835972411f4b73c37aae457ab46b6ee1 Mon Sep 17 00:00:00 2001 +From de7749cbb3ef82fec9860fcf156ebc3c86cd5e55 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 5 Feb 2015 16:01:44 +0000 -Subject: [PATCH 102/172] i2c_bcm2708: Fix clock reference counting +Subject: [PATCH 077/104] i2c_bcm2708: Fix clock reference counting --- drivers/i2c/busses/i2c-bcm2708.c | 12 ++++++++++-- @@ -138321,10 +126152,10 @@ index 526129b..fda59ba 100644 kfree(bi); -From 2ec0ca5c31b9dc7498362727d40361b5d542552a Mon Sep 17 00:00:00 2001 +From b2603b5c8719225f6d2cfef99f150158336c472a Mon Sep 17 00:00:00 2001 From: Byron Bradley Date: Fri, 6 Feb 2015 14:19:41 +0000 -Subject: [PATCH 103/172] Add device-tree overlay for pcf2127 +Subject: [PATCH 078/104] Add device-tree overlay for pcf2127 Signed-off-by: Byron Bradley --- @@ -138334,10 +126165,10 @@ Signed-off-by: Byron Bradley create mode 100644 arch/arm/boot/dts/pcf2127-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index f3b6364..2031ffe 100644 +index d45424c..ca55980 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -72,6 +72,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb +@@ -20,6 +20,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb @@ -138374,10 +126205,10 @@ index 0000000..01fc81d + }; +}; -From dc183cc424cc15195d4f5f6d559ba14da39e4c9f Mon Sep 17 00:00:00 2001 +From 9b0008cdb418c0c75914d6306995f06a6a72564f Mon Sep 17 00:00:00 2001 From: android Date: Mon, 25 Aug 2014 13:18:21 +0100 -Subject: [PATCH 104/172] BCM2708_VCIO : Add automatic creation of device node +Subject: [PATCH 079/104] BCM2708_VCIO : Add automatic creation of device node --- arch/arm/mach-bcm2708/vcio.c | 12 +++++++++++- @@ -138459,10 +126290,10 @@ index 5e43e85..700bff4 100644 } -From 7ae975fb0e9e826f87a895915a958b9a65c0f073 Mon Sep 17 00:00:00 2001 +From 0b6043e147ac773bcb495704a6f4e393981aca38 Mon Sep 17 00:00:00 2001 From: jeanleflambeur Date: Sun, 1 Feb 2015 12:35:38 +0100 -Subject: [PATCH 105/172] Fix grabbing lock from atomic context in i2c driver +Subject: [PATCH 080/104] Fix grabbing lock from atomic context in i2c driver 2 main changes: - check for timeouts in the bcm2708_bsc_setup function as indicated by this comment: @@ -138681,10 +126512,10 @@ index fda59ba..81e9374 100644 dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", pdev->id, (unsigned long)regs->start, irq, baudrate); -From 777022879290ae0dcd44f327a73088369957f2b8 Mon Sep 17 00:00:00 2001 +From 278b258303b0b5772e3b2847d304644072bae177 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 12 Feb 2015 11:17:53 +0000 -Subject: [PATCH 106/172] Fix LED "input" trigger implementation for 3.19 +Subject: [PATCH 081/104] Fix LED "input" trigger implementation for 3.19 --- drivers/leds/leds-gpio.c | 10 +++++++++- @@ -138693,7 +126524,7 @@ Subject: [PATCH 106/172] Fix LED "input" trigger implementation for 3.19 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c -index 7ea1ea42..cf70ea1 100644 +index d26af0a..9e49c72 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -41,6 +41,13 @@ static void gpio_led_work(struct work_struct *work) @@ -138757,410 +126588,24 @@ index 2ca2b98..07d1219 100644 static struct led_trigger input_led_trigger = { diff --git a/include/linux/leds.h b/include/linux/leds.h -index cfceef3..4c8dc19 100644 +index f70f84f..f3fcaa1 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h -@@ -46,6 +46,9 @@ struct led_classdev { - #define LED_SYSFS_DISABLE (1 << 20) - #define SET_BRIGHTNESS_ASYNC (1 << 21) +@@ -48,6 +48,9 @@ struct led_classdev { #define SET_BRIGHTNESS_SYNC (1 << 22) + #define LED_DEV_CAP_FLASH (1 << 23) + #define LED_DEV_CAP_SYNC_STROBE (1 << 24) + /* Additions for Raspberry Pi PWR LED */ -+#define SET_GPIO_INPUT (1 << 23) -+#define SET_GPIO_OUTPUT (1 << 24) ++#define SET_GPIO_INPUT (1 << 30) ++#define SET_GPIO_OUTPUT (1 << 31) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ -From 8d8b2a22f6227d1c55b05b7e4247c57fcbb3bd88 Mon Sep 17 00:00:00 2001 -From: P33M -Date: Thu, 12 Feb 2015 11:20:00 +0000 -Subject: [PATCH 107/172] fiq_fsm: Falling out of the state machine isn't fatal - -This edge case can be hit if the port is disabled while the FIQ is -in the middle of a transaction. Make the effects less severe. - -Also get rid of the useless return value. ---- - drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 20 +++++++------------- - 1 file changed, 7 insertions(+), 13 deletions(-) - -diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -index 6266661..8a31562 100644 ---- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -+++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c -@@ -2357,7 +2357,7 @@ int dwc_otg_fiq_unsetup_per_dma(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, dwc_otg_qt - * different from the normal (messy) path. This function and its friends handles - * channel cleanup and transaction completion from a FIQ transaction. - */ --int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) -+void dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - { - struct fiq_channel_state *st = &hcd->fiq_state->channel[num]; - dwc_hc_t *hc = hcd->hc_ptr_array[num]; -@@ -2366,7 +2366,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[num]; - hcint_data_t hcint = hcd->fiq_state->channel[num].hcint_copy; - int hostchannels = 0; -- int ret = 0; - fiq_print(FIQDBG_INT, hcd->fiq_state, "OUT %01d %01d ", num , st->fsm); - - hostchannels = hcd->available_host_channels; -@@ -2380,7 +2379,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - * CLEAR_TT_BUFFER hub command if we were in the start-split phase. - */ - release_channel(hcd, hc, NULL, hc->halt_status); -- ret = 1; - break; - - case FIQ_NP_SPLIT_DONE: -@@ -2393,7 +2391,6 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - } else if (hcint.b.nak) { - handle_hc_nak_intr(hcd, hc, hc_regs, qtd); - } -- ret = 1; - break; - - case FIQ_NP_SPLIT_HS_ABORTED: -@@ -2604,15 +2601,12 @@ int32_t dwc_otg_hcd_handle_hc_fsm(dwc_otg_hcd_t *hcd, uint32_t num) - break; - - default: -- local_fiq_disable(); -- DWC_WARN("unexpected state received on hc=%d fsm=%d", hc->hc_num, st->fsm); -- BUG(); -- } -- //if (hostchannels != hcd->available_host_channels) { -- /* should have incremented by now! */ -- // BUG(); --// } -- return ret; -+ DWC_WARN("Unexpected state received on hc=%d fsm=%d on transfer to device %d ep 0x%x", -+ hc->hc_num, st->fsm, hc->dev_addr, hc->ep_num); -+ qtd->error_count++; -+ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); -+ } -+ return; - } - - /** Handles interrupt for a specific Host Channel */ - -From 2455e8024a76f5de920dfa3d7e3177830fcacb7a Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 15 Feb 2015 12:06:16 +0000 -Subject: [PATCH 108/172] platform: Remove bcmrpi_small_defconfig - ---- - arch/arm/configs/bcmrpi_small_defconfig | 103 -------------------------------- - 1 file changed, 103 deletions(-) - delete mode 100644 arch/arm/configs/bcmrpi_small_defconfig - -diff --git a/arch/arm/configs/bcmrpi_small_defconfig b/arch/arm/configs/bcmrpi_small_defconfig -deleted file mode 100644 -index 3f6e378..0000000 ---- a/arch/arm/configs/bcmrpi_small_defconfig -+++ /dev/null -@@ -1,103 +0,0 @@ --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2836=y --# CONFIG_BCM2708_GPIO is not set --# CONFIG_BCM2708_VCMEM is not set --# CONFIG_CACHE_L2X0 is not set --CONFIG_SMP=y --CONFIG_HAVE_ARM_ARCH_TIMER=y --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_IDLE=y --# CONFIG_SUSPEND is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --# CONFIG_BCM2708_VCHIQ is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_RAW_DRIVER=y --# CONFIG_HID is not set --CONFIG_DMADEVICES=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NLS=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_KERNEL=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_DEBUG_LL=y --CONFIG_EARLY_PRINTK=y --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC16=y --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y - -From 82c208c7034e6a112808b16ee6fd1b910a50494c Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 15 Feb 2015 12:09:47 +0000 -Subject: [PATCH 109/172] platform: Remove bcmrpi_sdcard_defconfig - ---- - arch/arm/configs/bcmrpi_sdcard_defconfig | 176 ------------------------------- - 1 file changed, 176 deletions(-) - delete mode 100644 arch/arm/configs/bcmrpi_sdcard_defconfig - -diff --git a/arch/arm/configs/bcmrpi_sdcard_defconfig b/arch/arm/configs/bcmrpi_sdcard_defconfig -deleted file mode 100644 -index fef887a..0000000 ---- a/arch/arm/configs/bcmrpi_sdcard_defconfig -+++ /dev/null -@@ -1,176 +0,0 @@ --# CONFIG_ARM_PATCH_PHYS_VIRT is not set --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_POSIX_MQUEUE=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_EMBEDDED=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2708=y --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_UACCESS_WITH_MEMCPY=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_FREQ=y --CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y --CONFIG_CPU_FREQ_GOV_PERFORMANCE=y --CONFIG_CPU_FREQ_GOV_USERSPACE=y --CONFIG_CPU_FREQ_GOV_ONDEMAND=y --CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y --CONFIG_VFP=y --CONFIG_BINFMT_MISC=y --CONFIG_NET=y --CONFIG_PACKET=y --CONFIG_UNIX=y --CONFIG_INET=y --CONFIG_IP_MULTICAST=y --CONFIG_IP_PNP=y --CONFIG_IP_PNP_DHCP=y --CONFIG_IP_PNP_RARP=y --CONFIG_SYN_COOKIES=y --# CONFIG_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set --# CONFIG_WIRELESS is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --CONFIG_SCSI=y --# CONFIG_SCSI_PROC_FS is not set --# CONFIG_SCSI_LOWLEVEL is not set --CONFIG_NETDEVICES=y --CONFIG_MII=y --# CONFIG_NET_VENDOR_BROADCOM is not set --# CONFIG_NET_VENDOR_CIRRUS is not set --# CONFIG_NET_VENDOR_FARADAY is not set --# CONFIG_NET_VENDOR_INTEL is not set --# CONFIG_NET_VENDOR_MARVELL is not set --# CONFIG_NET_VENDOR_MICREL is not set --# CONFIG_NET_VENDOR_NATSEMI is not set --# CONFIG_NET_VENDOR_SEEQ is not set --# CONFIG_NET_VENDOR_STMICRO is not set --# CONFIG_NET_VENDOR_WIZNET is not set --CONFIG_PHYLIB=y --# CONFIG_WLAN is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --CONFIG_VT_HW_CONSOLE_BINDING=y --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_TTY_PRINTK=y --CONFIG_RAW_DRIVER=y --CONFIG_WATCHDOG=y --CONFIG_REGULATOR=y --CONFIG_REGULATOR_DEBUG=y --CONFIG_REGULATOR_FIXED_VOLTAGE=y --CONFIG_REGULATOR_VIRTUAL_CONSUMER=y --CONFIG_REGULATOR_USERSPACE_CONSUMER=y --CONFIG_FB=y --CONFIG_FRAMEBUFFER_CONSOLE=y --CONFIG_LOGO=y --# CONFIG_LOGO_LINUX_MONO is not set --# CONFIG_LOGO_LINUX_VGA16 is not set --CONFIG_SOUND=y --CONFIG_SND=y --CONFIG_MMC=y --CONFIG_MMC_SDHCI=y --CONFIG_MMC_SDHCI_PLTFM=y --CONFIG_MMC_SDHCI_BCM2708=y --CONFIG_MMC_SDHCI_BCM2708_DMA=y --CONFIG_NEW_LEDS=y --CONFIG_LEDS_CLASS=y --CONFIG_LEDS_TRIGGERS=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_EXT4_FS=y --CONFIG_EXT4_FS_POSIX_ACL=y --CONFIG_EXT4_FS_SECURITY=y --CONFIG_AUTOFS4_FS=y --CONFIG_FSCACHE=y --CONFIG_CACHEFILES=y --CONFIG_MSDOS_FS=y --CONFIG_VFAT_FS=y --CONFIG_FAT_DEFAULT_IOCHARSET="ascii" --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --CONFIG_CONFIGFS_FS=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NFS_FS=y --CONFIG_NFS_V3_ACL=y --CONFIG_NFS_V4=y --CONFIG_ROOT_NFS=y --CONFIG_NFS_FSCACHE=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_FS=y --CONFIG_DETECT_HUNG_TASK=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_DEBUG_BUGVERBOSE is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y - -From cf9e2491332c7b58f25d489baa2040ab529e7223 Mon Sep 17 00:00:00 2001 +From 7612654a1bf2d6ed7005e69b07c9dd43f68449b6 Mon Sep 17 00:00:00 2001 From: Rainer Herbers Date: Sun, 15 Feb 2015 13:44:14 +0100 -Subject: [PATCH 110/172] Create bmp085_i2c-sensor-overlay.dts and update +Subject: [PATCH 082/104] Create bmp085_i2c-sensor-overlay.dts and update Makefile --- @@ -139170,13 +126615,13 @@ Subject: [PATCH 110/172] Create bmp085_i2c-sensor-overlay.dts and update create mode 100644 arch/arm/boot/dts/bmp085_i2c-sensor-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 2031ffe..754291e 100644 +index ca55980..6839ece 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -64,6 +64,7 @@ endif - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b.dtb - dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -12,6 +12,7 @@ ifeq ($(CONFIG_BCM2709_DT),y) + RPI_DT_OVERLAYS=y + endif + +dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb @@ -139211,838 +126656,10 @@ index 0000000..b830bf2 + }; +}; -From 62d72625e0c8f80a16aca64437885bea2d8e5bee Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Mon, 16 Feb 2015 13:51:02 +0000 -Subject: [PATCH 111/172] lirc-rpi: use getnstimeofday instead of - read_current_timer - -read_current_timer isn't guaranteed to return values in -microseconds, and indeed it doesn't on a Pi2. - -Issue: linux#827 ---- - drivers/staging/media/lirc/lirc_rpi.c | 12 ++++++++++-- - 1 file changed, 10 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -index cd66ca2..24563ec 100644 ---- a/drivers/staging/media/lirc/lirc_rpi.c -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -107,6 +108,13 @@ static void safe_udelay(unsigned long usecs) - udelay(usecs); - } - -+static unsigned long read_current_us(void) -+{ -+ struct timespec now; -+ getnstimeofday(&now); -+ return (now.tv_sec * 1000000) + (now.tv_nsec/1000); -+} -+ - static int init_timing_params(unsigned int new_duty_cycle, - unsigned int new_freq) - { -@@ -135,7 +143,7 @@ static long send_pulse_softcarrier(unsigned long length) - length *= 1000; - - actual = 0; target = 0; flag = 0; -- read_current_timer(&actual_us); -+ actual_us = read_current_us(); - - while (actual < length) { - if (flag) { -@@ -153,7 +161,7 @@ static long send_pulse_softcarrier(unsigned long length) - */ - if ((int)(target_us - actual_us) > 0) - udelay(target_us - actual_us); -- read_current_timer(&actual_us); -+ actual_us = read_current_us(); - actual += (actual_us - initial_us) * 1000; - flag = !flag; - } - -From d38f6c099572f92660d4a7125b97159b89ac5970 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 18 Feb 2015 10:40:28 +0000 -Subject: [PATCH 112/172] vc_cma: Make the vc_cma area the default contiguous - DMA area - ---- - drivers/char/broadcom/vc_cma/vc_cma.c | 98 ++++++++++++++++++++++++++--------- - 1 file changed, 73 insertions(+), 25 deletions(-) - -diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c -index a635f9f..bd64317 100644 ---- a/drivers/char/broadcom/vc_cma/vc_cma.c -+++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -58,6 +58,8 @@ - #define LOG_DBG(fmt, ...) \ - if (vc_cma_debug) \ - printk(KERN_INFO fmt "\n", ##__VA_ARGS__) -+#define LOG_INFO(fmt, ...) \ -+ printk(KERN_INFO fmt "\n", ##__VA_ARGS__) - #define LOG_ERR(fmt, ...) \ - printk(KERN_ERR fmt "\n", ##__VA_ARGS__) - -@@ -120,6 +122,10 @@ unsigned int vc_cma_chunks; - unsigned int vc_cma_chunks_used; - unsigned int vc_cma_chunks_reserved; - -+ -+void *vc_cma_dma_alloc; -+unsigned int vc_cma_dma_size; -+ - static int in_loud_error; - - unsigned int vc_cma_reserve_total; -@@ -198,8 +204,17 @@ void vc_cma_reserve(void) - * size from the end of memory - */ - if (vc_cma_size) { -- if (dma_declare_contiguous(NULL /*&vc_cma_device.dev*/, vc_cma_size, -+ if (dma_declare_contiguous(&vc_cma_device.dev, vc_cma_size, - vc_cma_base, 0) == 0) { -+ if (!dev_get_cma_area(NULL)) { -+ /* There is no default CMA area - make this -+ the default */ -+ struct cma *vc_cma_area = dev_get_cma_area( -+ &vc_cma_device.dev); -+ dma_contiguous_set_default(vc_cma_area); -+ LOG_INFO("vc_cma_reserve - using vc_cma as " -+ "the default contiguous DMA area"); -+ } - } else { - LOG_ERR("vc_cma: dma_declare_contiguous(%x,%x) failed", - vc_cma_size, (unsigned int)vc_cma_base); -@@ -309,6 +324,9 @@ static int vc_cma_show_info(struct seq_file *m, void *v) - seq_printf(m, " PID %5d: %d bytes\n", user->pid, - user->reserve); - } -+ seq_printf(m, " dma_alloc : %p (%d pages)\n", -+ vc_cma_dma_alloc ? page_address(vc_cma_dma_alloc) : 0, -+ vc_cma_dma_size); - - seq_printf(m, "\n"); - -@@ -348,31 +366,33 @@ static int vc_cma_proc_write(struct file *file, - #define FREE_STR "free" - #define DEBUG_STR "debug" - #define RESERVE_STR "reserve" -+#define DMA_ALLOC_STR "dma_alloc" -+#define DMA_FREE_STR "dma_free" - if (strncmp(input_str, ALLOC_STR, strlen(ALLOC_STR)) == 0) { -- int size; -+ int alloc_size; - char *p = input_str + strlen(ALLOC_STR); - - while (*p == ' ') - p++; -- size = memparse(p, NULL); -- LOG_ERR("/proc/vc-cma: alloc %d", size); -- if (size) -+ alloc_size = memparse(p, NULL); -+ LOG_INFO("/proc/vc-cma: alloc %d", alloc_size); -+ if (alloc_size) - send_vc_msg(VC_CMA_MSG_REQUEST_FREE, -- size / VC_CMA_CHUNK_SIZE, 0); -+ alloc_size / VC_CMA_CHUNK_SIZE, 0); - else - LOG_ERR("invalid size '%s'", p); - rc = size; - } else if (strncmp(input_str, FREE_STR, strlen(FREE_STR)) == 0) { -- int size; -+ int alloc_size; - char *p = input_str + strlen(FREE_STR); - - while (*p == ' ') - p++; -- size = memparse(p, NULL); -- LOG_ERR("/proc/vc-cma: free %d", size); -- if (size) -+ alloc_size = memparse(p, NULL); -+ LOG_INFO("/proc/vc-cma: free %d", alloc_size); -+ if (alloc_size) - send_vc_msg(VC_CMA_MSG_REQUEST_ALLOC, -- size / VC_CMA_CHUNK_SIZE, 0); -+ alloc_size / VC_CMA_CHUNK_SIZE, 0); - else - LOG_ERR("invalid size '%s'", p); - rc = size; -@@ -384,18 +404,46 @@ static int vc_cma_proc_write(struct file *file, - vc_cma_debug = 1; - else if ((strcmp(p, "off") == 0) || (strcmp(p, "0") == 0)) - vc_cma_debug = 0; -- LOG_ERR("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); -+ LOG_INFO("/proc/vc-cma: debug %s", vc_cma_debug ? "on" : "off"); - rc = size; - } else if (strncmp(input_str, RESERVE_STR, strlen(RESERVE_STR)) == 0) { -- int size; -+ int alloc_size; - int reserved; - char *p = input_str + strlen(RESERVE_STR); - while (*p == ' ') - p++; -- size = memparse(p, NULL); -+ alloc_size = memparse(p, NULL); - -- reserved = vc_cma_set_reserve(size, current->tgid); -+ reserved = vc_cma_set_reserve(alloc_size, current->tgid); - rc = (reserved >= 0) ? size : reserved; -+ } else if (strncmp(input_str, DMA_ALLOC_STR, strlen(DMA_ALLOC_STR)) == 0) { -+ int alloc_size; -+ char *p = input_str + strlen(DMA_ALLOC_STR); -+ while (*p == ' ') -+ p++; -+ alloc_size = memparse(p, NULL); -+ -+ if (vc_cma_dma_alloc) { -+ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, -+ vc_cma_dma_size); -+ vc_cma_dma_alloc = NULL; -+ vc_cma_dma_size = 0; -+ } -+ vc_cma_dma_alloc = dma_alloc_from_contiguous(NULL, alloc_size, 0); -+ vc_cma_dma_size = (vc_cma_dma_alloc ? alloc_size : 0); -+ if (vc_cma_dma_alloc) -+ LOG_INFO("dma_alloc(%d pages) -> %p", alloc_size, page_address(vc_cma_dma_alloc)); -+ else -+ LOG_ERR("dma_alloc(%d pages) failed", alloc_size); -+ rc = size; -+ } else if (strncmp(input_str, DMA_FREE_STR, strlen(DMA_FREE_STR)) == 0) { -+ if (vc_cma_dma_alloc) { -+ dma_release_from_contiguous(NULL, vc_cma_dma_alloc, -+ vc_cma_dma_size); -+ vc_cma_dma_alloc = NULL; -+ vc_cma_dma_size = 0; -+ } -+ rc = size; - } - - out: -@@ -526,7 +574,7 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - uint8_t *chunk_addr; - size_t chunk_size = PAGES_PER_CHUNK << PAGE_SHIFT; - -- chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, - PAGES_PER_CHUNK, - VC_CMA_CHUNK_ORDER); - if (!chunk) -@@ -548,7 +596,7 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - "bad SPARSEMEM configuration?", - __func__, (unsigned int)page_to_phys(chunk), - vc_cma_base, vc_cma_base + vc_cma_size - 1); -- LOG_ERR("%s: dev->cma_area = %p\n", __func__, -+ LOG_ERR("%s: dev->cma_area = %p", __func__, - (void*)0/*vc_cma_device.dev.cma_area*/); - LOG_ERR("%s: ===============================", - __func__); -@@ -672,7 +720,7 @@ static int cma_worker_proc(void *param) - } - - if (!dma_release_from_contiguous -- (NULL /*&vc_cma_device.dev*/, page, -+ (&vc_cma_device.dev, page, - PAGES_PER_CHUNK)) { - LOG_ERR - ("CMA_MSG_FREE - failed to " -@@ -1023,12 +1071,12 @@ static int vc_cma_init(void) - if (!check_cma_config()) - goto out_release; - -- printk(KERN_INFO "vc-cma: Videocore CMA driver\n"); -- printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); -- printk(KERN_INFO "vc-cma: vc_cma_size = 0x%08x (%u MiB)\n", -- vc_cma_size, vc_cma_size / (1024 * 1024)); -- printk(KERN_INFO "vc-cma: vc_cma_initial = 0x%08x (%u MiB)\n", -- vc_cma_initial, vc_cma_initial / (1024 * 1024)); -+ LOG_INFO("vc-cma: Videocore CMA driver"); -+ LOG_INFO("vc-cma: vc_cma_base = 0x%08x", vc_cma_base); -+ LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", -+ vc_cma_size, vc_cma_size / (1024 * 1024)); -+ LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", -+ vc_cma_initial, vc_cma_initial / (1024 * 1024)); - - vc_cma_base_page = phys_to_page(vc_cma_base); - -@@ -1038,7 +1086,7 @@ static int vc_cma_init(void) - for (vc_cma_chunks_used = 0; - vc_cma_chunks_used < chunks_needed; vc_cma_chunks_used++) { - struct page *chunk; -- chunk = dma_alloc_from_contiguous(NULL /*&vc_cma_device.dev*/, -+ chunk = dma_alloc_from_contiguous(&vc_cma_device.dev, - PAGES_PER_CHUNK, - VC_CMA_CHUNK_ORDER); - if (!chunk) - -From 35c8e88b85da5994d81aad77928546f82784d5cc Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 18 Feb 2015 22:00:00 +0000 -Subject: [PATCH 113/172] config: extra options - ---- - arch/arm/configs/bcm2709_defconfig | 23 +++- - arch/arm/configs/bcm2835_sdcard_defconfig | 176 ------------------------------ - arch/arm/configs/bcmrpi_defconfig | 22 +++- - 3 files changed, 33 insertions(+), 188 deletions(-) - delete mode 100644 arch/arm/configs/bcm2835_sdcard_defconfig - -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index d4c18fa..bc557a5 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -5,7 +5,6 @@ CONFIG_LOCALVERSION="-v7" - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y - CONFIG_FHANDLE=y --CONFIG_AUDIT=y - CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y -@@ -14,12 +13,11 @@ CONFIG_TASKSTATS=y - CONFIG_TASK_DELAY_ACCT=y - CONFIG_TASK_XACCT=y - CONFIG_TASK_IO_ACCOUNTING=y --CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG=m - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y - CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y -@@ -55,7 +53,6 @@ CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_KEXEC=y - CONFIG_CPU_FREQ=y - CONFIG_CPU_FREQ_STAT=m - CONFIG_CPU_FREQ_STAT_DETAILS=y -@@ -64,11 +61,11 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y - CONFIG_CPU_FREQ_GOV_USERSPACE=y - CONFIG_CPU_FREQ_GOV_ONDEMAND=y - CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_NEON=y - CONFIG_KERNEL_MODE_NEON=y - CONFIG_BINFMT_MISC=m -+# CONFIG_SUSPEND is not set - CONFIG_NET=y - CONFIG_PACKET=y - CONFIG_UNIX=y -@@ -126,7 +123,6 @@ CONFIG_NF_CONNTRACK_SIP=m - CONFIG_NF_CONNTRACK_TFTP=m - CONFIG_NF_CT_NETLINK=m - CONFIG_NETFILTER_XT_SET=m --CONFIG_NETFILTER_XT_TARGET_AUDIT=m - CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m - CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m - CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -@@ -282,6 +278,7 @@ CONFIG_BRIDGE=m - CONFIG_VLAN_8021Q=m - CONFIG_VLAN_8021Q_GVRP=y - CONFIG_ATALK=m -+CONFIG_6LOWPAN=m - CONFIG_NET_SCHED=y - CONFIG_NET_SCH_CBQ=m - CONFIG_NET_SCH_HTB=m -@@ -345,6 +342,9 @@ CONFIG_BPQETHER=m - CONFIG_BAYCOM_SER_FDX=m - CONFIG_BAYCOM_SER_HDX=m - CONFIG_YAM=m -+CONFIG_CAN=m -+CONFIG_CAN_VCAN=m -+CONFIG_CAN_MCP251X=m - CONFIG_IRDA=m - CONFIG_IRLAN=m - CONFIG_IRNET=m -@@ -360,12 +360,14 @@ CONFIG_USB_IRDA=m - CONFIG_SIGMATEL_FIR=m - CONFIG_MCS_FIR=m - CONFIG_BT=m -+CONFIG_BT_6LOWPAN=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 -+CONFIG_BT_6LOWPAN=m - CONFIG_BT_HCIBTUSB=m - CONFIG_BT_HCIBCM203X=m - CONFIG_BT_HCIBPA10X=m -@@ -394,6 +396,7 @@ CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_ATA_OVER_ETH=m - CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set -@@ -527,6 +530,9 @@ CONFIG_JOYSTICK_XPAD=m - CONFIG_JOYSTICK_XPAD_FF=y - CONFIG_INPUT_TOUCHSCREEN=y - CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m -+CONFIG_TOUCHSCREEN_STMPE=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -560,6 +566,7 @@ CONFIG_I2C_CHARDEV=m - CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m -@@ -567,6 +574,7 @@ CONFIG_PPS_CLIENT_LDISC=m - CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y - CONFIG_GPIO_ARIZONA=m -+CONFIG_GPIO_STMPE=y - CONFIG_W1=m - CONFIG_W1_MASTER_DS2490=m - CONFIG_W1_MASTER_DS2482=m -@@ -592,6 +600,8 @@ CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y - CONFIG_BCM2708_WDT=m - CONFIG_UCB1400_CORE=m -+CONFIG_MFD_STMPE=y -+CONFIG_STMPE_SPI=y - CONFIG_MFD_ARIZONA_I2C=m - CONFIG_MFD_ARIZONA_SPI=m - CONFIG_MFD_WM5102=y -@@ -763,6 +773,7 @@ CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set -diff --git a/arch/arm/configs/bcm2835_sdcard_defconfig b/arch/arm/configs/bcm2835_sdcard_defconfig -deleted file mode 100644 -index 987471b..0000000 ---- a/arch/arm/configs/bcm2835_sdcard_defconfig -+++ /dev/null -@@ -1,176 +0,0 @@ --# CONFIG_ARM_PATCH_PHYS_VIRT is not set --CONFIG_LOCALVERSION="-quick" --# CONFIG_LOCALVERSION_AUTO is not set --# CONFIG_SWAP is not set --CONFIG_SYSVIPC=y --CONFIG_POSIX_MQUEUE=y --CONFIG_NO_HZ=y --CONFIG_HIGH_RES_TIMERS=y --CONFIG_IKCONFIG=y --CONFIG_IKCONFIG_PROC=y --CONFIG_KALLSYMS_ALL=y --CONFIG_EMBEDDED=y --CONFIG_PERF_EVENTS=y --# CONFIG_COMPAT_BRK is not set --CONFIG_SLAB=y --# CONFIG_BLK_DEV_BSG is not set --CONFIG_ARCH_BCM2708=y --# CONFIG_BCM2708_GPIO is not set --CONFIG_PREEMPT=y --CONFIG_AEABI=y --CONFIG_UACCESS_WITH_MEMCPY=y --CONFIG_ZBOOT_ROM_TEXT=0x0 --CONFIG_ZBOOT_ROM_BSS=0x0 --CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_CPU_FREQ=y --CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y --CONFIG_CPU_FREQ_GOV_PERFORMANCE=y --CONFIG_CPU_FREQ_GOV_USERSPACE=y --CONFIG_CPU_FREQ_GOV_ONDEMAND=y --CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y --CONFIG_VFP=y --CONFIG_BINFMT_MISC=y --CONFIG_NET=y --CONFIG_PACKET=y --CONFIG_UNIX=y --CONFIG_INET=y --CONFIG_IP_MULTICAST=y --CONFIG_IP_PNP=y --CONFIG_IP_PNP_DHCP=y --CONFIG_IP_PNP_RARP=y --CONFIG_SYN_COOKIES=y --# CONFIG_INET_XFRM_MODE_TRANSPORT is not set --# CONFIG_INET_XFRM_MODE_TUNNEL is not set --# CONFIG_INET_XFRM_MODE_BEET is not set --# CONFIG_INET_LRO is not set --# CONFIG_INET_DIAG is not set --# CONFIG_IPV6 is not set --# CONFIG_WIRELESS is not set --CONFIG_DEVTMPFS=y --CONFIG_DEVTMPFS_MOUNT=y --CONFIG_BLK_DEV_LOOP=y --CONFIG_BLK_DEV_RAM=y --CONFIG_SCSI=y --# CONFIG_SCSI_PROC_FS is not set --# CONFIG_SCSI_LOWLEVEL is not set --CONFIG_NETDEVICES=y --CONFIG_MII=y --# CONFIG_NET_VENDOR_BROADCOM is not set --# CONFIG_NET_VENDOR_CIRRUS is not set --# CONFIG_NET_VENDOR_FARADAY is not set --# CONFIG_NET_VENDOR_INTEL is not set --# CONFIG_NET_VENDOR_MARVELL is not set --# CONFIG_NET_VENDOR_MICREL is not set --# CONFIG_NET_VENDOR_NATSEMI is not set --# CONFIG_NET_VENDOR_SEEQ is not set --# CONFIG_NET_VENDOR_STMICRO is not set --# CONFIG_NET_VENDOR_WIZNET is not set --CONFIG_PHYLIB=y --# CONFIG_WLAN is not set --# CONFIG_INPUT_MOUSEDEV is not set --CONFIG_INPUT_EVDEV=y --# CONFIG_INPUT_KEYBOARD is not set --# CONFIG_INPUT_MOUSE is not set --# CONFIG_SERIO is not set --CONFIG_VT_HW_CONSOLE_BINDING=y --# CONFIG_LEGACY_PTYS is not set --# CONFIG_DEVKMEM is not set --CONFIG_SERIAL_AMBA_PL011=y --CONFIG_SERIAL_AMBA_PL011_CONSOLE=y --CONFIG_TTY_PRINTK=y --CONFIG_RAW_DRIVER=y --CONFIG_WATCHDOG=y --CONFIG_REGULATOR=y --CONFIG_REGULATOR_DEBUG=y --CONFIG_REGULATOR_FIXED_VOLTAGE=y --CONFIG_REGULATOR_VIRTUAL_CONSUMER=y --CONFIG_REGULATOR_USERSPACE_CONSUMER=y --CONFIG_FB=y --CONFIG_FRAMEBUFFER_CONSOLE=y --CONFIG_LOGO=y --# CONFIG_LOGO_LINUX_MONO is not set --# CONFIG_LOGO_LINUX_VGA16 is not set --CONFIG_SOUND=y --CONFIG_SND=y --CONFIG_MMC=y --CONFIG_MMC_SDHCI=y --CONFIG_MMC_SDHCI_PLTFM=y --CONFIG_MMC_SDHCI_BCM2708=y --CONFIG_MMC_SDHCI_BCM2708_DMA=y --CONFIG_NEW_LEDS=y --CONFIG_LEDS_CLASS=y --CONFIG_LEDS_TRIGGERS=y --# CONFIG_IOMMU_SUPPORT is not set --CONFIG_EXT4_FS=y --CONFIG_EXT4_FS_POSIX_ACL=y --CONFIG_EXT4_FS_SECURITY=y --CONFIG_AUTOFS4_FS=y --CONFIG_FSCACHE=y --CONFIG_CACHEFILES=y --CONFIG_MSDOS_FS=y --CONFIG_VFAT_FS=y --CONFIG_FAT_DEFAULT_IOCHARSET="ascii" --CONFIG_TMPFS=y --CONFIG_TMPFS_POSIX_ACL=y --CONFIG_CONFIGFS_FS=y --# CONFIG_MISC_FILESYSTEMS is not set --CONFIG_NFS_FS=y --CONFIG_NFS_V3_ACL=y --CONFIG_NFS_V4=y --CONFIG_ROOT_NFS=y --CONFIG_NFS_FSCACHE=y --CONFIG_NLS_DEFAULT="utf8" --CONFIG_NLS_CODEPAGE_437=y --CONFIG_NLS_CODEPAGE_737=y --CONFIG_NLS_CODEPAGE_775=y --CONFIG_NLS_CODEPAGE_850=y --CONFIG_NLS_CODEPAGE_852=y --CONFIG_NLS_CODEPAGE_855=y --CONFIG_NLS_CODEPAGE_857=y --CONFIG_NLS_CODEPAGE_860=y --CONFIG_NLS_CODEPAGE_861=y --CONFIG_NLS_CODEPAGE_862=y --CONFIG_NLS_CODEPAGE_863=y --CONFIG_NLS_CODEPAGE_864=y --CONFIG_NLS_CODEPAGE_865=y --CONFIG_NLS_CODEPAGE_866=y --CONFIG_NLS_CODEPAGE_869=y --CONFIG_NLS_CODEPAGE_936=y --CONFIG_NLS_CODEPAGE_950=y --CONFIG_NLS_CODEPAGE_932=y --CONFIG_NLS_CODEPAGE_949=y --CONFIG_NLS_CODEPAGE_874=y --CONFIG_NLS_ISO8859_8=y --CONFIG_NLS_CODEPAGE_1250=y --CONFIG_NLS_CODEPAGE_1251=y --CONFIG_NLS_ASCII=y --CONFIG_NLS_ISO8859_1=y --CONFIG_NLS_ISO8859_2=y --CONFIG_NLS_ISO8859_3=y --CONFIG_NLS_ISO8859_4=y --CONFIG_NLS_ISO8859_5=y --CONFIG_NLS_ISO8859_6=y --CONFIG_NLS_ISO8859_7=y --CONFIG_NLS_ISO8859_9=y --CONFIG_NLS_ISO8859_13=y --CONFIG_NLS_ISO8859_14=y --CONFIG_NLS_ISO8859_15=y --CONFIG_NLS_UTF8=y --CONFIG_PRINTK_TIME=y --CONFIG_DEBUG_FS=y --# CONFIG_DEBUG_PREEMPT is not set --# CONFIG_DEBUG_BUGVERBOSE is not set --# CONFIG_FTRACE is not set --CONFIG_KGDB=y --CONFIG_KGDB_KDB=y --# CONFIG_ARM_UNWIND is not set --CONFIG_CRYPTO_CBC=y --CONFIG_CRYPTO_HMAC=y --CONFIG_CRYPTO_MD5=y --CONFIG_CRYPTO_SHA1=y --CONFIG_CRYPTO_DES=y --# CONFIG_CRYPTO_ANSI_CPRNG is not set --# CONFIG_CRYPTO_HW is not set --CONFIG_CRC_ITU_T=y --CONFIG_LIBCRC32C=y -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 898ebd2..4d703f4 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -4,7 +4,6 @@ CONFIG_PHYS_OFFSET=0 - CONFIG_SYSVIPC=y - CONFIG_POSIX_MQUEUE=y - CONFIG_FHANDLE=y --CONFIG_AUDIT=y - CONFIG_NO_HZ=y - CONFIG_HIGH_RES_TIMERS=y - CONFIG_BSD_PROCESS_ACCT=y -@@ -13,12 +12,11 @@ CONFIG_TASKSTATS=y - CONFIG_TASK_DELAY_ACCT=y - CONFIG_TASK_XACCT=y - CONFIG_TASK_IO_ACCOUNTING=y --CONFIG_IKCONFIG=y -+CONFIG_IKCONFIG=m - CONFIG_IKCONFIG_PROC=y - CONFIG_CGROUP_FREEZER=y - CONFIG_CGROUP_DEVICE=y - CONFIG_CGROUP_CPUACCT=y --CONFIG_RESOURCE_COUNTERS=y - CONFIG_MEMCG=y - CONFIG_BLK_CGROUP=y - CONFIG_NAMESPACES=y -@@ -50,7 +48,6 @@ CONFIG_SECCOMP=y - CONFIG_ZBOOT_ROM_TEXT=0x0 - CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" --CONFIG_KEXEC=y - CONFIG_CPU_FREQ=y - CONFIG_CPU_FREQ_STAT=m - CONFIG_CPU_FREQ_STAT_DETAILS=y -@@ -59,9 +56,9 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y - CONFIG_CPU_FREQ_GOV_USERSPACE=y - CONFIG_CPU_FREQ_GOV_ONDEMAND=y - CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y --CONFIG_CPU_IDLE=y - CONFIG_VFP=y - CONFIG_BINFMT_MISC=m -+# CONFIG_SUSPEND is not set - CONFIG_NET=y - CONFIG_PACKET=y - CONFIG_UNIX=y -@@ -119,7 +116,6 @@ CONFIG_NF_CONNTRACK_SIP=m - CONFIG_NF_CONNTRACK_TFTP=m - CONFIG_NF_CT_NETLINK=m - CONFIG_NETFILTER_XT_SET=m --CONFIG_NETFILTER_XT_TARGET_AUDIT=m - CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m - CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m - CONFIG_NETFILTER_XT_TARGET_CONNMARK=m -@@ -275,6 +271,7 @@ CONFIG_BRIDGE=m - CONFIG_VLAN_8021Q=m - CONFIG_VLAN_8021Q_GVRP=y - CONFIG_ATALK=m -+CONFIG_6LOWPAN=m - CONFIG_NET_SCHED=y - CONFIG_NET_SCH_CBQ=m - CONFIG_NET_SCH_HTB=m -@@ -338,6 +335,9 @@ CONFIG_BPQETHER=m - CONFIG_BAYCOM_SER_FDX=m - CONFIG_BAYCOM_SER_HDX=m - CONFIG_YAM=m -+CONFIG_CAN=m -+CONFIG_CAN_VCAN=m -+CONFIG_CAN_MCP251X=m - CONFIG_IRDA=m - CONFIG_IRLAN=m - CONFIG_IRNET=m -@@ -353,6 +353,7 @@ CONFIG_USB_IRDA=m - CONFIG_SIGMATEL_FIR=m - CONFIG_MCS_FIR=m - CONFIG_BT=m -+CONFIG_BT_6LOWPAN=m - CONFIG_BT_RFCOMM=m - CONFIG_BT_RFCOMM_TTY=y - CONFIG_BT_BNEP=m -@@ -387,6 +388,7 @@ CONFIG_BLK_DEV_DRBD=m - CONFIG_BLK_DEV_NBD=m - CONFIG_BLK_DEV_RAM=y - CONFIG_CDROM_PKTCDVD=m -+CONFIG_ATA_OVER_ETH=m - CONFIG_EEPROM_AT24=m - CONFIG_SCSI=y - # CONFIG_SCSI_PROC_FS is not set -@@ -520,6 +522,9 @@ CONFIG_JOYSTICK_XPAD=m - CONFIG_JOYSTICK_XPAD_FF=y - CONFIG_INPUT_TOUCHSCREEN=y - CONFIG_TOUCHSCREEN_ADS7846=m -+CONFIG_TOUCHSCREEN_EGALAX=m -+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m -+CONFIG_TOUCHSCREEN_STMPE=m - CONFIG_INPUT_MISC=y - CONFIG_INPUT_AD714X=m - CONFIG_INPUT_ATI_REMOTE2=m -@@ -553,6 +558,7 @@ CONFIG_I2C_CHARDEV=m - CONFIG_I2C_MUX=m - CONFIG_I2C_BCM2708=m - CONFIG_SPI=y -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m -@@ -560,6 +566,7 @@ CONFIG_PPS_CLIENT_LDISC=m - CONFIG_PPS_CLIENT_GPIO=m - CONFIG_GPIO_SYSFS=y - CONFIG_GPIO_ARIZONA=m -+CONFIG_GPIO_STMPE=y - CONFIG_W1=m - CONFIG_W1_MASTER_DS2490=m - CONFIG_W1_MASTER_DS2482=m -@@ -585,6 +592,8 @@ CONFIG_THERMAL_BCM2835=y - CONFIG_WATCHDOG=y - CONFIG_BCM2708_WDT=m - CONFIG_UCB1400_CORE=m -+CONFIG_MFD_STMPE=y -+CONFIG_STMPE_SPI=y - CONFIG_MFD_ARIZONA_I2C=m - CONFIG_MFD_ARIZONA_SPI=m - CONFIG_MFD_WM5102=y -@@ -756,6 +765,7 @@ CONFIG_VIDEO_MT9V011=m - CONFIG_FB=y - CONFIG_FB_BCM2708=y - # CONFIG_BACKLIGHT_GENERIC is not set -+CONFIG_BACKLIGHT_GPIO=m - CONFIG_FRAMEBUFFER_CONSOLE=y - CONFIG_LOGO=y - # CONFIG_LOGO_LINUX_MONO is not set - -From b08151a4eda84af4ce2dbe0b85dab4895964b692 Mon Sep 17 00:00:00 2001 -From: "Yann E. MORIN" -Date: Wed, 18 Feb 2015 12:46:07 +0100 -Subject: [PATCH 114/172] char/vc_cma: fix build with LPAE enabled -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -LPAE is required for virtualisation on ARM. However, enabling LPAE -results in an unbuildable configuration, due to improper formats -used in vc_cma: - - CC drivers/char/broadcom/vc_cma/vc_cma.o - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘vc_cma_alloc_chunks’: - drivers/char/broadcom/vc_cma/vc_cma.c:547:4: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 4 has type ‘phys_addr_t’ [-Werror=format=] - LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " - ^ - drivers/char/broadcom/vc_cma/vc_cma.c:547:4: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 5 has type ‘phys_addr_t’ [-Werror=format=] - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘cma_worker_proc’: - drivers/char/broadcom/vc_cma/vc_cma.c:677:7: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 3 has type ‘long long unsigned int’ [-Werror=format=] - LOG_ERR - ^ - drivers/char/broadcom/vc_cma/vc_cma.c: In function ‘vc_cma_init’: - drivers/char/broadcom/vc_cma/vc_cma.c:1027:2: error: format ‘%x’ expects argument - of type ‘unsigned int’, but argument 2 has type ‘phys_addr_t’ [-Werror=format=] - printk(KERN_INFO "vc-cma: vc_cma_base = 0x%08x\n", vc_cma_base); - ^ - cc1: all warnings being treated as errors - make[6]: *** [drivers/char/broadcom/vc_cma/vc_cma.o] Error 1 - -As explained in Documentation/printk-formats.txt, printing a physical -address phys_addr_t should be done using the %pa[p] format, which -expects a "reference" to the value. - -Even if virtualisation is currently not possible (due to the bootloader -starting the kernel in SVC, not HYP, mode), fix the printk formats -nonetheless, it will anyway give better-looking code! ;-) - -Signed-off-by: "Yann E. MORIN" ---- - drivers/char/broadcom/vc_cma/vc_cma.c | 12 +++++++----- - 1 file changed, 7 insertions(+), 5 deletions(-) - -diff --git a/drivers/char/broadcom/vc_cma/vc_cma.c b/drivers/char/broadcom/vc_cma/vc_cma.c -index bd64317..07b31a7 100644 ---- a/drivers/char/broadcom/vc_cma/vc_cma.c -+++ b/drivers/char/broadcom/vc_cma/vc_cma.c -@@ -590,12 +590,13 @@ static int vc_cma_alloc_chunks(int num_chunks, struct cma_msg *reply) - BUG_ON(((page_to_phys(chunk) - vc_cma_base) % - VC_CMA_CHUNK_SIZE) != 0); - if (chunk_num >= vc_cma_chunks) { -+ phys_addr_t _pa = vc_cma_base + vc_cma_size - 1; - LOG_ERR("%s: ===============================", - __func__); -- LOG_ERR("%s: chunk phys %x, vc_cma %x-%x - " -+ LOG_ERR("%s: chunk phys %x, vc_cma %pa-%pa - " - "bad SPARSEMEM configuration?", - __func__, (unsigned int)page_to_phys(chunk), -- vc_cma_base, vc_cma_base + vc_cma_size - 1); -+ &vc_cma_base, &_pa); - LOG_ERR("%s: dev->cma_area = %p", __func__, - (void*)0/*vc_cma_device.dev.cma_area*/); - LOG_ERR("%s: ===============================", -@@ -722,11 +723,12 @@ static int cma_worker_proc(void *param) - if (!dma_release_from_contiguous - (&vc_cma_device.dev, page, - PAGES_PER_CHUNK)) { -+ phys_addr_t _pa = page_to_phys(page); - LOG_ERR - ("CMA_MSG_FREE - failed to " -- "release chunk %d (phys %x, " -+ "release chunk %d (phys %pa, " - "page %x)", chunk_num, -- page_to_phys(page), -+ &_pa, - (unsigned int)page); - } - vc_cma_chunks_used--; -@@ -1072,7 +1074,7 @@ static int vc_cma_init(void) - goto out_release; - - LOG_INFO("vc-cma: Videocore CMA driver"); -- LOG_INFO("vc-cma: vc_cma_base = 0x%08x", vc_cma_base); -+ LOG_INFO("vc-cma: vc_cma_base = %pa", &vc_cma_base); - LOG_INFO("vc-cma: vc_cma_size = 0x%08x (%u MiB)", - vc_cma_size, vc_cma_size / (1024 * 1024)); - LOG_INFO("vc-cma: vc_cma_initial = 0x%08x (%u MiB)", - -From 60a017bba035a8623994425e9a4e72bf73988358 Mon Sep 17 00:00:00 2001 +From a0be7922a0ebe90a4e23fd86b4fe5797e7ae88fc Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 24 Feb 2015 13:40:50 +0000 -Subject: [PATCH 115/172] pinctrl-bcm2835: Fix interrupt handling for GPIOs +Subject: [PATCH 083/104] pinctrl-bcm2835: Fix interrupt handling for GPIOs 28-31 and 46-53 Contrary to the documentation, the BCM2835 GPIO controller actually has @@ -140059,11 +126676,11 @@ This bug has only just been observed because GPIOs above 27 can only be accessed on an old Raspberry Pi with the optional P5 header fitted, where the pins are often used for I2S instead. --- - drivers/pinctrl/pinctrl-bcm2835.c | 50 +++++++++++++++++++++++++++++---------- - 1 file changed, 38 insertions(+), 12 deletions(-) + drivers/pinctrl/pinctrl-bcm2835.c | 51 ++++++++++++++++++++++++++++++--------- + 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index d9c727b..01a8966 100644 +index d9c727b..ae68710 100644 --- a/drivers/pinctrl/pinctrl-bcm2835.c +++ b/drivers/pinctrl/pinctrl-bcm2835.c @@ -47,6 +47,7 @@ @@ -140179,96 +126796,7 @@ index d9c727b..01a8966 100644 len = strlen(dev_name(pc->dev)) + 16; name = devm_kzalloc(pc->dev, len, GFP_KERNEL); - -From 4ac9769f94555bf911430ae6bb60147c86eef83c Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 25 Feb 2015 17:14:57 +0000 -Subject: [PATCH 116/172] perf: Add support for performance counter interrupt - and enable in DT - ---- - arch/arm/boot/dts/bcm2708.dtsi | 4 ++++ - arch/arm/boot/dts/bcm2709.dtsi | 5 +++++ - arch/arm/mach-bcm2709/armctrl.c | 6 +++++- - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi -index aeeabae..0713a1ed 100644 ---- a/arch/arm/boot/dts/bcm2708.dtsi -+++ b/arch/arm/boot/dts/bcm2708.dtsi -@@ -80,6 +80,10 @@ - leds: leds { - compatible = "gpio-leds"; - }; -+ -+ arm-pmu { -+ compatible = "arm,arm1176-pmu"; -+ }; - }; - - clocks { -diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi -index 6986455..34d2226 100644 ---- a/arch/arm/boot/dts/bcm2709.dtsi -+++ b/arch/arm/boot/dts/bcm2709.dtsi -@@ -80,6 +80,11 @@ - leds: leds { - compatible = "gpio-leds"; - }; -+ -+ arm-pmu { -+ compatible = "arm,cortex-a7-pmu"; -+ interrupts = <3 9>; -+ }; - }; - - clocks { -diff --git a/arch/arm/mach-bcm2709/armctrl.c b/arch/arm/mach-bcm2709/armctrl.c -index 6e3f0f2ff..fc6cb8b 100644 ---- a/arch/arm/mach-bcm2709/armctrl.c -+++ b/arch/arm/mach-bcm2709/armctrl.c -@@ -76,6 +76,8 @@ static void armctrl_mask_irq(struct irq_data *d) - } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { - unsigned int data = (unsigned int)irq_get_chip_data(d->irq); - writel(1 << (data & 0x1f), __io_address(disables[(data >> 5) & 0x3])); -+ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { -+ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_CLR)); - } else { printk("%s: %d\n", __func__, d->irq); BUG(); } - } - -@@ -119,6 +121,8 @@ static void armctrl_unmask_irq(struct irq_data *d) - } else if (d->irq >= ARM_IRQ1_BASE && d->irq < ARM_IRQ_LOCAL_BASE) { - unsigned int data = (unsigned int)irq_get_chip_data(d->irq); - writel(1 << (data & 0x1f), __io_address(enables[(data >> 5) & 0x3])); -+ } else if (d->irq == INTERRUPT_ARM_LOCAL_PMU_FAST) { -+ writel(0xf, __io_address(ARM_LOCAL_PM_ROUTING_SET)); - } else { printk("%s: %d\n", __func__, d->irq); BUG(); } - } - -@@ -145,7 +149,7 @@ static int armctrl_xlate(struct irq_domain *d, struct device_node *ctrlr, - if (WARN_ON(intspec[0] == 0 && intspec[1] >= NR_IRQS_BANK0)) - return -EINVAL; - -- if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5)) -+ if (WARN_ON(intspec[0] == 3 && intspec[1] > 3 && intspec[1] != 5 && intspec[1] != 9)) - return -EINVAL; - - if (intspec[0] == 0) - -From e9793e25e0c44d44d780efcda5b338185d158b41 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Thu, 26 Feb 2015 11:42:34 +0000 -Subject: [PATCH 117/172] pinctrl: squash - ---- - drivers/pinctrl/pinctrl-bcm2835.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/pinctrl/pinctrl-bcm2835.c b/drivers/pinctrl/pinctrl-bcm2835.c -index 01a8966..ae68710 100644 ---- a/drivers/pinctrl/pinctrl-bcm2835.c -+++ b/drivers/pinctrl/pinctrl-bcm2835.c -@@ -1095,6 +1095,7 @@ static struct platform_driver bcm2835_pinctrl_driver = { +@@ -1069,6 +1095,7 @@ static struct platform_driver bcm2835_pinctrl_driver = { .remove = bcm2835_pinctrl_remove, .driver = { .name = MODULE_NAME, @@ -140277,10 +126805,10 @@ index 01a8966..ae68710 100644 }, }; -From 30ed96c07f97f78a923fa5ef0611ace11e521d1c Mon Sep 17 00:00:00 2001 +From ee761823de93128c8a25e31ef84f7214516c326d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 26 Feb 2015 09:58:22 +0000 -Subject: [PATCH 118/172] pinctrl-bcm2835: Only request the interrupts listed +Subject: [PATCH 084/104] pinctrl-bcm2835: Only request the interrupts listed in the DTB Although the GPIO controller can generate three interrupts (four counting @@ -140307,10 +126835,10 @@ index ae68710..87b7a4a 100644 pc->irq_data[i].irqgroup = i; -From ac3374c94c497e1e55a7b734f3bf238a5587430a Mon Sep 17 00:00:00 2001 +From 03a991f4473ffb93d3ada0d93787ba85a033f2e7 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Fri, 27 Feb 2015 15:51:37 +0000 -Subject: [PATCH 119/172] serial/amba-pl011: Activate TX IRQ passively +Subject: [PATCH 085/104] serial/amba-pl011: Activate TX IRQ passively The current PL011 driver transmits a dummy character when the UART is opened, to assert the TX IRQ for the first time @@ -140654,32 +127182,223 @@ index 16ca535..c243729 100644 /* Ensure interrupts from this UART are masked and cleared */ -From 2820ce043829968ca725e149e5a008e3eb86b570 Mon Sep 17 00:00:00 2001 +From b67d48cca0c9b8f1b1cc07ff325f228a1ab51620 Mon Sep 17 00:00:00 2001 From: Dave Martin -Date: Fri, 27 Feb 2015 15:52:31 +0000 -Subject: [PATCH 120/172] serial/amba-pl011: Leave the TX IRQ alone when the - UART is not open +Date: Wed, 1 Apr 2015 14:08:31 +0100 +Subject: [PATCH 086/104] serial/amba-pl011: Refactor and simplify TX FIFO + handling -Getting the TX IRQ re-asserted from scratch can be inefficient in -some setups. +Commit 734745c serial/amba-pl011: Activate TX IRQ passively +adds some complexity and overhead in the form of a softirq +mechanism for transmitting in the absence of interrupts. -This patch avoids clearing the TX IRQ across pl011_shutdown()... -pl011_startup(), so that if the port is closed and reopened, the -IRQ will still work afterwards without having to bootstrap it again. +This patch simplifies the code flow to reduce the reliance on +subtle behaviour and avoid fragility under future maintenance. -The TX IRQ continues to be masked in IMSC when the UART is not in -use. +To this end, the TX softirq mechanism is removed and instead +pl011_start_tx() will now simply stuff the FIFO until full +(guaranteeing future TX IRQs), or until there are no more chars +to write (in which case we don't care whether an IRQ happens). Signed-off-by: Dave Martin +Signed-off-by: Jakub Kicinski --- - drivers/tty/serial/amba-pl011.c | 5 +---- - 1 file changed, 1 insertion(+), 4 deletions(-) + drivers/tty/serial/amba-pl011.c | 119 +++++++++------------------------------- + 1 file changed, 26 insertions(+), 93 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c -index c243729..9c6a2f4 100644 +index c243729..e32f4d7 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c -@@ -1676,9 +1676,6 @@ static int pl011_startup(struct uart_port *port) +@@ -58,7 +58,6 @@ + #include + #include + #include +-#include + + #define UART_NR 14 + +@@ -157,9 +156,7 @@ struct uart_amba_port { + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ + unsigned int old_cr; /* state during shutdown */ +- struct delayed_work tx_softirq_work; + bool autorts; +- unsigned int tx_irq_seen; /* 0=none, 1=1, 2=2 or more */ + char type[12]; + #ifdef CONFIG_DMA_ENGINE + /* DMA stuff */ +@@ -1209,15 +1206,14 @@ static void pl011_stop_tx(struct uart_port *port) + pl011_dma_tx_stop(uap); + } + +-static bool pl011_tx_chars(struct uart_amba_port *uap); ++static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq); + + /* Start TX with programmed I/O only (no DMA) */ + static void pl011_start_tx_pio(struct uart_amba_port *uap) + { + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); +- if (!uap->tx_irq_seen) +- pl011_tx_chars(uap); ++ pl011_tx_chars(uap, false); + } + + static void pl011_start_tx(struct uart_port *port) +@@ -1284,87 +1280,54 @@ __acquires(&uap->port.lock) + spin_lock(&uap->port.lock); + } + +-/* +- * Transmit a character +- * There must be at least one free entry in the TX FIFO to accept the char. +- * +- * Returns true if the FIFO might have space in it afterwards; +- * returns false if the FIFO definitely became full. +- */ +-static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) ++static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c, ++ bool from_irq) + { ++ if (unlikely(!from_irq) && ++ readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) ++ return false; /* unable to transmit character */ ++ + writew(c, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + +- if (likely(uap->tx_irq_seen > 1)) +- return true; +- +- return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF); ++ return true; + } + +-static bool pl011_tx_chars(struct uart_amba_port *uap) ++static void pl011_tx_chars(struct uart_amba_port *uap, bool from_irq) + { + struct circ_buf *xmit = &uap->port.state->xmit; +- int count; +- +- if (unlikely(uap->tx_irq_seen < 2)) +- /* +- * Initial FIFO fill level unknown: we must check TXFF +- * after each write, so just try to fill up the FIFO. +- */ +- count = uap->fifosize; +- else /* tx_irq_seen >= 2 */ +- /* +- * FIFO initially at least half-empty, so we can simply +- * write half the FIFO without polling TXFF. +- +- * Note: the *first* TX IRQ can still race with +- * pl011_start_tx_pio(), which can result in the FIFO +- * being fuller than expected in that case. +- */ +- count = uap->fifosize >> 1; +- +- /* +- * If the FIFO is full we're guaranteed a TX IRQ at some later point, +- * and can't transmit immediately in any case: +- */ +- if (unlikely(uap->tx_irq_seen < 2 && +- readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)) +- return false; ++ int count = uap->fifosize >> 1; + + if (uap->port.x_char) { +- pl011_tx_char(uap, uap->port.x_char); ++ if (!pl011_tx_char(uap, uap->port.x_char, from_irq)) ++ return; + uap->port.x_char = 0; + --count; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) { + pl011_stop_tx(&uap->port); +- goto done; ++ return; + } + + /* If we are using DMA mode, try to send some characters. */ + if (pl011_dma_tx_irq(uap)) +- goto done; ++ return; + +- while (count-- > 0 && pl011_tx_char(uap, xmit->buf[xmit->tail])) { +- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +- if (uart_circ_empty(xmit)) ++ do { ++ if (likely(from_irq) && count-- == 0) + break; +- } ++ ++ if (!pl011_tx_char(uap, xmit->buf[xmit->tail], from_irq)) ++ break; ++ ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ } while (!uart_circ_empty(xmit)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + +- if (uart_circ_empty(xmit)) { ++ if (uart_circ_empty(xmit)) + pl011_stop_tx(&uap->port); +- goto done; +- } +- +- if (unlikely(!uap->tx_irq_seen)) +- schedule_delayed_work(&uap->tx_softirq_work, uap->port.timeout); +- +-done: +- return false; + } + + static void pl011_modem_status(struct uart_amba_port *uap) +@@ -1391,28 +1354,6 @@ static void pl011_modem_status(struct uart_amba_port *uap) + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); + } + +-static void pl011_tx_softirq(struct work_struct *work) +-{ +- struct delayed_work *dwork = to_delayed_work(work); +- struct uart_amba_port *uap = +- container_of(dwork, struct uart_amba_port, tx_softirq_work); +- +- spin_lock(&uap->port.lock); +- while (pl011_tx_chars(uap)) ; +- spin_unlock(&uap->port.lock); +-} +- +-static void pl011_tx_irq_seen(struct uart_amba_port *uap) +-{ +- if (likely(uap->tx_irq_seen > 1)) +- return; +- +- uap->tx_irq_seen++; +- if (uap->tx_irq_seen < 2) +- /* first TX IRQ */ +- cancel_delayed_work(&uap->tx_softirq_work); +-} +- + static irqreturn_t pl011_int(int irq, void *dev_id) + { + struct uart_amba_port *uap = dev_id; +@@ -1451,10 +1392,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) + if (status & (UART011_DSRMIS|UART011_DCDMIS| + UART011_CTSMIS|UART011_RIMIS)) + pl011_modem_status(uap); +- if (status & UART011_TXIS) { +- pl011_tx_irq_seen(uap); +- pl011_tx_chars(uap); +- } ++ if (status & UART011_TXIS) ++ pl011_tx_chars(uap, true); + + if (pass_counter-- == 0) + break; +@@ -1676,9 +1615,6 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); @@ -140689,12485 +127408,28 @@ index c243729..9c6a2f4 100644 spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ -@@ -1742,7 +1739,7 @@ static void pl011_shutdown(struct uart_port *port) - spin_lock_irq(&uap->port.lock); - uap->im = 0; - writew(uap->im, uap->port.membase + UART011_IMSC); -- writew(0xffff, uap->port.membase + UART011_ICR); -+ writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR); - spin_unlock_irq(&uap->port.lock); - - pl011_dma_shutdown(uap); - -From 8def067fc67ecc6a7023cb22af2d7e39e7af3a3d Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:09 +0100 -Subject: [PATCH 121/172] staging: fbtft: core support - -This commit adds the core fbtft framework from -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/Kconfig | 2 + - drivers/staging/Makefile | 1 + - drivers/staging/fbtft/Kconfig | 9 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/README | 32 + - drivers/staging/fbtft/fbtft-bus.c | 256 ++++++ - drivers/staging/fbtft/fbtft-core.c | 1516 +++++++++++++++++++++++++++++++++++ - drivers/staging/fbtft/fbtft-io.c | 409 ++++++++++ - drivers/staging/fbtft/fbtft-sysfs.c | 222 +++++ - drivers/staging/fbtft/fbtft.h | 447 +++++++++++ - 10 files changed, 2897 insertions(+) - create mode 100644 drivers/staging/fbtft/Kconfig - create mode 100644 drivers/staging/fbtft/Makefile - create mode 100644 drivers/staging/fbtft/README - create mode 100644 drivers/staging/fbtft/fbtft-bus.c - create mode 100644 drivers/staging/fbtft/fbtft-core.c - create mode 100644 drivers/staging/fbtft/fbtft-io.c - create mode 100644 drivers/staging/fbtft/fbtft-sysfs.c - create mode 100644 drivers/staging/fbtft/fbtft.h - -diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig -index 815de37..ce8f420 100644 ---- a/drivers/staging/Kconfig -+++ b/drivers/staging/Kconfig -@@ -106,4 +106,6 @@ source "drivers/staging/unisys/Kconfig" - - source "drivers/staging/clocking-wizard/Kconfig" - -+source "drivers/staging/fbtft/Kconfig" -+ - endif # STAGING -diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile -index 33c640b..c242787 100644 ---- a/drivers/staging/Makefile -+++ b/drivers/staging/Makefile -@@ -45,3 +45,4 @@ obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/ - obj-$(CONFIG_CRYPTO_SKEIN) += skein/ - obj-$(CONFIG_UNISYSSPAR) += unisys/ - obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD) += clocking-wizard/ -+obj-$(CONFIG_FB_TFT) += fbtft/ -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -new file mode 100644 -index 0000000..3d6598b ---- /dev/null -+++ b/drivers/staging/fbtft/Kconfig -@@ -0,0 +1,9 @@ -+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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -new file mode 100644 -index 0000000..9132dde ---- /dev/null -+++ b/drivers/staging/fbtft/Makefile -@@ -0,0 +1,3 @@ -+# Core module -+obj-$(CONFIG_FB_TFT) += fbtft.o -+fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft-io.o -diff --git a/drivers/staging/fbtft/README b/drivers/staging/fbtft/README -new file mode 100644 -index 0000000..ba4c74c ---- /dev/null -+++ b/drivers/staging/fbtft/README -@@ -0,0 +1,32 @@ -+ FBTFT -+========= -+ -+Linux Framebuffer drivers for small TFT LCD display modules. -+The module 'fbtft' makes writing drivers for some of these displays very easy. -+ -+Development is done on a Raspberry Pi running the Raspbian "wheezy" distribution. -+ -+INSTALLATION -+ Download kernel sources -+ -+ From Linux 3.15 -+ cd drivers/video/fbdev/fbtft -+ git clone https://github.com/notro/fbtft.git -+ -+ Add to drivers/video/fbdev/Kconfig: source "drivers/video/fbdev/fbtft/Kconfig" -+ Add to drivers/video/fbdev/Makefile: obj-y += fbtft/ -+ -+ Before Linux 3.15 -+ cd drivers/video -+ git clone https://github.com/notro/fbtft.git -+ -+ Add to drivers/video/Kconfig: source "drivers/video/fbtft/Kconfig" -+ Add to drivers/video/Makefile: obj-y += fbtft/ -+ -+ Enable driver(s) in menuconfig and build the kernel -+ -+ -+See wiki for more information: https://github.com/notro/fbtft/wiki -+ -+ -+Source: https://github.com/notro/fbtft/ -diff --git a/drivers/staging/fbtft/fbtft-bus.c b/drivers/staging/fbtft/fbtft-bus.c -new file mode 100644 -index 0000000..b3cddb0 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c -new file mode 100644 -index 0000000..873e2c7 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c -new file mode 100644 -index 0000000..dfa2c46 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft-sysfs.c b/drivers/staging/fbtft/fbtft-sysfs.c -new file mode 100644 -index 0000000..45f8de3 ---- /dev/null -+++ b/drivers/staging/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/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h -new file mode 100644 -index 0000000..0dbf3f9 ---- /dev/null -+++ b/drivers/staging/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 */ - -From 06d51134bbb3a8c0424ff056c590f48c5c118655 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:10 +0100 -Subject: [PATCH 122/172] staging: fbtft: add fb_agm1264k-fl driver - -This commit adds the fb_agm1264k-fl driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/fb_agm1264k-fl.c | 462 +++++++++++++++++++++++++++++++++ - 3 files changed, 471 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_agm1264k-fl.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 3d6598b..d552af6 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -7,3 +7,9 @@ menuconfig FB_TFT - 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) -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 9132dde..eb13cc5 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -1,3 +1,6 @@ - # 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 -diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c -new file mode 100644 -index 0000000..7fe4fa0 ---- /dev/null -+++ b/drivers/staging/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"); - -From d39ffbc96a102b600acb37c3474492efa6ec8cff Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:11 +0100 -Subject: [PATCH 123/172] staging: fbtft: add fb_bd663474 driver - -This commit adds the fb_bd663474 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_bd663474.c | 193 ++++++++++++++++++++++++++++++++++++ - 3 files changed, 200 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_bd663474.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d552af6..400492a 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -13,3 +13,9 @@ config FB_TFT_AGM1264K_FL - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index eb13cc5..9f80a6e 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -4,3 +4,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - - # drivers - obj-$(CONFIG_FB_TFT_AGM1264K_FL) += fb_agm1264k-fl.o -+obj-$(CONFIG_FB_TFT_BD663474) += fb_bd663474.o -diff --git a/drivers/staging/fbtft/fb_bd663474.c b/drivers/staging/fbtft/fb_bd663474.c -new file mode 100644 -index 0000000..7e00c60 ---- /dev/null -+++ b/drivers/staging/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"); - -From 67c6f93bb8a6222372f8a9101f0cc251519af1cb Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:12 +0100 -Subject: [PATCH 124/172] staging: fbtft: add fb_hx8340bn driver - -This commit adds the fb_hx8340bn driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8340bn.c | 229 ++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8340bn.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 400492a..e6c23d5e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -19,3 +19,9 @@ config FB_TFT_BD663474 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 9f80a6e..7d89af7 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -5,3 +5,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - # 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 -diff --git a/drivers/staging/fbtft/fb_hx8340bn.c b/drivers/staging/fbtft/fb_hx8340bn.c -new file mode 100644 -index 0000000..3939502f ---- /dev/null -+++ b/drivers/staging/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"); - -From 8085e50c45d7c0265dc4e964c8c97cbe6c94f0f7 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:13 +0100 -Subject: [PATCH 125/172] staging: fbtft: add fb_hx8347d driver - -This commit adds the fb_hx8347d driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8347d.c | 181 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 188 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8347d.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index e6c23d5e..9bb6e7b 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -25,3 +25,9 @@ config FB_TFT_HX8340BN - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 7d89af7..de68bf8 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -6,3 +6,4 @@ fbtft-y += fbtft-core.o fbtft-sysfs.o fbtft-bus.o fbtft - 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 -diff --git a/drivers/staging/fbtft/fb_hx8347d.c b/drivers/staging/fbtft/fb_hx8347d.c -new file mode 100644 -index 0000000..8139a8f ---- /dev/null -+++ b/drivers/staging/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"); - -From e8dd102dd88b944666f475b2561947f014dc5322 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:14 +0100 -Subject: [PATCH 126/172] staging: fbtft: add fb_hx8353d driver - -This commit adds the fb_hx8353d driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_hx8353d.c | 166 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 173 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_hx8353d.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 9bb6e7b..015429b 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -31,3 +31,9 @@ config FB_TFT_HX8347D - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index de68bf8..766e285 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -7,3 +7,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_hx8353d.c b/drivers/staging/fbtft/fb_hx8353d.c -new file mode 100644 -index 0000000..c9512dc ---- /dev/null -+++ b/drivers/staging/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"); - -From ac4b97f943abb1e75ae9ce89b9a87c4c11ca7540 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:15 +0100 -Subject: [PATCH 127/172] staging: fbtft: add fb_ili9320 driver - -This commit adds the fb_ili9320 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9320.c | 234 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 241 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9320.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 015429b..eb57f19 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -37,3 +37,9 @@ config FB_TFT_HX8353D - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 766e285..e101cb6 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -8,3 +8,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9320.c b/drivers/staging/fbtft/fb_ili9320.c -new file mode 100644 -index 0000000..b26d893 ---- /dev/null -+++ b/drivers/staging/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"); - -From f4cd1ccd1d97e19bf697027ef2bf6fe49b7931d9 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:16 +0100 -Subject: [PATCH 128/172] staging: fbtft: add fb_ili9325 driver - -This commit adds the fb_ili9325 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9325.c | 291 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 298 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9325.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index eb57f19..24b9d91 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -43,3 +43,9 @@ config FB_TFT_ILI9320 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index e101cb6..09d4697 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -9,3 +9,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9325.c b/drivers/staging/fbtft/fb_ili9325.c -new file mode 100644 -index 0000000..5f88145 ---- /dev/null -+++ b/drivers/staging/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"); - -From 1fde4032c966ca64aa650d1d432f0e624dda2ef4 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:17 +0100 -Subject: [PATCH 129/172] staging: fbtft: add fb_ili9340 driver - -This commit adds the fb_ili9340 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9340.c | 163 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 170 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9340.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 24b9d91..6295acf 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -49,3 +49,9 @@ config FB_TFT_ILI9325 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 09d4697..aa65da8 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -10,3 +10,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9340.c b/drivers/staging/fbtft/fb_ili9340.c -new file mode 100644 -index 0000000..985687d ---- /dev/null -+++ b/drivers/staging/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"); - -From 231a5ec27c7d2ef02ec3bebe2797ece490f3083e Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:18 +0100 -Subject: [PATCH 130/172] staging: fbtft: add fb_ili9341 driver - -This commit adds the fb_ili9341 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9341.c | 179 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 186 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9341.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 6295acf..ee09e9d 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -55,3 +55,9 @@ config FB_TFT_ILI9340 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index aa65da8..a83a685 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -11,3 +11,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9341.c b/drivers/staging/fbtft/fb_ili9341.c -new file mode 100644 -index 0000000..225b2d8 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4df2aaf201deb710cb086303678f90ea9f734a5e Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:19 +0100 -Subject: [PATCH 131/172] staging: fbtft: add fb_ili9481 driver - -This commit adds the fb_ili9481 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9481.c | 117 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 124 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9481.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index ee09e9d..86a1bee 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -61,3 +61,9 @@ config FB_TFT_ILI9341 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index a83a685..0740641 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -12,3 +12,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9481.c b/drivers/staging/fbtft/fb_ili9481.c -new file mode 100644 -index 0000000..725157a ---- /dev/null -+++ b/drivers/staging/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"); - -From 514dc6a5071ade3f146f9fb20e1ed602a79858b2 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:20 +0100 -Subject: [PATCH 132/172] staging: fbtft: add fb_ili9486 driver - -This commit adds the fb_ili9486 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ili9486.c | 121 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 128 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ili9486.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 86a1bee..ca73537 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -67,3 +67,9 @@ config FB_TFT_ILI9481 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 0740641..8e633cf 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -13,3 +13,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ili9486.c b/drivers/staging/fbtft/fb_ili9486.c -new file mode 100644 -index 0000000..95b8999 ---- /dev/null -+++ b/drivers/staging/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"); - -From 0c2fe050a244e71f458b93795c7542f0c0f7adf9 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:21 +0100 -Subject: [PATCH 133/172] staging: fbtft: add fb_pcd8544 driver - -This commit adds the fb_pcd8544 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_pcd8544.c | 177 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 184 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_pcd8544.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index ca73537..fc1c76a 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -73,3 +73,9 @@ config FB_TFT_ILI9486 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 8e633cf..d7b9409 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -14,3 +14,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -new file mode 100644 -index 0000000..c4f2a5b ---- /dev/null -+++ b/drivers/staging/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"); - -From 0f29b5d63e6cab9d69eb1f7b18b6bd6fd79cf7bc Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:22 +0100 -Subject: [PATCH 134/172] staging: fbtft: add fb_ra8875 driver - -This commit adds the fb_ra8875 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ra8875.c | 331 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 338 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ra8875.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index fc1c76a..d66281e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -79,3 +79,9 @@ config FB_TFT_PCD8544 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index d7b9409..d9aa2b3 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -15,3 +15,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ra8875.c b/drivers/staging/fbtft/fb_ra8875.c -new file mode 100644 -index 0000000..c323c06 ---- /dev/null -+++ b/drivers/staging/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"); - -From a3cb66339f091b595534af93c2e2d5439ea6eddd Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:23 +0100 -Subject: [PATCH 135/172] staging: fbtft: add fb_s6d02a1 driver - -This commit adds the fb_s6d02a1 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_s6d02a1.c | 168 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 175 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_s6d02a1.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d66281e..34676db 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -85,3 +85,9 @@ config FB_TFT_RA8875 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index d9aa2b3..e349e7f 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -16,3 +16,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_s6d02a1.c b/drivers/staging/fbtft/fb_s6d02a1.c -new file mode 100644 -index 0000000..e412a42 ---- /dev/null -+++ b/drivers/staging/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"); - -From 664ff53df529fa15bbdbd95ec761b9438463ca9d Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:24 +0100 -Subject: [PATCH 136/172] staging: fbtft: add fb_s6d1121 driver - -This commit adds the fb_s6d1121 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_s6d1121.c | 208 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 215 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_s6d1121.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 34676db..4fd9cfd 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -91,3 +91,9 @@ config FB_TFT_S6D02A1 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index e349e7f..48b3ce1 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -17,3 +17,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_s6d1121.c b/drivers/staging/fbtft/fb_s6d1121.c -new file mode 100644 -index 0000000..7bbb21b ---- /dev/null -+++ b/drivers/staging/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"); - -From 147010bf2cc6bcd6e415c50c9c1af9dd4e32625a Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:25 +0100 -Subject: [PATCH 137/172] staging: fbtft: add fb_ssd1289 driver - -This commit adds the fb_ssd1289 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1289.c | 206 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 213 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1289.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 4fd9cfd..05e8571 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -97,3 +97,9 @@ config FB_TFT_S6D1121 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 48b3ce1..ea5fa1e 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -18,3 +18,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1289.c b/drivers/staging/fbtft/fb_ssd1289.c -new file mode 100644 -index 0000000..ef46fbc ---- /dev/null -+++ b/drivers/staging/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"); - -From 358b88a04cd9f842fe7ee796f366f1974f9388af Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:26 +0100 -Subject: [PATCH 138/172] staging: fbtft: add fb_ssd1306 driver - -This commit adds the fb_ssd1306 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1306.c | 229 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 236 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1306.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 05e8571..3ae8259 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -103,3 +103,9 @@ config FB_TFT_SSD1289 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index ea5fa1e..b612a4d 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -19,3 +19,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1306.c b/drivers/staging/fbtft/fb_ssd1306.c -new file mode 100644 -index 0000000..5ea195b ---- /dev/null -+++ b/drivers/staging/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"); - -From 21e5c10651440dde2f52472e220cab45418250a2 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:27 +0100 -Subject: [PATCH 139/172] staging: fbtft: add fb_ssd1331 driver - -This commit adds the fb_ssd1331 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1331.c | 205 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 212 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1331.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 3ae8259..f1e4832 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -109,3 +109,9 @@ config FB_TFT_SSD1306 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index b612a4d..8dffb75 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -20,3 +20,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1331.c b/drivers/staging/fbtft/fb_ssd1331.c -new file mode 100644 -index 0000000..da7464f ---- /dev/null -+++ b/drivers/staging/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"); - -From c5d81394418cd5a1c03d9fb1806b487d3aeaa8c8 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:28 +0100 -Subject: [PATCH 140/172] staging: fbtft: add fb_ssd1351 driver - -This commit adds the fb_ssd1351 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_ssd1351.c | 258 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 265 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_ssd1351.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index f1e4832..2ba8ef7 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -115,3 +115,9 @@ config FB_TFT_SSD1331 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 8dffb75..7481e34 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -21,3 +21,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c -new file mode 100644 -index 0000000..062d986 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4ea4d4cde4cefeddf0de53fac33251c1e1fbef0c Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:29 +0100 -Subject: [PATCH 141/172] staging: fbtft: add fb_st7735r driver - -This commit adds the fb_st7735r driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_st7735r.c | 195 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 202 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_st7735r.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 2ba8ef7..6bc2121 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -121,3 +121,9 @@ config FB_TFT_SSD1351 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 7481e34..2771e60 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -22,3 +22,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_st7735r.c b/drivers/staging/fbtft/fb_st7735r.c -new file mode 100644 -index 0000000..ea8f2b0 ---- /dev/null -+++ b/drivers/staging/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"); - -From 4c95802207386b77ce4382d6da461c531b0a5233 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:30 +0100 -Subject: [PATCH 142/172] staging: fbtft: add fb_tinylcd driver - -This commit adds the fb_tinylcd driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_tinylcd.c | 124 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 131 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_tinylcd.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 6bc2121..dd740bfb 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -127,3 +127,9 @@ config FB_TFT_ST7735R - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 2771e60..a5c2d15 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -23,3 +23,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_tinylcd.c b/drivers/staging/fbtft/fb_tinylcd.c -new file mode 100644 -index 0000000..ca98bfb ---- /dev/null -+++ b/drivers/staging/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"); - -From bda5811957a10834fb6fff897798cb88f483df69 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:31 +0100 -Subject: [PATCH 143/172] staging: fbtft: add fb_tls8204 driver - -This commit adds the fb_tls8204 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_tls8204.c | 176 +++++++++++++++++++++++++++++++++++++ - 3 files changed, 183 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_tls8204.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index dd740bfb..73c181c 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -133,3 +133,9 @@ config FB_TFT_TINYLCD - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index a5c2d15..5fa20f0 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -24,3 +24,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_tls8204.c b/drivers/staging/fbtft/fb_tls8204.c -new file mode 100644 -index 0000000..8738c7a ---- /dev/null -+++ b/drivers/staging/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"); - -From 356a07e91094b2137aa065cda8109f3b311c049b Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:32 +0100 -Subject: [PATCH 144/172] staging: fbtft: add fb_uc1701 driver - -This commit adds the fb_uc1701 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 ++ - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_uc1701.c | 210 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 217 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_uc1701.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 73c181c..fb3f4df 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -139,3 +139,9 @@ config FB_TFT_TLS8204 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 5fa20f0..732b219 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -25,3 +25,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_uc1701.c b/drivers/staging/fbtft/fb_uc1701.c -new file mode 100644 -index 0000000..d70ac52 ---- /dev/null -+++ b/drivers/staging/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"); - -From 9b7cb18c0b0d94f9f810f51c3f1ea702041ee48a Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:33 +0100 -Subject: [PATCH 145/172] staging: fbtft: add fb_upd161704 driver - -This commit adds the fb_upd161704 driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_upd161704.c | 206 +++++++++++++++++++++++++++++++++++ - 3 files changed, 213 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_upd161704.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index fb3f4df..886a48c 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -145,3 +145,9 @@ config FB_TFT_UC1701 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 732b219..dca2a45 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -26,3 +26,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_upd161704.c b/drivers/staging/fbtft/fb_upd161704.c -new file mode 100644 -index 0000000..8d7794e ---- /dev/null -+++ b/drivers/staging/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"); - -From fba3874e2796888b550853e6a64b38bf0dfe97f5 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:34 +0100 -Subject: [PATCH 146/172] staging: fbtft: add fb_watterott driver - -This commit adds the fb_watterott driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/fb_watterott.c | 324 +++++++++++++++++++++++++++++++++++ - 3 files changed, 331 insertions(+) - create mode 100644 drivers/staging/fbtft/fb_watterott.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 886a48c..8edec90 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -151,3 +151,9 @@ config FB_TFT_UPD161704 - 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 -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index dca2a45..78008c3 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -27,3 +27,4 @@ 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 -diff --git a/drivers/staging/fbtft/fb_watterott.c b/drivers/staging/fbtft/fb_watterott.c -new file mode 100644 -index 0000000..975b579 ---- /dev/null -+++ b/drivers/staging/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"); - -From dcf965435e041238d46101633705e5b60bd448e3 Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:35 +0100 -Subject: [PATCH 147/172] staging: fbtft: add flexfb driver - -This commit adds the flexfb driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 6 + - drivers/staging/fbtft/Makefile | 1 + - drivers/staging/fbtft/flexfb.c | 593 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 600 insertions(+) - create mode 100644 drivers/staging/fbtft/flexfb.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index 8edec90..d7ddd6e 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -157,3 +157,9 @@ config FB_TFT_WATTEROTT - 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. -diff --git a/drivers/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 78008c3..5b3254a 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -28,3 +28,4 @@ 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 -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -new file mode 100644 -index 0000000..45574a0 ---- /dev/null -+++ b/drivers/staging/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"); - -From e6ea545ba51748ab7d35a9a66d15130f10b20cde Mon Sep 17 00:00:00 2001 -From: Thomas Petazzoni -Date: Wed, 31 Dec 2014 10:11:36 +0100 -Subject: [PATCH 148/172] staging: fbtft: add fbtft_device driver - -This commit adds the fbtft_device driver from the fbtft project at -https://github.com/notro/fbtft. - -Signed-off-by: Thomas Petazzoni -Signed-off-by: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/Kconfig | 4 + - drivers/staging/fbtft/Makefile | 3 + - drivers/staging/fbtft/fbtft_device.c | 1444 ++++++++++++++++++++++++++++++++++ - 3 files changed, 1451 insertions(+) - create mode 100644 drivers/staging/fbtft/fbtft_device.c - -diff --git a/drivers/staging/fbtft/Kconfig b/drivers/staging/fbtft/Kconfig -index d7ddd6e..995a910 100644 ---- a/drivers/staging/fbtft/Kconfig -+++ b/drivers/staging/fbtft/Kconfig -@@ -163,3 +163,7 @@ config FB_FLEX - 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/staging/fbtft/Makefile b/drivers/staging/fbtft/Makefile -index 5b3254a..e773f0f 100644 ---- a/drivers/staging/fbtft/Makefile -+++ b/drivers/staging/fbtft/Makefile -@@ -29,3 +29,6 @@ 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 -diff --git a/drivers/staging/fbtft/fbtft_device.c b/drivers/staging/fbtft/fbtft_device.c -new file mode 100644 -index 0000000..b9f4c30 ---- /dev/null -+++ b/drivers/staging/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"); - -From 6cd6c52b928abddd97ed84d81dc7dc4299e4ef0b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Tue, 20 Jan 2015 23:40:26 +0100 -Subject: [PATCH 149/172] staging: fbtft: remove ARCH_BCM2708 optimization -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -ARCH_BCM2708 is not present in mainline so remove optimization. - -Signed-off-by: Noralf Trønnes -Reported-by: Paul Bolle -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fbtft-io.c | 170 --------------------------------------- - 1 file changed, 170 deletions(-) - -diff --git a/drivers/staging/fbtft/fbtft-io.c b/drivers/staging/fbtft/fbtft-io.c -index dfa2c46..32155a7 100644 ---- a/drivers/staging/fbtft/fbtft-io.c -+++ b/drivers/staging/fbtft/fbtft-io.c -@@ -2,9 +2,6 @@ - #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) -@@ -129,171 +126,6 @@ int fbtft_read_spi(struct fbtft_par *par, void *buf, size_t len) - } - EXPORT_SYMBOL(fbtft_read_spi); +@@ -1734,8 +1670,6 @@ static void pl011_shutdown(struct uart_port *port) + container_of(port, struct uart_amba_port, port); + unsigned int cr; +- cancel_delayed_work_sync(&uap->tx_softirq_work); - --#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 -@@ -405,5 +237,3 @@ int fbtft_write_gpio16_wr_latched(struct fbtft_par *par, void *buf, size_t len) - return -1; - } - EXPORT_SYMBOL(fbtft_write_gpio16_wr_latched); -- --#endif /* CONFIG_ARCH_BCM2708 */ + /* + * disable all interrupts + */ +@@ -2282,7 +2216,6 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) + uap->port.ops = &amba_pl011_pops; + uap->port.flags = UPF_BOOT_AUTOCONF; + uap->port.line = i; +- INIT_DELAYED_WORK(&uap->tx_softirq_work, pl011_tx_softirq); + pl011_dma_probe(&dev->dev, uap); + + /* Ensure interrupts from this UART are masked and cleared */ -From 4db13fcabcf3976a2996aeaabaae5c14dd203506 Mon Sep 17 00:00:00 2001 -From: Dan Carpenter -Date: Fri, 23 Jan 2015 17:20:25 +0300 -Subject: [PATCH 150/172] staging: fbtft: fix allyesconfig build - -There are two functions call reset() so the allyesconfig breaks. Let's -make this one static. - -Reported-by: Jim Davis -Signed-off-by: Dan Carpenter -Tested-by: Jim Davis -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_agm1264k-fl.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_agm1264k-fl.c b/drivers/staging/fbtft/fb_agm1264k-fl.c -index 7fe4fa0..9cc7d25 100644 ---- a/drivers/staging/fbtft/fb_agm1264k-fl.c -+++ b/drivers/staging/fbtft/fb_agm1264k-fl.c -@@ -89,7 +89,7 @@ static int init_display(struct fbtft_par *par) - return 0; - } - --void reset(struct fbtft_par *par) -+static void reset(struct fbtft_par *par) - { - if (par->gpio.reset == -1) - return; - -From 6d2e37a80dfcefcbfbe944a494bfacd7a67fb850 Mon Sep 17 00:00:00 2001 -From: Krzysztof Adamski -Date: Thu, 22 Jan 2015 19:08:58 +0100 -Subject: [PATCH 151/172] staging: fbtft: fbtft-core mark functions static - -This fixes sparse warnings like: - warning: symbol XXX was not declared. Should it be static? - -by declaring all local functions static. - -Signed-off-by: Krzysztof Adamski -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fbtft-core.c | 47 +++++++++++++++++++++----------------- - 1 file changed, 26 insertions(+), 21 deletions(-) - -diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c -index 873e2c7..37dcf7e 100644 ---- a/drivers/staging/fbtft/fbtft-core.c -+++ b/drivers/staging/fbtft/fbtft-core.c -@@ -79,7 +79,7 @@ void fbtft_dbg_hex(const struct device *dev, int groupsize, - } - EXPORT_SYMBOL(fbtft_dbg_hex); - --unsigned long fbtft_request_gpios_match(struct fbtft_par *par, -+static unsigned long fbtft_request_gpios_match(struct fbtft_par *par, - const struct fbtft_gpio *gpio) - { - int ret; -@@ -123,7 +123,7 @@ unsigned long fbtft_request_gpios_match(struct fbtft_par *par, - return FBTFT_GPIO_NO_MATCH; - } - --int fbtft_request_gpios(struct fbtft_par *par) -+static int fbtft_request_gpios(struct fbtft_par *par) - { - struct fbtft_platform_data *pdata = par->pdata; - const struct fbtft_gpio *gpio; -@@ -249,7 +249,7 @@ static int fbtft_request_gpios_dt(struct fbtft_par *par) - #endif - - #ifdef CONFIG_FB_BACKLIGHT --int fbtft_backlight_update_status(struct backlight_device *bd) -+static 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); -@@ -266,7 +266,7 @@ int fbtft_backlight_update_status(struct backlight_device *bd) - return 0; - } - --int fbtft_backlight_get_brightness(struct backlight_device *bd) -+static int fbtft_backlight_get_brightness(struct backlight_device *bd) - { - return bd->props.brightness; - } -@@ -337,7 +337,8 @@ void fbtft_unregister_backlight(struct fbtft_par *par) { }; - 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) -+static 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); -@@ -355,7 +356,7 @@ void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) - } - - --void fbtft_reset(struct fbtft_par *par) -+static void fbtft_reset(struct fbtft_par *par) - { - if (par->gpio.reset == -1) - return; -@@ -367,7 +368,8 @@ void fbtft_reset(struct fbtft_par *par) - } - - --void fbtft_update_display(struct fbtft_par *par, unsigned start_line, unsigned end_line) -+static 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; -@@ -445,7 +447,7 @@ void fbtft_update_display(struct fbtft_par *par, unsigned start_line, unsigned e - } - - --void fbtft_mkdirty(struct fb_info *info, int y, int height) -+static void fbtft_mkdirty(struct fb_info *info, int y, int height) - { - struct fbtft_par *par = info->par; - struct fb_deferred_io *fbdefio = info->fbdefio; -@@ -468,7 +470,7 @@ void fbtft_mkdirty(struct fb_info *info, int y, int height) - schedule_delayed_work(&info->deferred_work, fbdefio->delay); - } - --void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) -+static 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; -@@ -507,7 +509,8 @@ void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist) - } - - --void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) -+static void fbtft_fb_fillrect(struct fb_info *info, -+ const struct fb_fillrect *rect) - { - struct fbtft_par *par = info->par; - -@@ -519,7 +522,8 @@ void fbtft_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) - par->fbtftops.mkdirty(info, rect->dy, rect->height); - } - --void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) -+static void fbtft_fb_copyarea(struct fb_info *info, -+ const struct fb_copyarea *area) - { - struct fbtft_par *par = info->par; - -@@ -531,7 +535,8 @@ void fbtft_fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) - par->fbtftops.mkdirty(info, area->dy, area->height); - } - --void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *image) -+static void fbtft_fb_imageblit(struct fb_info *info, -+ const struct fb_image *image) - { - struct fbtft_par *par = info->par; - -@@ -543,8 +548,8 @@ void fbtft_fb_imageblit(struct fb_info *info, const struct fb_image *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) -+static 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; -@@ -561,16 +566,16 @@ ssize_t fbtft_fb_write(struct fb_info *info, - } - - /* from pxafb.c */ --unsigned int chan_to_field(unsigned chan, struct fb_bitfield *bf) -+static 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) -+static 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; -@@ -598,7 +603,7 @@ int fbtft_fb_setcolreg(unsigned regno, - return ret; - } - --int fbtft_fb_blank(int blank, struct fb_info *info) -+static int fbtft_fb_blank(int blank, struct fb_info *info) - { - struct fbtft_par *par = info->par; - int ret = -EINVAL; -@@ -623,7 +628,7 @@ int fbtft_fb_blank(int blank, struct fb_info *info) - return ret; - } - --void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) -+static void fbtft_merge_fbtftops(struct fbtft_ops *dst, struct fbtft_ops *src) - { - if (src->write) - dst->write = src->write; -@@ -1265,7 +1270,7 @@ EXPORT_SYMBOL(fbtft_init_display); - * - * Return: 0 if successful, negative if error - */ --int fbtft_verify_gpios(struct fbtft_par *par) -+static int fbtft_verify_gpios(struct fbtft_par *par) - { - struct fbtft_platform_data *pdata; - int i; - -From beabd79f51c4211da36c358630199afc2ecc0d70 Mon Sep 17 00:00:00 2001 -From: Mauro Stettler -Date: Sun, 25 Jan 2015 20:26:17 +0900 -Subject: [PATCH 152/172] staging: flexfb: Remove initialization to 0 - -(the initial post had a typo in the short summary, reposting) - -This fixes some of the code style issues in the flexfb driver by removing -unnecessary initializations of static variables to 0 or to NULL. - -Signed-off-by: Mauro Stettler -Cc: Greg Kroah-Hartman -Cc: Thomas Petazzoni -Cc: Noralf Tronnes -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/flexfb.c | 18 +++++++++--------- - 1 file changed, 9 insertions(+), 9 deletions(-) - -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -index 45574a0..4eec888 100644 ---- a/drivers/staging/fbtft/flexfb.c -+++ b/drivers/staging/fbtft/flexfb.c -@@ -31,24 +31,24 @@ - #define DRVNAME "flexfb" - - --static char *chip = NULL; -+static char *chip; - module_param(chip, charp, 0); - MODULE_PARM_DESC(chip, "LCD controller"); - --static unsigned int width = 0; -+static unsigned int width; - module_param(width, uint, 0); - MODULE_PARM_DESC(width, "Display width"); - --static unsigned int height = 0; -+static unsigned int height; - module_param(height, uint, 0); - MODULE_PARM_DESC(height, "Display height"); - - static int init[512]; --static int init_num = 0; -+static int init_num; - module_param_array(init, int, &init_num, 0); - MODULE_PARM_DESC(init, "Init sequence"); - --static unsigned int setaddrwin = 0; -+static unsigned int setaddrwin; - module_param(setaddrwin, uint, 0); - MODULE_PARM_DESC(setaddrwin, "Which set_addr_win() implementation to use"); - -@@ -60,17 +60,17 @@ static unsigned int regwidth = 8; - module_param(regwidth, uint, 0); - MODULE_PARM_DESC(regwidth, "Width of controller register (default: 8)"); - --static bool nobacklight = false; -+static bool nobacklight; - module_param(nobacklight, bool, 0); - MODULE_PARM_DESC(nobacklight, "Turn off backlight functionality."); - --static bool latched = false; -+static bool latched; - module_param(latched, bool, 0); - MODULE_PARM_DESC(latched, "Use with latched 16-bit databus"); - - --static int *initp = NULL; --static int initp_num = 0; -+static int *initp; -+static int initp_num; - - /* default init sequences */ - static int st7735r_init[] = { \ - -From 7e831153b89601cab2d004a6ac0eaeb6e734a2c8 Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:33:03 +0200 -Subject: [PATCH 153/172] staging: fbtft: fix space required after that ';' - -This patch fixes the following checkpatch.pl error: -fix space required after that ';' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index c4f2a5b..a5f0ad4 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x=0;x<84;x++) { -- for (y=0;y<6;y++) { -+ for (x=0; x<84; x++) { -+ for (y=0; y<6; y++) { - *buf = 0x00; -- for (i=0;i<8;i++) { -+ for (i=0; i<8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From fd28a2e66acf9bb757cb5b63aba22e6765ab441b Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:35:30 +0200 -Subject: [PATCH 154/172] staging: fbtft: fix spaces required around that '=' - -This patch fixes the following checkpatch.pl error: -fix spaces required around that '=' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index a5f0ad4..e44eecb 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x=0; x<84; x++) { -- for (y=0; y<6; y++) { -+ for (x = 0; x<84; x++) { -+ for (y = 0; y<6; y++) { - *buf = 0x00; -- for (i=0; i<8; i++) { -+ for (i = 0; i<8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From 6f65a6510903f0c9fceb1f3d65ff28bd9a784e6c Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:37:24 +0200 -Subject: [PATCH 155/172] staging: fbtft: fix spaces required around that '<' - -This patch fixes the following checkpatch.pl error: -fix spaces required around that '<' - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index e44eecb..ab46ccf 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -117,10 +117,10 @@ static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) - - fbtft_par_dbg(DEBUG_WRITE_VMEM, par, "%s()\n", __func__); - -- for (x = 0; x<84; x++) { -- for (y = 0; y<6; y++) { -+ for (x = 0; x < 84; x++) { -+ for (y = 0; y < 6; y++) { - *buf = 0x00; -- for (i = 0; i<8; i++) { -+ for (i = 0; i < 8; i++) { - *buf |= (vmem16[(y*8+i)*84+x] ? 1 : 0) << i; - } - buf++; - -From 2e22627744670a081bb431ca45bbf5cf84a2030c Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:39:56 +0200 -Subject: [PATCH 156/172] staging: fbtft: fix Macros with complex values should - be enclosed in parentheses - -This patch fixes the following checkpatch.pl error: -fix Macros with complex values should be enclosed in parentheses - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index ab46ccf..1d1a865 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -33,7 +33,7 @@ - #define DRVNAME "fb_pcd8544" - #define WIDTH 84 - #define HEIGHT 48 --#define TXBUFLEN 84*6 -+#define TXBUFLEN (84*6) - #define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */ - - static unsigned tc = 0; - -From 3b5f096eae4c0080b6e88f6b8aaa1fffefc5a0c7 Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Tue, 27 Jan 2015 22:42:59 +0200 -Subject: [PATCH 157/172] staging: fbtft: fix do not initialise statics to 0 or - NULL - -This patch fixes the following checkpatch.pl error: -fix do not initialise statics to 0 or NULL - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/fb_pcd8544.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/fbtft/fb_pcd8544.c b/drivers/staging/fbtft/fb_pcd8544.c -index 1d1a865..8b9ebfb 100644 ---- a/drivers/staging/fbtft/fb_pcd8544.c -+++ b/drivers/staging/fbtft/fb_pcd8544.c -@@ -36,7 +36,7 @@ - #define TXBUFLEN (84*6) - #define DEFAULT_GAMMA "40" /* gamma is used to control contrast in this driver */ - --static unsigned tc = 0; -+static unsigned tc; - module_param(tc, uint, 0); - MODULE_PARM_DESC(tc, "TC[1:0] Temperature coefficient: 0-3 (default: 0)"); - - -From 608b9170aa906a03b34017095c076df47c68b10b Mon Sep 17 00:00:00 2001 -From: Heba Aamer -Date: Wed, 28 Jan 2015 15:29:05 +0200 -Subject: [PATCH 158/172] staging: fbtft: remove redundant .owner - -This patch fixes the following coccicheck warning: -No need to set .owner here. The core will do it. - -Signed-off-by: Heba Aamer -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/fbtft/flexfb.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/drivers/staging/fbtft/flexfb.c b/drivers/staging/fbtft/flexfb.c -index 4eec888..90832c3 100644 ---- a/drivers/staging/fbtft/flexfb.c -+++ b/drivers/staging/fbtft/flexfb.c -@@ -559,7 +559,6 @@ static const struct platform_device_id flexfb_platform_ids[] = { - static struct platform_driver flexfb_platform_driver = { - .driver = { - .name = DRVNAME, -- .owner = THIS_MODULE, - }, - .id_table = flexfb_platform_ids, - .probe = flexfb_probe_pdev, - -From fc7a6c1b574ecf2541ba11643b4936e8b24d2b09 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= -Date: Wed, 18 Feb 2015 22:49:42 +0100 -Subject: [PATCH 159/172] config: enable FBTFT drivers - ---- - arch/arm/configs/bcm2709_defconfig | 28 ++++++++++++++++++++++++++++ - arch/arm/configs/bcmrpi_defconfig | 28 ++++++++++++++++++++++++++++ - 2 files changed, 56 insertions(+) - -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index bc557a5..70d9e3b 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -1027,6 +1027,34 @@ CONFIG_LIRC_IMON=m - CONFIG_LIRC_RPI=m - CONFIG_LIRC_SASEM=m - CONFIG_LIRC_SERIAL=m -+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_IOMMU_SUPPORT is not set - CONFIG_EXTCON=m - CONFIG_EXTCON_ARIZONA=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 4d703f4..1563996 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -1019,6 +1019,34 @@ CONFIG_LIRC_IMON=m - CONFIG_LIRC_RPI=m - CONFIG_LIRC_SASEM=m - CONFIG_LIRC_SERIAL=m -+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_IOMMU_SUPPORT is not set - CONFIG_EXTCON=m - CONFIG_EXTCON_ARIZONA=m - -From 1e1f6c97e1b39e609d2c5e63db46082f5dbd1312 Mon Sep 17 00:00:00 2001 +From 379ed9d4153e718fb64f5954348bcb09b46de153 Mon Sep 17 00:00:00 2001 From: Jon Burgess Date: Tue, 17 Feb 2015 13:22:17 +0000 -Subject: [PATCH 160/172] Create generic i2c-rtc overlay for supporting ds1307, +Subject: [PATCH 087/104] Create generic i2c-rtc overlay for supporting ds1307, ds3231, pcf2127 and pcf8523. Signed-off-by: Jon Burgess @@ -153178,11 +127440,11 @@ Signed-off-by: Jon Burgess create mode 100644 arch/arm/boot/dts/i2c-rtc-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 754291e..831d946 100644 +index 6839ece..bc13a9a 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -66,6 +66,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -14,6 +14,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += i2c-rtc-overlay.dtb @@ -153239,10 +127501,10 @@ index 0000000..5d5abb1 + }; +}; -From b06a6f1514466b593b2a533c128dc87cc8d16979 Mon Sep 17 00:00:00 2001 +From f484d5e034443ee69b02cc557e20ad5d06b4eafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:24:05 +0100 -Subject: [PATCH 161/172] dts: overlay: add support for rpi-display +Subject: [PATCH 088/104] dts: overlay: add support for rpi-display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153257,17 +127519,17 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/rpi-display-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 831d946..b3b775b 100644 +index bc13a9a..3b2da65 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -77,6 +77,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb +@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb + diff --git a/arch/arm/boot/dts/rpi-display-overlay.dts b/arch/arm/boot/dts/rpi-display-overlay.dts new file mode 100644 index 0000000..0578810 @@ -153356,10 +127618,10 @@ index 0000000..0578810 + }; +}; -From 9da9198099befac4b05083230123fd114c5d286d Mon Sep 17 00:00:00 2001 +From 6db0c3f79508f0cef74658ee671ad055685f36b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:36:27 +0100 -Subject: [PATCH 162/172] dts: overlay: add support for HY28A display +Subject: [PATCH 089/104] dts: overlay: add support for HY28A display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153375,10 +127637,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/hy28a-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index b3b775b..9d6f947 100644 +index 3b2da65..1e1be41 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -71,6 +71,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb +@@ -19,6 +19,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb @@ -153480,10 +127742,10 @@ index 0000000..3cd3083 + }; +}; -From 44b11d19ade98ef346cb6135d5b9697deadd8b39 Mon Sep 17 00:00:00 2001 +From 6d38a81ec9b4c405e32dd3fb0a61318e5edc1643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:38:00 +0100 -Subject: [PATCH 163/172] dts: overlay: add support for HY28B display +Subject: [PATCH 090/104] dts: overlay: add support for HY28B display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153499,10 +127761,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/hy28b-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 9d6f947..e4b26b3 100644 +index 1e1be41..7321acb 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -72,6 +72,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb +@@ -20,6 +20,7 @@ dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-digi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb @@ -153659,10 +127921,10 @@ index 0000000..f774c4a + }; +}; -From 9fa8ac547b9ec39fb6fcdaa4a3409aa518ffd5cc Mon Sep 17 00:00:00 2001 +From 69280718c53ce21129f1ccb56d5efabe868b6c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Thu, 26 Feb 2015 21:38:47 +0100 -Subject: [PATCH 164/172] dts: overlay: add support for PiScreen display +Subject: [PATCH 091/104] dts: overlay: add support for PiScreen display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -153677,10 +127939,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/piscreen-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index e4b26b3..890c789 100644 +index 7321acb..68ee5dd 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -78,6 +78,7 @@ dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb +@@ -26,6 +26,7 @@ dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb @@ -153789,415 +128051,10 @@ index 0000000..8cd6a95 + }; +}; -From 75aedead57189858c6aec1568f028d8e13445752 Mon Sep 17 00:00:00 2001 -From: Dave Stevenson <6by9@users.noreply.github.com> -Date: Thu, 26 Feb 2015 20:51:03 +0000 -Subject: [PATCH 165/172] BCM2835-V4L2: Fix compliance test failures - -VIDIOC_TRY_FMT and VIDIOC_S_FMT tests were faling due -to reporting V4L2_COLORSPACE_JPEG when the colour -format wasn't V4L2_PIX_FMT_JPEG. -Now reports V4L2_COLORSPACE_SMPTE170M for YUV formats. ---- - drivers/media/platform/bcm2835/bcm2835-camera.c | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c -index e5a0010..eb4d831 100644 ---- a/drivers/media/platform/bcm2835/bcm2835-camera.c -+++ b/drivers/media/platform/bcm2835/bcm2835-camera.c -@@ -774,7 +774,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, - a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; - a->fmt.sizeimage = (preview_port->es.video.width * - preview_port->es.video.height * 3)>>1; -- a->fmt.colorspace = V4L2_COLORSPACE_JPEG; -+ a->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; - } -@@ -860,8 +860,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - - if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -- else -+ else if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; - - v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, -@@ -904,10 +906,12 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) - f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; - -- if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_RGB24) -+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB24) - f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; -- else -+ else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; -+ else -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.priv = 0; - - v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, - -From 4c8a8d6a18dea92a111c797d48e4b05491ec1399 Mon Sep 17 00:00:00 2001 -From: Phil Elwell -Date: Wed, 25 Feb 2015 09:45:56 +0000 -Subject: [PATCH 166/172] vchiq_arm: Complete support for SYNCHRONOUS mode - ---- - .../vc04_services/interface/vchi/vchi_common.h | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 5 +- - .../vc04_services/interface/vchiq_arm/vchiq_core.c | 106 +++++++++++++++++---- - .../vc04_services/interface/vchiq_arm/vchiq_core.h | 1 + - .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 3 + - 5 files changed, 98 insertions(+), 18 deletions(-) - -diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_common.h b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -index d76118c..d535a72 100644 ---- a/drivers/misc/vc04_services/interface/vchi/vchi_common.h -+++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h -@@ -116,6 +116,7 @@ typedef enum - VCHI_SERVICE_OPTION_MIN, - - VCHI_SERVICE_OPTION_TRACE, -+ VCHI_SERVICE_OPTION_SYNCHRONOUS, - - VCHI_SERVICE_OPTION_MAX - } VCHI_SERVICE_OPTION_T; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -index c382740..d2797db 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h -@@ -36,7 +36,7 @@ - - #define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V', 'C', 'H', 'I') - /* The version of VCHIQ - change with any non-trivial change */ --#define VCHIQ_VERSION 7 -+#define VCHIQ_VERSION 8 - /* The minimum compatible version - update to match VCHIQ_VERSION with any - ** incompatible change */ - #define VCHIQ_VERSION_MIN 3 -@@ -47,6 +47,9 @@ - /* The version that introduced the VCHIQ_IOC_CLOSE_DELIVERED ioctl */ - #define VCHIQ_VERSION_CLOSE_DELIVERED 7 - -+/* The version that made it safe to use SYNCHRONOUS mode */ -+#define VCHIQ_VERSION_SYNCHRONOUS_MODE 8 -+ - #define VCHIQ_MAX_STATES 1 - #define VCHIQ_MAX_SERVICES 4096 - #define VCHIQ_MAX_SLOTS 128 -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -index 14a4155..835688b 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c -@@ -66,6 +66,13 @@ struct vchiq_openack_payload { - short version; - }; - -+enum -+{ -+ QMFLAGS_IS_BLOCKING = (1 << 0), -+ QMFLAGS_NO_MUTEX_LOCK = (1 << 1), -+ QMFLAGS_NO_MUTEX_UNLOCK = (1 << 2) -+}; -+ - /* we require this for consistency between endpoints */ - vchiq_static_assert(sizeof(VCHIQ_HEADER_T) == 8); - vchiq_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); -@@ -743,7 +750,7 @@ process_free_queue(VCHIQ_STATE_T *state) - static VCHIQ_STATUS_T - queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - int msgid, const VCHIQ_ELEMENT_T *elements, -- int count, int size, int is_blocking) -+ int count, int size, int flags) - { - VCHIQ_SHARED_STATE_T *local; - VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; -@@ -758,7 +765,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - - WARN_ON(!(stride <= VCHIQ_SLOT_SIZE)); - -- if ((type != VCHIQ_MSG_RESUME) && -+ if (!(flags & QMFLAGS_NO_MUTEX_LOCK) && - (mutex_lock_interruptible(&state->slot_mutex) != 0)) - return VCHIQ_RETRY; - -@@ -766,6 +773,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - int tx_end_index; - - BUG_ON(!service); -+ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); - - if (service->closing) { - /* The service has been closed */ -@@ -841,12 +850,15 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - spin_unlock("a_spinlock); - } - -- header = reserve_space(state, stride, is_blocking); -+ header = reserve_space(state, stride, flags & QMFLAGS_IS_BLOCKING); - - if (!header) { - if (service) - VCHIQ_SERVICE_STATS_INC(service, slot_stalls); -- mutex_unlock(&state->slot_mutex); -+ /* In the event of a failure, return the mutex to the -+ state it was in */ -+ if (!(flags & QMFLAGS_NO_MUTEX_LOCK)) -+ mutex_unlock(&state->slot_mutex); - return VCHIQ_RETRY; - } - -@@ -864,6 +876,8 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - VCHIQ_MSG_DSTPORT(msgid)); - - BUG_ON(!service); -+ BUG_ON((flags & (QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK)) != 0); - - for (i = 0, pos = 0; i < (unsigned int)count; - pos += elements[i++].size) -@@ -965,7 +979,7 @@ queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, - if (service && (type == VCHIQ_MSG_CLOSE)) - vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); - -- if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) -+ if (!(flags & QMFLAGS_NO_MUTEX_UNLOCK)) - mutex_unlock(&state->slot_mutex); - - remote_event_signal(&state->remote->trigger); -@@ -1535,8 +1549,14 @@ parse_open(VCHIQ_STATE_T *state, VCHIQ_HEADER_T *header) - sizeof(ack_payload) - }; - -+ if (state->version_common < -+ VCHIQ_VERSION_SYNCHRONOUS_MODE) -+ service->sync = 0; -+ - /* Acknowledge the OPEN */ -- if (service->sync) { -+ if (service->sync && -+ (state->version_common >= -+ VCHIQ_VERSION_SYNCHRONOUS_MODE)) { - if (queue_message_sync(state, NULL, - VCHIQ_MAKE_MSG( - VCHIQ_MSG_OPENACK, -@@ -1790,6 +1810,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) - vchiq_log_info(vchiq_core_log_level, - "%d: prs CONNECT@%x", - state->id, (unsigned int)header); -+ state->version_common = ((VCHIQ_SLOT_ZERO_T *) -+ state->slot_data)->version; - up(&state->connect); - break; - case VCHIQ_MSG_BULK_RX: -@@ -1943,7 +1965,8 @@ parse_rx_slots(VCHIQ_STATE_T *state) - /* Send a PAUSE in response */ - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -- NULL, 0, 0, 0) == VCHIQ_RETRY) -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK) -+ == VCHIQ_RETRY) - goto bail_not_ready; - if (state->is_master) - pause_bulks(state); -@@ -2041,7 +2064,9 @@ slot_handler_func(void *v) - pause_bulks(state); - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), -- NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ NULL, 0, 0, -+ QMFLAGS_NO_MUTEX_UNLOCK) -+ != VCHIQ_RETRY) { - vchiq_set_conn_state(state, - VCHIQ_CONNSTATE_PAUSE_SENT); - } else { -@@ -2059,7 +2084,8 @@ slot_handler_func(void *v) - case VCHIQ_CONNSTATE_RESUMING: - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), -- NULL, 0, 0, 0) != VCHIQ_RETRY) { -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_LOCK) -+ != VCHIQ_RETRY) { - if (state->is_master) - resume_bulks(state); - vchiq_set_conn_state(state, -@@ -2180,6 +2206,7 @@ sync_func(void *v) - service->remoteport = remoteport; - vchiq_set_service_state(service, - VCHIQ_SRVSTATE_OPENSYNC); -+ service->sync = 1; - up(&service->remove_event); - } - release_message_sync(state, header); -@@ -2358,6 +2385,9 @@ vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, - return VCHIQ_ERROR; - } - -+ if (VCHIQ_VERSION < slot_zero->version) -+ slot_zero->version = VCHIQ_VERSION; -+ - if (is_master) { - local = &slot_zero->master; - remote = &slot_zero->slave; -@@ -2661,7 +2691,7 @@ vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) - vchiq_use_service_internal(service); - status = queue_message(service->state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), -- &body, 1, sizeof(payload), 1); -+ &body, 1, sizeof(payload), QMFLAGS_IS_BLOCKING); - if (status == VCHIQ_SUCCESS) { - /* Wait for the ACK/NAK */ - if (down_interruptible(&service->remove_event) != 0) { -@@ -2690,7 +2720,18 @@ release_service_messages(VCHIQ_SERVICE_T *service) - int slot_last = state->remote->slot_last; - int i; - -- /* Release any claimed messages */ -+ /* Release any claimed messages aimed at this service */ -+ -+ if (service->sync) { -+ VCHIQ_HEADER_T *header = -+ (VCHIQ_HEADER_T *)SLOT_DATA_FROM_INDEX(state, -+ state->remote->slot_sync); -+ if (VCHIQ_MSG_DSTPORT(header->msgid) == service->localport) -+ release_message_sync(state, header); -+ -+ return; -+ } -+ - for (i = state->remote->slot_first; i <= slot_last; i++) { - VCHIQ_SLOT_INFO_T *slot_info = - SLOT_INFO_FROM_INDEX(state, i); -@@ -2888,17 +2929,31 @@ vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) - (VCHIQ_MSG_CLOSE, - service->localport, - VCHIQ_MSG_DSTPORT(service->remoteport)), -- NULL, 0, 0, 0); -+ NULL, 0, 0, QMFLAGS_NO_MUTEX_UNLOCK); - - if (status == VCHIQ_SUCCESS) { -- if (!close_recvd) -+ if (!close_recvd) { -+ /* Change the state while the mutex is -+ still held */ -+ vchiq_set_service_state(service, -+ VCHIQ_SRVSTATE_CLOSESENT); -+ mutex_unlock(&state->slot_mutex); -+ if (service->sync) -+ mutex_unlock(&state->sync_mutex); - break; -+ } - } else if (service->srvstate == VCHIQ_SRVSTATE_OPENSYNC) { - mutex_unlock(&state->sync_mutex); - break; - } else - break; - -+ /* Change the state while the mutex is still held */ -+ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSERECVD); -+ mutex_unlock(&state->slot_mutex); -+ if (service->sync) -+ mutex_unlock(&state->sync_mutex); -+ - status = close_service_complete(service, - VCHIQ_SRVSTATE_CLOSERECVD); - break; -@@ -3005,7 +3060,7 @@ vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) - if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { - if (queue_message(state, NULL, - VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, -- 0, 1) == VCHIQ_RETRY) -+ 0, QMFLAGS_IS_BLOCKING) == VCHIQ_RETRY) - return VCHIQ_RETRY; - - vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTING); -@@ -3291,6 +3346,16 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - service->localport, service->remoteport, dir_char, - size, (unsigned int)bulk->data, (unsigned int)userdata); - -+ /* The slot mutex must be held when the service is being closed, so -+ claim it here to ensure that isn't happening */ -+ if (mutex_lock_interruptible(&state->slot_mutex) != 0) { -+ status = VCHIQ_RETRY; -+ goto cancel_bulk_error_exit; -+ } -+ -+ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) -+ goto unlock_both_error_exit; -+ - if (state->is_master) { - queue->local_insert++; - if (resolve_bulks(service, queue)) -@@ -3304,14 +3369,17 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - status = queue_message(state, NULL, - VCHIQ_MAKE_MSG(dir_msgtype, - service->localport, service->remoteport), -- &element, 1, sizeof(payload), 1); -+ &element, 1, sizeof(payload), -+ QMFLAGS_IS_BLOCKING | -+ QMFLAGS_NO_MUTEX_LOCK | -+ QMFLAGS_NO_MUTEX_UNLOCK); - if (status != VCHIQ_SUCCESS) { -- vchiq_complete_bulk(bulk); -- goto unlock_error_exit; -+ goto unlock_both_error_exit; - } - queue->local_insert++; - } - -+ mutex_unlock(&state->slot_mutex); - mutex_unlock(&service->bulk_mutex); - - vchiq_log_trace(vchiq_core_log_level, -@@ -3335,6 +3403,10 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_HANDLE_T handle, - - return status; - -+unlock_both_error_exit: -+ mutex_unlock(&state->slot_mutex); -+cancel_bulk_error_exit: -+ vchiq_complete_bulk(bulk); - unlock_error_exit: - mutex_unlock(&service->bulk_mutex); - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -index 1b27917..9be484c 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h -@@ -402,6 +402,7 @@ struct vchiq_state_struct { - int initialised; - VCHIQ_CONNSTATE_T conn_state; - int is_master; -+ short version_common; - - VCHIQ_SHARED_STATE_T *local; - VCHIQ_SHARED_STATE_T *remote; -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -index c26eff7..8072ff6 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c -@@ -738,6 +738,9 @@ int32_t vchi_service_set_option(const VCHI_SERVICE_HANDLE_T handle, - case VCHI_SERVICE_OPTION_TRACE: - vchiq_option = VCHIQ_SERVICE_OPTION_TRACE; - break; -+ case VCHI_SERVICE_OPTION_SYNCHRONOUS: -+ vchiq_option = VCHIQ_SERVICE_OPTION_SYNCHRONOUS; -+ break; - default: - service = NULL; - break; - -From 32765026651961cf8f67a28d224165af9adeae32 Mon Sep 17 00:00:00 2001 +From 4ac0db1ce0bd16d4a16add2bd605680556ec6014 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Fri, 27 Feb 2015 15:10:24 +0000 -Subject: [PATCH 167/172] enc28j60: Add device tree compatible string and an +Subject: [PATCH 092/104] enc28j60: Add device tree compatible string and an overlay --- @@ -154208,11 +128065,11 @@ Subject: [PATCH 167/172] enc28j60: Add device tree compatible string and an create mode 100755 arch/arm/boot/dts/enc28j60-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 890c789..7480e1c 100644 +index 68ee5dd..a91c55d 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -66,6 +66,7 @@ dtb-$(CONFIG_BCM2708_DT) += bcm2708-rpi-b-plus.dtb - dtb-$(CONFIG_BCM2709_DT) += bcm2709-rpi-2-b.dtb +@@ -14,6 +14,7 @@ endif + dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += ds1307-rtc-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb @@ -154281,10 +128138,10 @@ index b1b5f66..c6b6e1a 100644 .probe = enc28j60_probe, .remove = enc28j60_remove, -From d90aba0187af45d761259e4060387821b9bfa3f4 Mon Sep 17 00:00:00 2001 +From cd7f5832bc7f0e01e2f1df445d7f5f86434e22e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Fri, 27 Feb 2015 18:17:25 +0100 -Subject: [PATCH 168/172] dts: overlay: add support for Adafruit PiTFT +Subject: [PATCH 093/104] dts: overlay: add support for Adafruit PiTFT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154299,10 +128156,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/pitft28-resistive-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 7480e1c..ad6984e 100644 +index a91c55d..707d373 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -80,6 +80,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb +@@ -28,6 +28,7 @@ dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += pcf8523-rtc-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += piscreen-overlay.dtb @@ -154432,32 +128289,30 @@ index 0000000..d506eae + }; +}; -From acc9df3814ad9edf4a323c12b150123fd690f3be Mon Sep 17 00:00:00 2001 +From 061c3aeaf590bcf9550d78f763a763f97ad46380 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 2 Mar 2015 09:37:02 +0000 -Subject: [PATCH 169/172] enable compiling spi-bcm2835 and add overlay to allow +Subject: [PATCH 094/104] enable compiling spi-bcm2835 and add overlay to allow us to load the driver --- arch/arm/boot/dts/Makefile | 1 + arch/arm/boot/dts/spi-bcm2835-overlay.dts | 18 ++++++++++++++++++ - arch/arm/configs/bcm2709_defconfig | 1 + - arch/arm/configs/bcmrpi_defconfig | 1 + - 4 files changed, 21 insertions(+) + 2 files changed, 19 insertions(+) create mode 100644 arch/arm/boot/dts/spi-bcm2835-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index ad6984e..ed45f81 100644 +index 707d373..3988c44 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -85,6 +85,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb +@@ -33,6 +33,7 @@ dtb-$(RPI_DT_OVERLAYS) += pps-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += rpi-display-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += w1-gpio-pullup-overlay.dtb +dtb-$(RPI_DT_OVERLAYS) += spi-bcm2835-overlay.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb - dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b-plus.dtb - dtb-$(CONFIG_ARCH_BCM_5301X) += \ + + dtb-$(CONFIG_MACH_ASM9260) += \ + alphascale-asm9260-devkit.dtb diff --git a/arch/arm/boot/dts/spi-bcm2835-overlay.dts b/arch/arm/boot/dts/spi-bcm2835-overlay.dts new file mode 100644 index 0000000..fc1e39b @@ -154482,35 +128337,11 @@ index 0000000..fc1e39b + }; + }; +}; -diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig -index 70d9e3b..d1d6b6c 100644 ---- a/arch/arm/configs/bcm2709_defconfig -+++ b/arch/arm/configs/bcm2709_defconfig -@@ -568,6 +568,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m -diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 1563996..044de53 100644 ---- a/arch/arm/configs/bcmrpi_defconfig -+++ b/arch/arm/configs/bcmrpi_defconfig -@@ -560,6 +560,7 @@ CONFIG_I2C_BCM2708=m - CONFIG_SPI=y - CONFIG_SPI_BCM2835=m - CONFIG_SPI_BCM2708=m -+CONFIG_SPI_BCM2835=m - CONFIG_SPI_SPIDEV=y - CONFIG_PPS=m - CONFIG_PPS_CLIENT_LDISC=m -From b27eece1cbc6981540739b849572a58ffd34aefb Mon Sep 17 00:00:00 2001 +From 0ccf9782b6eb86e532bc7dc3b82de29729719219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:39:30 +0100 -Subject: [PATCH 170/172] dts: overlay: add support for MZ61581 display +Subject: [PATCH 095/104] dts: overlay: add support for MZ61581 display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154525,10 +128356,10 @@ Signed-off-by: Noralf Trønnes create mode 100644 arch/arm/boot/dts/mz61581-overlay.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index ed45f81..f918523 100644 +index 3988c44..371d34a 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile -@@ -77,6 +77,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb +@@ -25,6 +25,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb @@ -154652,10 +128483,10 @@ index 0000000..c06fe12 + }; +}; -From e5c5c9e51bb483f34f0210f0406a3cf9597f77d0 Mon Sep 17 00:00:00 2001 +From 6c94db64884db6e86207ebc62acd815154b79bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:40:07 +0100 -Subject: [PATCH 171/172] dts: overlay: piscreen: set speed to 24MHz +Subject: [PATCH 096/104] dts: overlay: piscreen: set speed to 24MHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154682,10 +128513,10 @@ index 8cd6a95..b7fd7ea 100644 bgr; fps = <30>; -From a1c1709f994bde15ce55aac74de0dcb40761fc7b Mon Sep 17 00:00:00 2001 +From f6e5fb7511f69e49729399b5cc30e84710297b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= Date: Sat, 7 Mar 2015 20:40:43 +0100 -Subject: [PATCH 172/172] dts: overlay: rpi-display: pullup irq gpio +Subject: [PATCH 097/104] dts: overlay: rpi-display: pullup irq gpio MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -154710,3 +128541,3535 @@ index 0578810..a8fa974 100644 }; }; }; + +From f85415a5136808a71dd215aed3656f378a7df9c3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 14:48:30 +0100 +Subject: [PATCH 098/104] BCM270x_DT: add bcm2835-dma entry +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Device Tree entry for bcm2835-dma. +The entry doesn't contain any resources since they are handled +by the arch/arm/mach-bcm270x/dma.c driver. +In non-DT mode, don't add the device in the board file. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/boot/dts/bcm2708.dtsi | 5 +++++ + arch/arm/boot/dts/bcm2709.dtsi | 5 +++++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + arch/arm/mach-bcm2709/bcm2709.c | 2 +- + 4 files changed, 12 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 0713a1ed..846f148 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -17,6 +17,11 @@ + #size-cells = <1>; + ranges = <0x7e000000 0x20000000 0x01000000>; + ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ + intc: interrupt-controller { + compatible = "brcm,bcm2708-armctrl-ic"; + reg = <0x7e00b200 0x200>; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index 34d2226..d468363 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -17,6 +17,11 @@ + #size-cells = <1>; + ranges = <0x7e000000 0x3f000000 0x01000000>; + ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ + intc: interrupt-controller { + compatible = "brcm,bcm2708-armctrl-ic"; + reg = <0x7e00b200 0x200>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index c8aea5b..6a08e95 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -857,7 +857,7 @@ void __init bcm2708_init(void) + bcm2708_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); +- bcm_register_device(&bcm2708_dmaengine_device); ++ bcm_register_device_dt(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index e3882f3..ae60973 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -879,7 +879,7 @@ void __init bcm2709_init(void) + bcm2709_dt_init(); + + bcm_register_device(&bcm2708_dmaman_device); +- bcm_register_device(&bcm2708_dmaengine_device); ++ bcm_register_device_dt(&bcm2708_dmaengine_device); + bcm_register_device(&bcm2708_vcio_device); + #ifdef CONFIG_BCM2708_GPIO + bcm_register_device_dt(&bcm2708_gpio_device); + +From a493226d7c7781bb49bbaaadf4180beadd5bbeeb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 14:50:19 +0100 +Subject: [PATCH 099/104] bcm270x: add mmc-bcm2835 clock +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add clock for the mmc-bcm2835.0 device. +Will be used in non-DT mode. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/mach-bcm2708/bcm2708.c | 1 + + arch/arm/mach-bcm2709/bcm2709.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index 6a08e95..b5f22e2 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -233,6 +233,7 @@ void __init bcm2708_init_clocks(void) + bcm2708_register_clkdev(clk, "dev:f1"); + + clk = bcm2708_clk_register("sdhost_clk", 250000000); ++ bcm2708_register_clkdev(clk, "mmc-bcm2835.0"); + bcm2708_register_clkdev(clk, "bcm2708_spi.0"); + bcm2708_register_clkdev(clk, "bcm2708_i2c.0"); + bcm2708_register_clkdev(clk, "bcm2708_i2c.1"); +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index ae60973..a327e74 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -243,6 +243,7 @@ void __init bcm2709_init_clocks(void) + bcm2709_register_clkdev(clk, "dev:f1"); + + clk = bcm2709_clk_register("sdhost_clk", 250000000); ++ bcm2709_register_clkdev(clk, "mmc-bcm2835.0"); + bcm2709_register_clkdev(clk, "bcm2708_spi.0"); + bcm2709_register_clkdev(clk, "bcm2708_i2c.0"); + bcm2709_register_clkdev(clk, "bcm2708_i2c.1"); + +From 5b3a82514aa4e5c58846329cd581b187a4b32731 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= +Date: Tue, 24 Mar 2015 15:12:35 +0100 +Subject: [PATCH 100/104] BCM270x_DT: add bcm2835-mmc entry +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Device Tree entry for bcm2835-mmc. +In non-DT mode, don't add the device in the board file. + +Signed-off-by: Noralf Trønnes +--- + arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 5 +++++ + arch/arm/boot/dts/bcm2708-rpi-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2708.dtsi | 19 +++++++++++++++++++ + arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 5 +++++ + arch/arm/boot/dts/bcm2709.dtsi | 19 +++++++++++++++++++ + arch/arm/mach-bcm2708/bcm2708.c | 2 +- + arch/arm/mach-bcm2709/bcm2709.c | 2 +- + 7 files changed, 55 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +index 7f84473..f25563a 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2708-rpi-b.dts b/arch/arm/boot/dts/bcm2708-rpi-b.dts +index a39562f..17b4b8c 100644 +--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts ++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 846f148..42edda5 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -41,6 +41,17 @@ + #interrupt-cells = <2>; + }; + ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + i2s: i2s@7e203000 { + compatible = "brcm,bcm2708-i2s"; + reg = <0x7e203000 0x20>, +@@ -96,6 +107,14 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ + clk_i2c: i2c { + compatible = "fixed-clock"; + reg = <1>; +diff --git a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +index 75c222f..c73249b 100644 +--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts ++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts +@@ -44,6 +44,11 @@ + }; + }; + ++&mmc { ++ status = "okay"; ++ bus-width = <4>; ++}; ++ + &spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index d468363..bd6605d 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -41,6 +41,17 @@ + #interrupt-cells = <2>; + }; + ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ + i2s: i2s@7e203000 { + compatible = "brcm,bcm2708-i2s"; + reg = <0x7e203000 0x20>, +@@ -97,6 +108,14 @@ + #address-cells = <1>; + #size-cells = <0>; + ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ + clk_i2c: i2c { + compatible = "fixed-clock"; + reg = <1>; +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index b5f22e2..16ba248 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -885,7 +885,7 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_powerman_device); + + #ifdef CONFIG_MMC_BCM2835 +- bcm_register_device(&bcm2835_emmc_device); ++ bcm_register_device_dt(&bcm2835_emmc_device); + #endif + bcm2708_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) +diff --git a/arch/arm/mach-bcm2709/bcm2709.c b/arch/arm/mach-bcm2709/bcm2709.c +index a327e74..aa91259 100644 +--- a/arch/arm/mach-bcm2709/bcm2709.c ++++ b/arch/arm/mach-bcm2709/bcm2709.c +@@ -909,7 +909,7 @@ void __init bcm2709_init(void) + bcm_register_device(&bcm2708_powerman_device); + + #ifdef CONFIG_MMC_BCM2835 +- bcm_register_device(&bcm2835_emmc_device); ++ bcm_register_device_dt(&bcm2835_emmc_device); + #endif + bcm2709_init_led(); + for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) + +From 0fb4c9b7d54a6dd6208b64b5b3f8199e92ec8e51 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Fri, 27 Mar 2015 11:45:44 +0000 +Subject: [PATCH 101/104] BCM270x_DT: Refactor bcm2708.dtsi and bcm2709.dtsi + +Extract the common elements, creating bcm2708_common.dtsi +--- + arch/arm/boot/dts/bcm2708.dtsi | 118 +-------------------------------- + arch/arm/boot/dts/bcm2708_common.dtsi | 120 ++++++++++++++++++++++++++++++++++ + arch/arm/boot/dts/bcm2709.dtsi | 118 +-------------------------------- + 3 files changed, 124 insertions(+), 232 deletions(-) + create mode 100644 arch/arm/boot/dts/bcm2708_common.dtsi + +diff --git a/arch/arm/boot/dts/bcm2708.dtsi b/arch/arm/boot/dts/bcm2708.dtsi +index 42edda5..0d47427 100644 +--- a/arch/arm/boot/dts/bcm2708.dtsi ++++ b/arch/arm/boot/dts/bcm2708.dtsi +@@ -1,133 +1,19 @@ +-/include/ "skeleton.dtsi" ++/include/ "bcm2708_common.dtsi" + + / { + compatible = "brcm,bcm2708"; + model = "BCM2708"; + +- interrupt-parent = <&intc>; +- + chosen { + /* No padding required - the boot loader can do that. */ + bootargs = ""; + }; + +- soc: soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; ++ soc { + ranges = <0x7e000000 0x20000000 0x01000000>; + +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- #dma-cells = <1>; +- }; +- +- intc: interrupt-controller { +- compatible = "brcm,bcm2708-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gpio: gpio { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- interrupts = <2 17>, <2 18>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- mmc: mmc@7e300000 { +- compatible = "brcm,bcm2835-mmc"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clk_mmc>; +- dmas = <&dma 11>, +- <&dma 11>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2708-i2s"; +- reg = <0x7e203000 0x20>, +- <0x7e101098 0x02>; +- +- //dmas = <&dma 2>, +- // <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi0: spi@7e204000 { +- compatible = "brcm,bcm2708-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clk_spi>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- leds: leds { +- compatible = "gpio-leds"; +- }; +- + arm-pmu { + compatible = "arm,arm1176-pmu"; + }; + }; +- +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- clk_mmc: clock@0 { +- compatible = "fixed-clock"; +- reg = <0>; +- #clock-cells = <0>; +- clock-output-names = "mmc"; +- clock-frequency = <250000000>; +- }; +- +- clk_i2c: i2c { +- compatible = "fixed-clock"; +- reg = <1>; +- #clock-cells = <0>; +- clock-frequency = <250000000>; +- }; +- +- clk_spi: clock@2 { +- compatible = "fixed-clock"; +- reg = <2>; +- #clock-cells = <0>; +- clock-output-names = "spi"; +- clock-frequency = <250000000>; +- }; +- }; + }; +diff --git a/arch/arm/boot/dts/bcm2708_common.dtsi b/arch/arm/boot/dts/bcm2708_common.dtsi +new file mode 100644 +index 0000000..ff70c58 +--- /dev/null ++++ b/arch/arm/boot/dts/bcm2708_common.dtsi +@@ -0,0 +1,120 @@ ++/include/ "skeleton.dtsi" ++ ++/ { ++ interrupt-parent = <&intc>; ++ ++ soc: soc { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ dma: dma@7e007000 { ++ compatible = "brcm,bcm2835-dma"; ++ #dma-cells = <1>; ++ }; ++ ++ intc: interrupt-controller { ++ compatible = "brcm,bcm2708-armctrl-ic"; ++ reg = <0x7e00b200 0x200>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ gpio: gpio { ++ compatible = "brcm,bcm2835-gpio"; ++ reg = <0x7e200000 0xb4>; ++ interrupts = <2 17>, <2 18>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ }; ++ ++ mmc: mmc@7e300000 { ++ compatible = "brcm,bcm2835-mmc"; ++ reg = <0x7e300000 0x100>; ++ interrupts = <2 30>; ++ clocks = <&clk_mmc>; ++ dmas = <&dma 11>, ++ <&dma 11>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ i2s: i2s@7e203000 { ++ compatible = "brcm,bcm2708-i2s"; ++ reg = <0x7e203000 0x20>, ++ <0x7e101098 0x02>; ++ ++ //dmas = <&dma 2>, ++ // <&dma 3>; ++ dma-names = "tx", "rx"; ++ status = "disabled"; ++ }; ++ ++ spi0: spi@7e204000 { ++ compatible = "brcm,bcm2708-spi"; ++ reg = <0x7e204000 0x1000>; ++ interrupts = <2 22>; ++ clocks = <&clk_spi>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c0: i2c@7e205000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e205000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ i2c1: i2c@7e804000 { ++ compatible = "brcm,bcm2708-i2c"; ++ reg = <0x7e804000 0x1000>; ++ interrupts = <2 21>; ++ clocks = <&clk_i2c>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ leds: leds { ++ compatible = "gpio-leds"; ++ }; ++ }; ++ ++ clocks { ++ compatible = "simple-bus"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ clk_mmc: clock@0 { ++ compatible = "fixed-clock"; ++ reg = <0>; ++ #clock-cells = <0>; ++ clock-output-names = "mmc"; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_i2c: i2c { ++ compatible = "fixed-clock"; ++ reg = <1>; ++ #clock-cells = <0>; ++ clock-frequency = <250000000>; ++ }; ++ ++ clk_spi: clock@2 { ++ compatible = "fixed-clock"; ++ reg = <2>; ++ #clock-cells = <0>; ++ clock-output-names = "spi"; ++ clock-frequency = <250000000>; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/bcm2709.dtsi b/arch/arm/boot/dts/bcm2709.dtsi +index bd6605d..5e0b935 100644 +--- a/arch/arm/boot/dts/bcm2709.dtsi ++++ b/arch/arm/boot/dts/bcm2709.dtsi +@@ -1,137 +1,23 @@ +-/include/ "skeleton.dtsi" ++/include/ "bcm2708_common.dtsi" + + / { + compatible = "brcm,bcm2709"; + model = "BCM2709"; + +- interrupt-parent = <&intc>; +- + chosen { + /* No padding required - the boot loader can do that. */ + bootargs = ""; + }; + +- soc: soc { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <1>; ++ soc { + ranges = <0x7e000000 0x3f000000 0x01000000>; + +- dma: dma@7e007000 { +- compatible = "brcm,bcm2835-dma"; +- #dma-cells = <1>; +- }; +- +- intc: interrupt-controller { +- compatible = "brcm,bcm2708-armctrl-ic"; +- reg = <0x7e00b200 0x200>; +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- gpio: gpio { +- compatible = "brcm,bcm2835-gpio"; +- reg = <0x7e200000 0xb4>; +- interrupts = <2 17>, <2 18>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- interrupt-controller; +- #interrupt-cells = <2>; +- }; +- +- mmc: mmc@7e300000 { +- compatible = "brcm,bcm2835-mmc"; +- reg = <0x7e300000 0x100>; +- interrupts = <2 30>; +- clocks = <&clk_mmc>; +- dmas = <&dma 11>, +- <&dma 11>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- i2s: i2s@7e203000 { +- compatible = "brcm,bcm2708-i2s"; +- reg = <0x7e203000 0x20>, +- <0x7e101098 0x02>; +- +- //dmas = <&dma 2>, +- // <&dma 3>; +- dma-names = "tx", "rx"; +- status = "disabled"; +- }; +- +- spi0: spi@7e204000 { +- compatible = "brcm,bcm2708-spi"; +- reg = <0x7e204000 0x1000>; +- interrupts = <2 22>; +- clocks = <&clk_spi>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c0: i2c@7e205000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e205000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- i2c1: i2c@7e804000 { +- compatible = "brcm,bcm2708-i2c"; +- reg = <0x7e804000 0x1000>; +- interrupts = <2 21>; +- clocks = <&clk_i2c>; +- #address-cells = <1>; +- #size-cells = <0>; +- status = "disabled"; +- }; +- +- leds: leds { +- compatible = "gpio-leds"; +- }; +- + arm-pmu { + compatible = "arm,cortex-a7-pmu"; + interrupts = <3 9>; + }; + }; + +- clocks { +- compatible = "simple-bus"; +- #address-cells = <1>; +- #size-cells = <0>; +- +- clk_mmc: clock@0 { +- compatible = "fixed-clock"; +- reg = <0>; +- #clock-cells = <0>; +- clock-output-names = "mmc"; +- clock-frequency = <250000000>; +- }; +- +- clk_i2c: i2c { +- compatible = "fixed-clock"; +- reg = <1>; +- #clock-cells = <0>; +- clock-frequency = <250000000>; +- }; +- +- clk_spi: clock@2 { +- compatible = "fixed-clock"; +- reg = <2>; +- #clock-cells = <0>; +- clock-output-names = "spi"; +- clock-frequency = <250000000>; +- }; +- }; +- + timer { + compatible = "arm,armv7-timer"; + clock-frequency = <19200000>; + +From 9a8b27b914735cbf8b886591160b821aacee3920 Mon Sep 17 00:00:00 2001 +From: Waldemar Brodkorb +Date: Wed, 25 Mar 2015 09:26:17 +0100 +Subject: [PATCH 102/104] Add driver for rpi-proto + +Forward port of 3.10.x driver from https://github.com/koalo +We are using a custom board and would like to use rpi 3.18.x +kernel. Patch works fine for our embedded system. + +URL to the audio chip: +http://www.mikroe.com/add-on-boards/audio-voice/audio-codec-proto/ + +Playback tested with devicetree enabled. + +Signed-off-by: Waldemar Brodkorb +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/rpi-proto-overlay.dts | 39 ++++++++ + sound/soc/bcm/Kconfig | 7 ++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-proto.c | 153 ++++++++++++++++++++++++++++++++ + 5 files changed, 202 insertions(+) + create mode 100644 arch/arm/boot/dts/rpi-proto-overlay.dts + create mode 100644 sound/soc/bcm/rpi-proto.c + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index 371d34a..eda0009 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -24,6 +24,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += pcf2127-rtc-overlay.dtb +diff --git a/arch/arm/boot/dts/rpi-proto-overlay.dts b/arch/arm/boot/dts/rpi-proto-overlay.dts +new file mode 100644 +index 0000000..2029930 +--- /dev/null ++++ b/arch/arm/boot/dts/rpi-proto-overlay.dts +@@ -0,0 +1,39 @@ ++// Definitions for Rpi-Proto ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "rpi,rpi-proto"; ++ i2s-controller = <&i2s>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target = <&i2c1>; ++ __overlay__ { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ wm8731@1a { ++ #sound-dai-cells = <0>; ++ compatible = "wlf,wm8731"; ++ reg = <0x1a>; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 40d27c1..003ae28 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -54,6 +54,13 @@ config SND_BCM2708_SOC_RPI_DAC + help + Say Y or M if you want to add support for RPi-DAC. + ++config SND_BCM2708_SOC_RPI_PROTO ++ tristate "Support for Rpi-PROTO" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_WM8731 ++ help ++ Say Y or M if you want to add support for Audio Codec Board PROTO (WM8731). ++ + config SND_BCM2708_SOC_IQAUDIO_DAC + tristate "Support for IQaudIO-DAC" + depends on SND_BCM2708_SOC_I2S +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 17ea2b0..883241b 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -14,6 +14,7 @@ snd-soc-hifiberry-dacplus-objs := hifiberry_dacplus.o + snd-soc-hifiberry-digi-objs := hifiberry_digi.o + snd-soc-hifiberry-amp-objs := hifiberry_amp.o + snd-soc-rpi-dac-objs := rpi-dac.o ++snd-soc-rpi-proto-objs := rpi-proto.o + snd-soc-iqaudio-dac-objs := iqaudio-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o +@@ -21,4 +22,5 @@ obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS) += snd-soc-hifiberry-dacplus.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI) += snd-soc-hifiberry-digi.o + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o + obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_PROTO) += snd-soc-rpi-proto.o + obj-$(CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC) += snd-soc-iqaudio-dac.o +diff --git a/sound/soc/bcm/rpi-proto.c b/sound/soc/bcm/rpi-proto.c +new file mode 100644 +index 0000000..c6e45a0 +--- /dev/null ++++ b/sound/soc/bcm/rpi-proto.c +@@ -0,0 +1,153 @@ ++/* ++ * ASoC driver for PROTO AudioCODEC (with a WM8731) ++ * connected to a Raspberry Pi ++ * ++ * Author: Florian Meier, ++ * Copyright 2013 ++ * ++ * 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 ++ ++#include ++#include ++#include ++#include ++ ++#include "../codecs/wm8731.h" ++ ++static const unsigned int wm8731_rates_12288000[] = { ++ 8000, 32000, 48000, 96000, ++}; ++ ++static struct snd_pcm_hw_constraint_list wm8731_constraints_12288000 = { ++ .list = wm8731_rates_12288000, ++ .count = ARRAY_SIZE(wm8731_rates_12288000), ++}; ++ ++static int snd_rpi_proto_startup(struct snd_pcm_substream *substream) ++{ ++ /* Setup constraints, because there is a 12.288 MHz XTAL on the board */ ++ snd_pcm_hw_constraint_list(substream->runtime, 0, ++ SNDRV_PCM_HW_PARAM_RATE, ++ &wm8731_constraints_12288000); ++ return 0; ++} ++ ++static int snd_rpi_proto_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_codec *codec = rtd->codec; ++ struct snd_soc_dai *codec_dai = rtd->codec_dai; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ int sysclk = 12288000; /* This is fixed on this board */ ++ ++ /* Set proto bclk */ ++ int ret = snd_soc_dai_set_bclk_ratio(cpu_dai,32*2); ++ if (ret < 0){ ++ dev_err(codec->dev, ++ "Failed to set BCLK ratio %d\n", ret); ++ return ret; ++ } ++ ++ /* Set proto sysclk */ ++ ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, ++ sysclk, SND_SOC_CLOCK_IN); ++ if (ret < 0) { ++ dev_err(codec->dev, ++ "Failed to set WM8731 SYSCLK: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_proto_ops = { ++ .startup = snd_rpi_proto_startup, ++ .hw_params = snd_rpi_proto_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_proto_dai[] = { ++{ ++ .name = "WM8731", ++ .stream_name = "WM8731 HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "wm8731-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "wm8731.1-001a", ++ .dai_fmt = SND_SOC_DAIFMT_I2S ++ | SND_SOC_DAIFMT_NB_NF ++ | SND_SOC_DAIFMT_CBM_CFM, ++ .ops = &snd_rpi_proto_ops, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_proto = { ++ .name = "snd_rpi_proto", ++ .dai_link = snd_rpi_proto_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_proto_dai), ++}; ++ ++static int snd_rpi_proto_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_proto.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_proto_dai[0]; ++ i2s_node = of_parse_phandle(pdev->dev.of_node, ++ "i2s-controller", 0); ++ ++ if (i2s_node) { ++ dai->cpu_dai_name = NULL; ++ dai->cpu_of_node = i2s_node; ++ dai->platform_name = NULL; ++ dai->platform_of_node = i2s_node; ++ } ++ } ++ ++ ret = snd_soc_register_card(&snd_rpi_proto); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "snd_soc_register_card() failed: %d\n", ret); ++ } ++ ++ return ret; ++} ++ ++ ++static int snd_rpi_proto_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_proto); ++} ++ ++static const struct of_device_id snd_rpi_proto_of_match[] = { ++ { .compatible = "rpi,rpi-proto", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_proto_of_match); ++ ++static struct platform_driver snd_rpi_proto_driver = { ++ .driver = { ++ .name = "snd-rpi-proto", ++ .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_proto_of_match, ++ }, ++ .probe = snd_rpi_proto_probe, ++ .remove = snd_rpi_proto_remove, ++}; ++ ++module_platform_driver(snd_rpi_proto_driver); ++ ++MODULE_AUTHOR("Florian Meier"); ++MODULE_DESCRIPTION("ASoC Driver for Raspberry Pi connected to PROTO board (WM8731)"); ++MODULE_LICENSE("GPL"); + +From 2615f067017adc52048591f3dd1659647555a92a Mon Sep 17 00:00:00 2001 +From: Clive Messer +Date: Thu, 2 Apr 2015 12:22:55 +0100 +Subject: [PATCH 103/104] Add Device Tree support for RPi-DAC. + +--- + arch/arm/boot/dts/Makefile | 1 + + arch/arm/boot/dts/rpi-dac-overlay.dts | 34 ++++++++++++++++++++++++++++++++++ + sound/soc/bcm/rpi-dac.c | 21 +++++++++++++++++++++ + sound/soc/codecs/pcm1794a.c | 7 +++++++ + 4 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/rpi-dac-overlay.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index eda0009..2b8c692a 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -24,6 +24,7 @@ dtb-$(RPI_DT_OVERLAYS) += hy28a-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += hy28b-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += iqaudio-dacplus-overlay.dtb ++dtb-$(RPI_DT_OVERLAYS) += rpi-dac-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += rpi-proto-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += lirc-rpi-overlay.dtb + dtb-$(RPI_DT_OVERLAYS) += mz61581-overlay.dtb +diff --git a/arch/arm/boot/dts/rpi-dac-overlay.dts b/arch/arm/boot/dts/rpi-dac-overlay.dts +new file mode 100644 +index 0000000..7fc6ac9 +--- /dev/null ++++ b/arch/arm/boot/dts/rpi-dac-overlay.dts +@@ -0,0 +1,34 @@ ++// Definitions for RPi DAC ++/dts-v1/; ++/plugin/; ++ ++/ { ++ compatible = "brcm,bcm2708"; ++ ++ fragment@0 { ++ target = <&sound>; ++ __overlay__ { ++ compatible = "rpi,rpi-dac"; ++ i2s-controller = <&i2s>; ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@1 { ++ target = <&i2s>; ++ __overlay__ { ++ status = "okay"; ++ }; ++ }; ++ ++ fragment@2 { ++ target-path = "/"; ++ __overlay__ { ++ pcm1794a-codec { ++ #sound-dai-cells = <0>; ++ compatible = "ti,pcm1794a"; ++ status = "okay"; ++ }; ++ }; ++ }; ++}; +diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c +index 6d6e0ba..d5fac1b 100644 +--- a/sound/soc/bcm/rpi-dac.c ++++ b/sound/soc/bcm/rpi-dac.c +@@ -69,6 +69,20 @@ static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) + int ret = 0; + + snd_rpi_rpi_dac.dev = &pdev->dev; ++ ++ if (pdev->dev.of_node) { ++ struct device_node *i2s_node; ++ struct snd_soc_dai_link *dai = &snd_rpi_rpi_dac_dai[0]; ++ i2s_node = of_parse_phandle(pdev->dev.of_node, "i2s-controller", 0); ++ ++ if (i2s_node) { ++ dai->cpu_dai_name = NULL; ++ dai->cpu_of_node = i2s_node; ++ dai->platform_name = NULL; ++ dai->platform_of_node = i2s_node; ++ } ++ } ++ + ret = snd_soc_register_card(&snd_rpi_rpi_dac); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); +@@ -81,10 +95,17 @@ static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) + return snd_soc_unregister_card(&snd_rpi_rpi_dac); + } + ++static const struct of_device_id snd_rpi_rpi_dac_of_match[] = { ++ { .compatible = "rpi,rpi-dac", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, snd_rpi_rpi_dac_of_match); ++ + static struct platform_driver snd_rpi_rpi_dac_driver = { + .driver = { + .name = "snd-rpi-dac", + .owner = THIS_MODULE, ++ .of_match_table = snd_rpi_rpi_dac_of_match, + }, + .probe = snd_rpi_rpi_dac_probe, + .remove = snd_rpi_rpi_dac_remove, +diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c +index b4eaa44..afe1b41 100644 +--- a/sound/soc/codecs/pcm1794a.c ++++ b/sound/soc/codecs/pcm1794a.c +@@ -46,12 +46,19 @@ static int pcm1794a_remove(struct platform_device *pdev) + return 0; + } + ++static const struct of_device_id pcm1794a_of_match[] = { ++ { .compatible = "ti,pcm1794a", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, pcm1794a_of_match); ++ + static struct platform_driver pcm1794a_codec_driver = { + .probe = pcm1794a_probe, + .remove = pcm1794a_remove, + .driver = { + .name = "pcm1794a-codec", + .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(pcm1794a_of_match), + }, + }; + + +From 15eb32f3eaecad4cba2432e3ee339ef5f04f62f5 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 13 Apr 2015 17:16:29 +0100 +Subject: [PATCH 104/104] config: Add default configs + +--- + arch/arm/configs/bcm2709_defconfig | 1192 ++++++++++++++++++++++++++++++++++++ + arch/arm/configs/bcmrpi_defconfig | 1187 +++++++++++++++++++++++++++++++++++ + 2 files changed, 2379 insertions(+) + create mode 100644 arch/arm/configs/bcm2709_defconfig + create mode 100644 arch/arm/configs/bcmrpi_defconfig + +diff --git a/arch/arm/configs/bcm2709_defconfig b/arch/arm/configs/bcm2709_defconfig +new file mode 100644 +index 0000000..23079b4 +--- /dev/null ++++ b/arch/arm/configs/bcm2709_defconfig +@@ -0,0 +1,1192 @@ ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0 ++CONFIG_LOCALVERSION="-v7" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_IKCONFIG=m ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2709=y ++CONFIG_BCM2709_DT=y ++# CONFIG_CACHE_L2X0 is not set ++CONFIG_SMP=y ++CONFIG_HAVE_ARM_ARCH_TIMER=y ++CONFIG_VMSPLIT_2G=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=m ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_VFP=y ++CONFIG_NEON=y ++CONFIG_KERNEL_MODE_NEON=y ++CONFIG_BINFMT_MISC=m ++# CONFIG_SUSPEND is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++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_INET_AH=m ++CONFIG_INET_ESP=m ++CONFIG_INET_IPCOMP=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=m ++CONFIG_INET_XFRM_MODE_TUNNEL=m ++CONFIG_INET_XFRM_MODE_BEET=m ++CONFIG_INET_LRO=m ++CONFIG_INET_DIAG=m ++CONFIG_INET6_AH=m ++CONFIG_INET6_ESP=m ++CONFIG_INET6_IPCOMP=m ++CONFIG_IPV6_TUNNEL=m ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y ++CONFIG_NETFILTER=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_ZONES=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m ++CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 ++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_ESP=m ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_PROTO_ESP=y ++CONFIG_IP_VS_PROTO_AH=y ++CONFIG_IP_VS_PROTO_SCTP=y ++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 ++CONFIG_IP_VS_FTP=m ++CONFIG_IP_VS_PE_SIP=m ++CONFIG_NF_CONNTRACK_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_MATCH_AH=m ++CONFIG_IP_NF_MATCH_ECN=m ++CONFIG_IP_NF_MATCH_TTL=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=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 ++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_RT=m ++CONFIG_IP6_NF_TARGET_HL=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y ++CONFIG_ATM=m ++CONFIG_L2TP=m ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=m ++CONFIG_L2TP_ETH=m ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=m ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_ATALK=m ++CONFIG_6LOWPAN=m ++CONFIG_NET_SCHED=y ++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 ++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_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_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_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_BATMAN_ADV=m ++CONFIG_OPENVSWITCH=m ++CONFIG_NET_PKTGEN=m ++CONFIG_HAMRADIO=y ++CONFIG_AX25=m ++CONFIG_NETROM=m ++CONFIG_ROSE=m ++CONFIG_MKISS=m ++CONFIG_6PACK=m ++CONFIG_BPQETHER=m ++CONFIG_BAYCOM_SER_FDX=m ++CONFIG_BAYCOM_SER_HDX=m ++CONFIG_YAM=m ++CONFIG_CAN=m ++CONFIG_CAN_VCAN=m ++CONFIG_CAN_MCP251X=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++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 ++CONFIG_BT_6LOWPAN=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_BT_WILINK=m ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=m ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_RFKILL=m ++CONFIG_RFKILL_INPUT=y ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=5 ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_DRBD=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_ATA_OVER_ETH=m ++CONFIG_EEPROM_AT24=m ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=m ++CONFIG_CHR_DEV_SG=m ++CONFIG_SCSI_ISCSI_ATTRS=y ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m ++CONFIG_MD=y ++CONFIG_MD_LINEAR=m ++CONFIG_MD_RAID0=m ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_CRYPT=m ++CONFIG_DM_SNAPSHOT=m ++CONFIG_DM_MIRROR=m ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_RAID=m ++CONFIG_DM_ZERO=m ++CONFIG_DM_DELAY=m ++CONFIG_NETDEVICES=y ++CONFIG_BONDING=m ++CONFIG_DUMMY=m ++CONFIG_IFB=m ++CONFIG_MACVLAN=m ++CONFIG_NETCONSOLE=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++CONFIG_ENC28J60=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPPOATM=m ++CONFIG_PPPOE=m ++CONFIG_PPPOL2TP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=y ++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=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_CARDS=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_USB=m ++CONFIG_AR5523=m ++CONFIG_B43=m ++# CONFIG_B43_PHY_N is not set ++CONFIG_B43LEGACY=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_USB=y ++CONFIG_HOSTAP=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT3573=y ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RT2800USB_RT55XX=y ++CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_JOYSTICK_IFORCE=m ++CONFIG_JOYSTICK_IFORCE_USB=y ++CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m ++CONFIG_TOUCHSCREEN_STMPE=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2708=m ++CONFIG_RAW_DRIVER=y ++CONFIG_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VC_SM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2708=m ++CONFIG_SPI_SPIDEV=y ++CONFIG_PPS=m ++CONFIG_PPS_CLIENT_LDISC=m ++CONFIG_PPS_CLIENT_GPIO=m ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_ARIZONA=m ++CONFIG_GPIO_STMPE=y ++CONFIG_W1=m ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2406=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++CONFIG_BATTERY_DS2760=m ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++CONFIG_UCB1400_CORE=m ++CONFIG_MFD_STMPE=y ++CONFIG_STMPE_SPI=y ++CONFIG_MFD_ARIZONA_I2C=m ++CONFIG_MFD_ARIZONA_SPI=m ++CONFIG_MFD_WM5102=y ++CONFIG_MEDIA_SUPPORT=m ++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_LIRC=m ++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_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m ++CONFIG_USB_ZR364XX=m ++CONFIG_USB_STKWEBCAM=m ++CONFIG_USB_S2255=m ++CONFIG_VIDEO_USBTV=m ++CONFIG_VIDEO_PVRUSB2=m ++CONFIG_VIDEO_HDPVR=m ++CONFIG_VIDEO_USBVISION=m ++CONFIG_VIDEO_STK1160_COMMON=m ++CONFIG_VIDEO_STK1160_AC97=y ++CONFIG_VIDEO_GO7007=m ++CONFIG_VIDEO_GO7007_USB=m ++CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m ++CONFIG_VIDEO_AU0828=m ++CONFIG_VIDEO_AU0828_RC=y ++CONFIG_VIDEO_CX231XX=m ++CONFIG_VIDEO_CX231XX_ALSA=m ++CONFIG_VIDEO_CX231XX_DVB=m ++CONFIG_VIDEO_TM6000=m ++CONFIG_VIDEO_TM6000_ALSA=m ++CONFIG_VIDEO_TM6000_DVB=m ++CONFIG_DVB_USB=m ++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_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_AS102=m ++CONFIG_VIDEO_EM28XX=m ++CONFIG_VIDEO_EM28XX_V4L2=m ++CONFIG_VIDEO_EM28XX_ALSA=m ++CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_BCM2835=y ++CONFIG_VIDEO_BCM2835_MMAL=m ++CONFIG_RADIO_SI470X=y ++CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m ++CONFIG_RADIO_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_MA901=m ++CONFIG_RADIO_TEA5764=m ++CONFIG_RADIO_SAA7706H=m ++CONFIG_RADIO_TEF6862=m ++CONFIG_RADIO_WL1273=m ++CONFIG_RADIO_WL128X=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_VIDEO_UDA1342=m ++CONFIG_VIDEO_SONY_BTF_MPX=m ++CONFIG_VIDEO_TVP5150=m ++CONFIG_VIDEO_TW2804=m ++CONFIG_VIDEO_TW9903=m ++CONFIG_VIDEO_TW9906=m ++CONFIG_VIDEO_OV7640=m ++CONFIG_VIDEO_MT9V011=m ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_GPIO=m ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_SOC=m ++CONFIG_SND_BCM2708_SOC_I2S=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m ++CONFIG_SND_BCM2708_SOC_RPI_DAC=m ++CONFIG_SND_BCM2708_SOC_RPI_PROTO=m ++CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_ELO=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++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_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THINGM=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_XINMO=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_PRINTER=m ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++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 ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USBIP_CORE=m ++CONFIG_USBIP_VHCI_HCD=m ++CONFIG_USBIP_HOST=m ++CONFIG_USB_SERIAL=m ++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_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_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_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_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_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 ++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=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_USB_ATM=m ++CONFIG_USB_SPEEDTOUCH=m ++CONFIG_USB_CXACRU=m ++CONFIG_USB_UEAGLEATM=m ++CONFIG_USB_XUSBATM=m ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SPI=m ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=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=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_TRIGGER_INPUT=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_HCTOSYS is not set ++CONFIG_RTC_DRV_DS1307=m ++CONFIG_RTC_DRV_DS1374=m ++CONFIG_RTC_DRV_DS1672=m ++CONFIG_RTC_DRV_DS3232=m ++CONFIG_RTC_DRV_MAX6900=m ++CONFIG_RTC_DRV_RS5C372=m ++CONFIG_RTC_DRV_ISL1208=m ++CONFIG_RTC_DRV_ISL12022=m ++CONFIG_RTC_DRV_ISL12057=m ++CONFIG_RTC_DRV_X1205=m ++CONFIG_RTC_DRV_PCF2127=m ++CONFIG_RTC_DRV_PCF8523=m ++CONFIG_RTC_DRV_PCF8563=m ++CONFIG_RTC_DRV_PCF8583=m ++CONFIG_RTC_DRV_M41T80=m ++CONFIG_RTC_DRV_BQ32K=m ++CONFIG_RTC_DRV_S35390A=m ++CONFIG_RTC_DRV_FM3130=m ++CONFIG_RTC_DRV_RX8581=m ++CONFIG_RTC_DRV_RX8025=m ++CONFIG_RTC_DRV_EM3027=m ++CONFIG_RTC_DRV_RV3029C2=m ++CONFIG_RTC_DRV_M41T93=m ++CONFIG_RTC_DRV_M41T94=m ++CONFIG_RTC_DRV_DS1305=m ++CONFIG_RTC_DRV_DS1390=m ++CONFIG_RTC_DRV_MAX6902=m ++CONFIG_RTC_DRV_R9701=m ++CONFIG_RTC_DRV_RS5C348=m ++CONFIG_RTC_DRV_DS3234=m ++CONFIG_RTC_DRV_PCF2123=m ++CONFIG_RTC_DRV_RX4581=m ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2708=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV_GENIRQ=m ++CONFIG_STAGING=y ++CONFIG_PRISM2_USB=m ++CONFIG_R8712U=m ++CONFIG_R8188EU=m ++CONFIG_R8723AU=m ++CONFIG_VT6656=m ++CONFIG_SPEAKUP=m ++CONFIG_SPEAKUP_SYNTH_SOFT=m ++CONFIG_STAGING_MEDIA=y ++CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_IMON=m ++CONFIG_LIRC_RPI=m ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SERIAL=m ++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_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++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_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_NTFS_RW=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_F2FS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_SWAP=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_NFSD=m ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_NLS_DEFAULT="utf8" ++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=y ++CONFIG_NLS_ISO8859_1=m ++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_DLM=m ++CONFIG_PRINTK_TIME=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_PREEMPT is not set ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++# CONFIG_KPROBE_EVENT is not set ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=m ++CONFIG_CRYPTO_XTS=m ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_SHA1_ARM_NEON=m ++CONFIG_CRYPTO_SHA512_ARM_NEON=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_AES_ARM_BS=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +new file mode 100644 +index 0000000..919e0b7 +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -0,0 +1,1187 @@ ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_PHYS_OFFSET=0 ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_FHANDLE=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_TASKSTATS=y ++CONFIG_TASK_DELAY_ACCT=y ++CONFIG_TASK_XACCT=y ++CONFIG_TASK_IO_ACCOUNTING=y ++CONFIG_IKCONFIG=m ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_MEMCG=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_JUMP_LABEL=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2708=y ++CONFIG_BCM2708_DT=y ++CONFIG_PREEMPT=y ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_CMA=y ++CONFIG_UACCESS_WITH_MEMCPY=y ++CONFIG_SECCOMP=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_STAT=m ++CONFIG_CPU_FREQ_STAT_DETAILS=y ++CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE=y ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_USERSPACE=y ++CONFIG_CPU_FREQ_GOV_ONDEMAND=y ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y ++CONFIG_VFP=y ++CONFIG_BINFMT_MISC=m ++# CONFIG_SUSPEND is not set ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++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_INET_AH=m ++CONFIG_INET_ESP=m ++CONFIG_INET_IPCOMP=m ++CONFIG_INET_XFRM_MODE_TRANSPORT=m ++CONFIG_INET_XFRM_MODE_TUNNEL=m ++CONFIG_INET_XFRM_MODE_BEET=m ++CONFIG_INET_LRO=m ++CONFIG_INET_DIAG=m ++CONFIG_INET6_AH=m ++CONFIG_INET6_ESP=m ++CONFIG_INET6_IPCOMP=m ++CONFIG_IPV6_TUNNEL=m ++CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y ++CONFIG_NETFILTER=y ++CONFIG_NF_CONNTRACK=m ++CONFIG_NF_CONNTRACK_ZONES=y ++CONFIG_NF_CONNTRACK_EVENTS=y ++CONFIG_NF_CONNTRACK_TIMESTAMP=y ++CONFIG_NF_CT_PROTO_DCCP=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_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_NETFILTER_XT_SET=m ++CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m ++CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m ++CONFIG_NETFILTER_XT_TARGET_CONNMARK=m ++CONFIG_NETFILTER_XT_TARGET_DSCP=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_NFLOG=m ++CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m ++CONFIG_NETFILTER_XT_TARGET_NOTRACK=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 ++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_ESP=m ++CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m ++CONFIG_NETFILTER_XT_MATCH_HELPER=m ++CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=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_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_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_PROTO_TCP=y ++CONFIG_IP_VS_PROTO_UDP=y ++CONFIG_IP_VS_PROTO_ESP=y ++CONFIG_IP_VS_PROTO_AH=y ++CONFIG_IP_VS_PROTO_SCTP=y ++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 ++CONFIG_IP_VS_FTP=m ++CONFIG_IP_VS_PE_SIP=m ++CONFIG_NF_CONNTRACK_IPV4=m ++CONFIG_IP_NF_IPTABLES=m ++CONFIG_IP_NF_MATCH_AH=m ++CONFIG_IP_NF_MATCH_ECN=m ++CONFIG_IP_NF_MATCH_TTL=m ++CONFIG_IP_NF_FILTER=m ++CONFIG_IP_NF_TARGET_REJECT=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 ++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_RT=m ++CONFIG_IP6_NF_TARGET_HL=m ++CONFIG_IP6_NF_FILTER=m ++CONFIG_IP6_NF_TARGET_REJECT=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_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_SCTP_COOKIE_HMAC_SHA1=y ++CONFIG_ATM=m ++CONFIG_L2TP=m ++CONFIG_L2TP_V3=y ++CONFIG_L2TP_IP=m ++CONFIG_L2TP_ETH=m ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=m ++CONFIG_VLAN_8021Q_GVRP=y ++CONFIG_ATALK=m ++CONFIG_6LOWPAN=m ++CONFIG_NET_SCHED=y ++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 ++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_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_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_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_BATMAN_ADV=m ++CONFIG_OPENVSWITCH=m ++CONFIG_NET_PKTGEN=m ++CONFIG_HAMRADIO=y ++CONFIG_AX25=m ++CONFIG_NETROM=m ++CONFIG_ROSE=m ++CONFIG_MKISS=m ++CONFIG_6PACK=m ++CONFIG_BPQETHER=m ++CONFIG_BAYCOM_SER_FDX=m ++CONFIG_BAYCOM_SER_HDX=m ++CONFIG_YAM=m ++CONFIG_CAN=m ++CONFIG_CAN_VCAN=m ++CONFIG_CAN_MCP251X=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRNET=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++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 ++CONFIG_BT_6LOWPAN=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_BT_WILINK=m ++CONFIG_CFG80211_WEXT=y ++CONFIG_MAC80211=m ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_RFKILL=m ++CONFIG_RFKILL_INPUT=y ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_DEVTMPFS=y ++CONFIG_DEVTMPFS_MOUNT=y ++CONFIG_DMA_CMA=y ++CONFIG_CMA_SIZE_MBYTES=5 ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_DRBD=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_ATA_OVER_ETH=m ++CONFIG_EEPROM_AT24=m ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m ++CONFIG_BLK_DEV_SR=m ++CONFIG_CHR_DEV_SG=m ++CONFIG_SCSI_ISCSI_ATTRS=y ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m ++CONFIG_MD=y ++CONFIG_MD_LINEAR=m ++CONFIG_MD_RAID0=m ++CONFIG_BLK_DEV_DM=m ++CONFIG_DM_CRYPT=m ++CONFIG_DM_SNAPSHOT=m ++CONFIG_DM_MIRROR=m ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_RAID=m ++CONFIG_DM_ZERO=m ++CONFIG_DM_DELAY=m ++CONFIG_NETDEVICES=y ++CONFIG_BONDING=m ++CONFIG_DUMMY=m ++CONFIG_IFB=m ++CONFIG_MACVLAN=m ++CONFIG_NETCONSOLE=m ++CONFIG_TUN=m ++CONFIG_VETH=m ++CONFIG_ENC28J60=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_PPP=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPPOATM=m ++CONFIG_PPPOE=m ++CONFIG_PPPOL2TP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_RTL8152=m ++CONFIG_USB_USBNET=y ++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=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=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_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_CARDS=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_ATH6KL=m ++CONFIG_ATH6KL_USB=m ++CONFIG_AR5523=m ++CONFIG_B43=m ++# CONFIG_B43_PHY_N is not set ++CONFIG_B43LEGACY=m ++CONFIG_BRCMFMAC=m ++CONFIG_BRCMFMAC_USB=y ++CONFIG_HOSTAP=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT3573=y ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RT2800USB_RT55XX=y ++CONFIG_RT2800USB_UNKNOWN=y ++CONFIG_RTL8192CU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_JOYSTICK=y ++CONFIG_JOYSTICK_IFORCE=m ++CONFIG_JOYSTICK_IFORCE_USB=y ++CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD_FF=y ++CONFIG_INPUT_TOUCHSCREEN=y ++CONFIG_TOUCHSCREEN_ADS7846=m ++CONFIG_TOUCHSCREEN_EGALAX=m ++CONFIG_TOUCHSCREEN_USB_COMPOSITE=m ++CONFIG_TOUCHSCREEN_STMPE=m ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_DEVPTS_MULTIPLE_INSTANCES=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_TTY_PRINTK=y ++CONFIG_HW_RANDOM=y ++CONFIG_HW_RANDOM_BCM2708=m ++CONFIG_RAW_DRIVER=y ++CONFIG_BRCM_CHAR_DRIVERS=y ++CONFIG_BCM_VC_CMA=y ++CONFIG_BCM_VC_SM=y ++CONFIG_I2C=y ++CONFIG_I2C_CHARDEV=m ++CONFIG_I2C_BCM2708=m ++CONFIG_SPI=y ++CONFIG_SPI_BCM2835=m ++CONFIG_SPI_BCM2708=m ++CONFIG_SPI_SPIDEV=y ++CONFIG_PPS=m ++CONFIG_PPS_CLIENT_LDISC=m ++CONFIG_PPS_CLIENT_GPIO=m ++CONFIG_GPIO_SYSFS=y ++CONFIG_GPIO_ARIZONA=m ++CONFIG_GPIO_STMPE=y ++CONFIG_W1=m ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2406=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++CONFIG_BATTERY_DS2760=m ++# CONFIG_HWMON is not set ++CONFIG_THERMAL=y ++CONFIG_THERMAL_BCM2835=y ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++CONFIG_UCB1400_CORE=m ++CONFIG_MFD_STMPE=y ++CONFIG_STMPE_SPI=y ++CONFIG_MFD_ARIZONA_I2C=m ++CONFIG_MFD_ARIZONA_SPI=m ++CONFIG_MFD_WM5102=y ++CONFIG_MEDIA_SUPPORT=m ++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_LIRC=m ++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_MEDIA_USB_SUPPORT=y ++CONFIG_USB_VIDEO_CLASS=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_DTCS033=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_STK1135=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_VIDEO_CPIA2=m ++CONFIG_USB_ZR364XX=m ++CONFIG_USB_STKWEBCAM=m ++CONFIG_USB_S2255=m ++CONFIG_VIDEO_USBTV=m ++CONFIG_VIDEO_PVRUSB2=m ++CONFIG_VIDEO_HDPVR=m ++CONFIG_VIDEO_USBVISION=m ++CONFIG_VIDEO_STK1160_COMMON=m ++CONFIG_VIDEO_STK1160_AC97=y ++CONFIG_VIDEO_GO7007=m ++CONFIG_VIDEO_GO7007_USB=m ++CONFIG_VIDEO_GO7007_USB_S2250_BOARD=m ++CONFIG_VIDEO_AU0828=m ++CONFIG_VIDEO_AU0828_RC=y ++CONFIG_VIDEO_CX231XX=m ++CONFIG_VIDEO_CX231XX_ALSA=m ++CONFIG_VIDEO_CX231XX_DVB=m ++CONFIG_VIDEO_TM6000=m ++CONFIG_VIDEO_TM6000_ALSA=m ++CONFIG_VIDEO_TM6000_DVB=m ++CONFIG_DVB_USB=m ++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_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_AS102=m ++CONFIG_VIDEO_EM28XX=m ++CONFIG_VIDEO_EM28XX_V4L2=m ++CONFIG_VIDEO_EM28XX_ALSA=m ++CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_BCM2835=y ++CONFIG_VIDEO_BCM2835_MMAL=m ++CONFIG_RADIO_SI470X=y ++CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m ++CONFIG_RADIO_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_MA901=m ++CONFIG_RADIO_TEA5764=m ++CONFIG_RADIO_SAA7706H=m ++CONFIG_RADIO_TEF6862=m ++CONFIG_RADIO_WL1273=m ++CONFIG_RADIO_WL128X=m ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++CONFIG_VIDEO_UDA1342=m ++CONFIG_VIDEO_SONY_BTF_MPX=m ++CONFIG_VIDEO_TVP5150=m ++CONFIG_VIDEO_TW2804=m ++CONFIG_VIDEO_TW9903=m ++CONFIG_VIDEO_TW9906=m ++CONFIG_VIDEO_OV7640=m ++CONFIG_VIDEO_MT9V011=m ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++# CONFIG_BACKLIGHT_GENERIC is not set ++CONFIG_BACKLIGHT_GPIO=m ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_SOC=m ++CONFIG_SND_BCM2708_SOC_I2S=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DACPLUS=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DIGI=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP=m ++CONFIG_SND_BCM2708_SOC_RPI_DAC=m ++CONFIG_SND_BCM2708_SOC_RPI_PROTO=m ++CONFIG_SND_BCM2708_SOC_IQAUDIO_DAC=m ++CONFIG_SND_SIMPLE_CARD=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HIDRAW=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_ELO=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++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_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THINGM=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_XINMO=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_PRINTER=m ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++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 ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USBIP_CORE=m ++CONFIG_USBIP_VHCI_HCD=m ++CONFIG_USBIP_HOST=m ++CONFIG_USB_SERIAL=m ++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_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_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_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_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_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 ++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=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_USB_ATM=m ++CONFIG_USB_SPEEDTOUCH=m ++CONFIG_USB_CXACRU=m ++CONFIG_USB_UEAGLEATM=m ++CONFIG_USB_XUSBATM=m ++CONFIG_MMC=y ++CONFIG_MMC_BLOCK_MINORS=32 ++CONFIG_MMC_BCM2835=y ++CONFIG_MMC_BCM2835_DMA=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SPI=m ++CONFIG_LEDS_CLASS=y ++CONFIG_LEDS_GPIO=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=y ++CONFIG_LEDS_TRIGGER_GPIO=y ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=y ++CONFIG_LEDS_TRIGGER_TRANSIENT=m ++CONFIG_LEDS_TRIGGER_CAMERA=m ++CONFIG_LEDS_TRIGGER_INPUT=y ++CONFIG_RTC_CLASS=y ++# CONFIG_RTC_HCTOSYS is not set ++CONFIG_RTC_DRV_DS1307=m ++CONFIG_RTC_DRV_DS1374=m ++CONFIG_RTC_DRV_DS1672=m ++CONFIG_RTC_DRV_DS3232=m ++CONFIG_RTC_DRV_MAX6900=m ++CONFIG_RTC_DRV_RS5C372=m ++CONFIG_RTC_DRV_ISL1208=m ++CONFIG_RTC_DRV_ISL12022=m ++CONFIG_RTC_DRV_ISL12057=m ++CONFIG_RTC_DRV_X1205=m ++CONFIG_RTC_DRV_PCF2127=m ++CONFIG_RTC_DRV_PCF8523=m ++CONFIG_RTC_DRV_PCF8563=m ++CONFIG_RTC_DRV_PCF8583=m ++CONFIG_RTC_DRV_M41T80=m ++CONFIG_RTC_DRV_BQ32K=m ++CONFIG_RTC_DRV_S35390A=m ++CONFIG_RTC_DRV_FM3130=m ++CONFIG_RTC_DRV_RX8581=m ++CONFIG_RTC_DRV_RX8025=m ++CONFIG_RTC_DRV_EM3027=m ++CONFIG_RTC_DRV_RV3029C2=m ++CONFIG_RTC_DRV_M41T93=m ++CONFIG_RTC_DRV_M41T94=m ++CONFIG_RTC_DRV_DS1305=m ++CONFIG_RTC_DRV_DS1390=m ++CONFIG_RTC_DRV_MAX6902=m ++CONFIG_RTC_DRV_R9701=m ++CONFIG_RTC_DRV_RS5C348=m ++CONFIG_RTC_DRV_DS3234=m ++CONFIG_RTC_DRV_PCF2123=m ++CONFIG_RTC_DRV_RX4581=m ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2708=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV_GENIRQ=m ++CONFIG_STAGING=y ++CONFIG_PRISM2_USB=m ++CONFIG_R8712U=m ++CONFIG_R8188EU=m ++CONFIG_R8723AU=m ++CONFIG_VT6656=m ++CONFIG_SPEAKUP=m ++CONFIG_SPEAKUP_SYNTH_SOFT=m ++CONFIG_STAGING_MEDIA=y ++CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_IMON=m ++CONFIG_LIRC_RPI=m ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SERIAL=m ++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_IOMMU_SUPPORT is not set ++CONFIG_EXTCON=m ++CONFIG_EXTCON_ARIZONA=m ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++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_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_QFMT_V1=m ++CONFIG_QFMT_V2=m ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_NTFS_RW=y ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_F2FS_FS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_NFS_SWAP=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_NFSD=m ++CONFIG_NFSD_V3_ACL=y ++CONFIG_NFSD_V4=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_NLS_DEFAULT="utf8" ++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=y ++CONFIG_NLS_ISO8859_1=m ++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_DLM=m ++CONFIG_PRINTK_TIME=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_PREEMPT is not set ++CONFIG_LATENCYTOP=y ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++# CONFIG_KPROBE_EVENT is not set ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_CRYPTD=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_CTS=m ++CONFIG_CRYPTO_XTS=m ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_SHA1_ARM=m ++CONFIG_CRYPTO_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_AES_ARM=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y diff --git a/projects/RPi2/patches/linux/linux-02-RPi_support.patch b/projects/RPi2/patches/linux/linux-02-RPi_support.patch deleted file mode 100644 index da6ce91543..0000000000 --- a/projects/RPi2/patches/linux/linux-02-RPi_support.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 9cc28c602914b6b07ad8d7ab4ea146b147b78245 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 8 Apr 2015 18:51:44 +0100 -Subject: [PATCH] vchiq: Remove inline from suspend/resume - ---- - drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -index 3e328ab..0ad9656 100644 ---- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -+++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c -@@ -1776,7 +1776,7 @@ vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state) - ** VC_RESUME_FAILED - Currently unused - no mechanism to fail resume exists. - */ - --inline void -+void - set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, - enum vc_suspend_status new_state) - { -@@ -1813,7 +1813,7 @@ set_suspend_state(VCHIQ_ARM_STATE_T *arm_state, - } - } - --inline void -+void - set_resume_state(VCHIQ_ARM_STATE_T *arm_state, - enum vc_resume_status new_state) - {